From a9a38f88bba242d09a1c0f7d8d9a633df3a8a9da Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Wed, 3 Sep 2025 21:30:26 +0800 Subject: [PATCH 01/41] fix: transform parameters when adding mcp by json (#9850) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mcp): 添加 streamable_http 类型并转换为 streamableHttp 为了兼容其他配置,添加 streamable_http 类型并在解析时自动转换为 streamableHttp * feat(mcp): 根据URL自动推断服务器类型 当未显式指定服务器类型时,通过检查URL后缀自动设置合适的类型(mcp或sse) * feat(mcp): 添加http类型支持并映射到streamableHttp --- src/renderer/src/types/mcp.ts | 36 ++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/src/renderer/src/types/mcp.ts b/src/renderer/src/types/mcp.ts index b2e44180d8..18fd6683f2 100644 --- a/src/renderer/src/types/mcp.ts +++ b/src/renderer/src/types/mcp.ts @@ -16,7 +16,21 @@ export type MCPConfigSample = z.infer * 允许 inMemory 作为合法字段,需要额外校验 name 是否 builtin */ export const McpServerTypeSchema = z - .union([z.literal('stdio'), z.literal('sse'), z.literal('streamableHttp'), z.literal('inMemory')]) + .union([ + z.literal('stdio'), + z.literal('sse'), + z.literal('streamableHttp'), + z.literal('http'), + z.literal('streamable_http'), + z.literal('inMemory') + ]) + .transform((type) => { + if (type === 'streamable_http' || type === 'http') { + return 'streamableHttp' + } else { + return type + } + }) .default('stdio') // 大多数情况下默认使用 stdio /** * 定义单个 MCP 服务器的配置。 @@ -174,6 +188,26 @@ export const McpServerConfigSchema = z message: 'Server type is inMemory but this is not a builtin MCP server, which is not allowed' } ) + .transform((schema) => { + // 显式传入的type会覆盖掉从url推断的逻辑 + if (!schema.type) { + const url = schema.baseUrl ?? schema.url ?? null + if (url !== null) { + if (url.endsWith('/mcp')) { + return { + ...schema, + type: 'streamableHttp' + } as const + } else if (url.endsWith('/sse')) { + return { + ...schema, + type: 'sse' + } as const + } + } + } + return schema + }) /** * 将服务器别名(字符串ID)映射到其配置的对象。 * 例如: { "my-tools": { command: "...", args: [...] }, "github": { ... } } From 0a36869b3c84749852566969c1596b9cd7e38eaf Mon Sep 17 00:00:00 2001 From: Pleasure1234 <3196812536@qq.com> Date: Thu, 4 Sep 2025 02:42:42 +0800 Subject: [PATCH 02/41] fix: NavigationService initialization timing issue and add tab drag reordering (#9700) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: NavigationService initialization timing issue and add tab drag reordering - Fix NavigationService timing issue in TabsService by adding fallback navigation with setTimeout - Add drag and drop functionality for tab reordering with visual feedback - Remove unused MessageSquareDiff icon from Navbar - Add reorderTabs action to tabs store 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude * Update tabs.ts * Update TabContainer.tsx * Update TabContainer.tsx * fix(dnd): horizontal sortable (#9827) * refactor(CodeViewer): improve props, aligned to CodeEditor (#9786) * refactor(CodeViewer): improve props, aligned to CodeEditor * refactor: simplify internal variables * refactor: remove default lineNumbers * fix: shiki theme container style * revert: use ReactMarkdown for prompt editing * fix: draggable list id type (#9809) * refactor(dnd): rename idKey to itemKey for clarity * refactor: key and id type for draggable lists * chore: update yarn lock * fix: type error * refactor: improve getId fallbacks * feat: integrate file selection and upload functionality in KnowledgeFiles component (#9815) * feat: integrate file selection and upload functionality in KnowledgeFiles component - Added useFiles hook to manage file selection. - Updated handleAddFile to utilize the new file selection logic, allowing multiple file uploads. - Improved user experience by handling file uploads asynchronously and logging the results. * feat: enhance file upload interaction in KnowledgeFiles component - Wrapped Dragger component in a div to allow for custom click handling. - Prevented default click behavior to improve user experience when adding files. - Maintained existing file upload functionality while enhancing the UI interaction. * refactor(KnowledgeFiles): 提取文件处理逻辑到独立函数 将重复的文件上传和处理逻辑提取到独立的processFiles函数中,提高代码复用性和可维护性 --------- Co-authored-by: icarus * fix(Sortable): correct gap and horizontal style * feat: make tabs sortable (example) * refactor: improve sortable direction and gap * refactor: update example * fix: remove useless states --------- Co-authored-by: beyondkmp Co-authored-by: icarus Co-authored-by: Pleasure1234 <3196812536@qq.com> * fix: syntax error * refactor: remove useless styles * fix: tabs overflow, add scrollbar * fix: button gap * fix: app region drag * refactor: remove scrollbar, add space for app dragging * Revert "refactor: remove scrollbar, add space for app dragging" This reverts commit f6ebeb143e4dfe30c8d857084154b1e175adcfc1. * refactor: update style * refactor: add a scroll-to-right button --------- Co-authored-by: Claude Co-authored-by: one Co-authored-by: beyondkmp Co-authored-by: icarus --- .../src/components/Tab/TabContainer.tsx | 169 ++++++++++++++---- src/renderer/src/components/dnd/Sortable.tsx | 30 +++- src/renderer/src/pages/home/Navbar.tsx | 7 +- .../settings/MCPSettings/McpServersList.tsx | 1 + src/renderer/src/services/TabsService.ts | 10 +- src/renderer/src/store/tabs.ts | 5 +- 6 files changed, 177 insertions(+), 45 deletions(-) diff --git a/src/renderer/src/components/Tab/TabContainer.tsx b/src/renderer/src/components/Tab/TabContainer.tsx index 66e62dca6f..1168e02431 100644 --- a/src/renderer/src/components/Tab/TabContainer.tsx +++ b/src/renderer/src/components/Tab/TabContainer.tsx @@ -1,4 +1,7 @@ import { PlusOutlined } from '@ant-design/icons' +import { TopNavbarOpenedMinappTabs } from '@renderer/components/app/PinnedMinapps' +import { Sortable, useDndReorder } from '@renderer/components/dnd' +import Scrollbar from '@renderer/components/Scrollbar' import { isLinux, isMac, isWin } from '@renderer/config/constant' import { useTheme } from '@renderer/context/ThemeProvider' import { useFullscreen } from '@renderer/hooks/useFullscreen' @@ -7,11 +10,12 @@ import { getThemeModeLabel, getTitleLabel } from '@renderer/i18n/label' import tabsService from '@renderer/services/TabsService' import { useAppDispatch, useAppSelector } from '@renderer/store' import type { Tab } from '@renderer/store/tabs' -import { addTab, removeTab, setActiveTab } from '@renderer/store/tabs' +import { addTab, removeTab, setActiveTab, setTabs } from '@renderer/store/tabs' import { ThemeMode } from '@renderer/types' import { classNames } from '@renderer/utils' -import { Tooltip } from 'antd' +import { Button, Tooltip } from 'antd' import { + ChevronRight, FileSearch, Folder, Hammer, @@ -28,13 +32,11 @@ import { Terminal, X } from 'lucide-react' -import { useCallback, useEffect } from 'react' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' import { useLocation, useNavigate } from 'react-router-dom' import styled from 'styled-components' -import { TopNavbarOpenedMinappTabs } from '../app/PinnedMinapps' - interface TabsContainerProps { children: React.ReactNode } @@ -81,6 +83,8 @@ const TabsContainer: React.FC = ({ children }) => { const { settedTheme, toggleTheme } = useTheme() const { hideMinappPopup } = useMinappPopup() const { t } = useTranslation() + const scrollRef = useRef(null) + const [canScroll, setCanScroll] = useState(false) const getTabId = (path: string): string => { if (path === '/') return 'home' @@ -142,34 +146,83 @@ const TabsContainer: React.FC = ({ children }) => { navigate(tab.path) } + const handleScrollRight = () => { + scrollRef.current?.scrollBy({ left: 200, behavior: 'smooth' }) + } + + useEffect(() => { + const scrollElement = scrollRef.current + if (!scrollElement) return + + const checkScrollability = () => { + setCanScroll(scrollElement.scrollWidth > scrollElement.clientWidth) + } + + checkScrollability() + + const resizeObserver = new ResizeObserver(checkScrollability) + resizeObserver.observe(scrollElement) + + window.addEventListener('resize', checkScrollability) + + return () => { + resizeObserver.disconnect() + window.removeEventListener('resize', checkScrollability) + } + }, [tabs]) + + const visibleTabs = useMemo(() => tabs.filter((tab) => !specialTabs.includes(tab.id)), [tabs]) + + const { onSortEnd } = useDndReorder({ + originalList: tabs, + filteredList: visibleTabs, + onUpdate: (newTabs) => dispatch(setTabs(newTabs)), + itemKey: 'id' + }) + return ( - {tabs - .filter((tab) => !specialTabs.includes(tab.id)) - .map((tab) => { - return ( - handleTabClick(tab)}> - - {tab.id && {getTabIcon(tab.id)}} - {getTitleLabel(tab.id)} - - {tab.id !== 'home' && ( - { - e.stopPropagation() - closeTab(tab.id) - }}> - - - )} - - ) - })} - - - + + + ( + handleTabClick(tab)}> + + {tab.id && {getTabIcon(tab.id)}} + {getTitleLabel(tab.id)} + + {tab.id !== 'home' && ( + { + e.stopPropagation() + closeTab(tab.id) + }}> + + + )} + + )} + /> + + {canScroll && ( + + + + )} + + + + ` @@ -221,6 +275,34 @@ const TabsBar = styled.div<{ $isFullscreen: boolean }>` } ` +const TabsArea = styled.div` + display: flex; + align-items: center; + flex: 1 1 auto; + min-width: 0; + gap: 6px; + padding-right: 2rem; + position: relative; + + -webkit-app-region: drag; + + > * { + -webkit-app-region: no-drag; + } + + &:hover { + .scroll-right-button { + opacity: 1; + } + } +` + +const TabsScroll = styled(Scrollbar)` + &::-webkit-scrollbar { + display: none; + } +` + const Tab = styled.div<{ active?: boolean }>` display: flex; align-items: center; @@ -228,12 +310,12 @@ const Tab = styled.div<{ active?: boolean }>` padding: 4px 10px; padding-right: 8px; background: ${(props) => (props.active ? 'var(--color-list-item)' : 'transparent')}; + transition: background 0.2s; border-radius: var(--list-item-border-radius); - cursor: pointer; user-select: none; height: 30px; min-width: 90px; - transition: background 0.2s; + .close-button { opacity: 0; transition: opacity 0.2s; @@ -251,12 +333,15 @@ const TabHeader = styled.div` display: flex; align-items: center; gap: 6px; + min-width: 0; + flex: 1; ` const TabIcon = styled.span` display: flex; align-items: center; color: var(--color-text-2); + flex-shrink: 0; ` const TabTitle = styled.span` @@ -265,6 +350,8 @@ const TabTitle = styled.span` display: flex; align-items: center; margin-right: 4px; + overflow: hidden; + white-space: nowrap; ` const CloseButton = styled.span` @@ -284,6 +371,7 @@ const AddTabButton = styled.div` cursor: pointer; color: var(--color-text-2); border-radius: var(--list-item-border-radius); + flex-shrink: 0; &.active { background: var(--color-list-item); } @@ -292,11 +380,28 @@ const AddTabButton = styled.div` } ` +const ScrollButton = styled(Button)` + position: absolute; + right: 4rem; + top: 50%; + transform: translateY(-50%); + z-index: 1; + opacity: 0; + transition: opacity 0.2s ease-in-out; + + border: none; + box-shadow: + 0 6px 16px 0 rgba(0, 0, 0, 0.08), + 0 3px 6px -4px rgba(0, 0, 0, 0.12), + 0 9px 28px 8px rgba(0, 0, 0, 0.05); +` + const RightButtonsContainer = styled.div` display: flex; align-items: center; gap: 6px; margin-left: auto; + flex-shrink: 0; ` const ThemeButton = styled.div` diff --git a/src/renderer/src/components/dnd/Sortable.tsx b/src/renderer/src/components/dnd/Sortable.tsx index a3102748cc..3ef77acb31 100644 --- a/src/renderer/src/components/dnd/Sortable.tsx +++ b/src/renderer/src/components/dnd/Sortable.tsx @@ -56,6 +56,8 @@ interface SortableProps { listStyle?: React.CSSProperties /** Ghost item style */ ghostItemStyle?: React.CSSProperties + /** Item gap */ + gap?: number | string } function Sortable({ @@ -70,7 +72,8 @@ function Sortable({ useDragOverlay = true, showGhost = false, className, - listStyle + listStyle, + gap }: SortableProps) { const sensors = useSensors( useSensor(PortalSafePointerSensor, { @@ -150,7 +153,12 @@ function Sortable({ onDragCancel={handleDragCancel} modifiers={modifiers}> - + {items.map((item, index) => ( ({ ) } -const ListWrapper = styled.div` +const ListWrapper = styled.div<{ $gap?: number | string }>` + gap: ${({ $gap }) => $gap}; + &[data-layout='grid'] { display: grid; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); width: 100%; - gap: 12px; @media (max-width: 768px) { grid-template-columns: 1fr; } } + + &[data-layout='list'] { + display: flex; + align-items: center; + + [data-direction='horizontal'] { + flex-direction: row; + } + + [data-direction='vertical'] { + flex-direction: column; + } + } ` export default Sortable diff --git a/src/renderer/src/pages/home/Navbar.tsx b/src/renderer/src/pages/home/Navbar.tsx index 5f75c36c0a..4fa1568954 100644 --- a/src/renderer/src/pages/home/Navbar.tsx +++ b/src/renderer/src/pages/home/Navbar.tsx @@ -14,7 +14,7 @@ import { setNarrowMode } from '@renderer/store/settings' import { Assistant, Topic } from '@renderer/types' import { Tooltip } from 'antd' import { t } from 'i18next' -import { Menu, MessageSquareDiff, PanelLeftClose, PanelRightClose, Search } from 'lucide-react' +import { Menu, PanelLeftClose, PanelRightClose, Search } from 'lucide-react' import { AnimatePresence, motion } from 'motion/react' import { FC } from 'react' import styled from 'styled-components' @@ -83,11 +83,6 @@ const HeaderNavbar: FC = ({ activeAssistant, setActiveAssistant, activeTo - - EventEmitter.emit(EVENT_NAMES.ADD_NEW_TOPIC)} style={{ marginRight: 5 }}> - - - )} diff --git a/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx b/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx index bbd108a26b..bb78b49db7 100644 --- a/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx +++ b/src/renderer/src/pages/settings/MCPSettings/McpServersList.tsx @@ -251,6 +251,7 @@ const McpServersList: FC = () => { itemKey="id" onSortEnd={onSortEnd} layout="grid" + gap={'12px'} useDragOverlay showGhost renderItem={(server) => ( diff --git a/src/renderer/src/services/TabsService.ts b/src/renderer/src/services/TabsService.ts index bce9fa376f..0153dd5663 100644 --- a/src/renderer/src/services/TabsService.ts +++ b/src/renderer/src/services/TabsService.ts @@ -34,12 +34,18 @@ class TabsService { const remainingTabs = tabs.filter((tab) => tab.id !== tabId) const lastTab = remainingTabs[remainingTabs.length - 1] + store.dispatch(setActiveTab(lastTab.id)) + // 使用 NavigationService 导航到新的标签页 if (NavigationService.navigate) { NavigationService.navigate(lastTab.path) } else { - logger.error('Navigation service is not initialized') - return false + logger.warn('Navigation service not ready, will navigate on next render') + setTimeout(() => { + if (NavigationService.navigate) { + NavigationService.navigate(lastTab.path) + } + }, 100) } } diff --git a/src/renderer/src/store/tabs.ts b/src/renderer/src/store/tabs.ts index 01dc7b1fb3..16195cd5f2 100644 --- a/src/renderer/src/store/tabs.ts +++ b/src/renderer/src/store/tabs.ts @@ -24,6 +24,9 @@ const tabsSlice = createSlice({ name: 'tabs', initialState, reducers: { + setTabs: (state, action: PayloadAction) => { + state.tabs = action.payload + }, addTab: (state, action: PayloadAction) => { const existingTab = state.tabs.find((tab) => tab.path === action.payload.path) if (!existingTab) { @@ -53,5 +56,5 @@ const tabsSlice = createSlice({ } }) -export const { addTab, removeTab, setActiveTab, updateTab } = tabsSlice.actions +export const { setTabs, addTab, removeTab, setActiveTab, updateTab } = tabsSlice.actions export default tabsSlice.reducer From 9a92372c3e7a196545ac97d24d4dbfa7b62f61df Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Thu, 4 Sep 2025 11:41:26 +0800 Subject: [PATCH 03/41] refactor(mcp): use includes http to detect streamable http type mcp server (#9865) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit refactor(mcp): 简化 McpServerTypeSchema 的类型校验逻辑 将联合类型替换为字符串校验并优化 http 相关类型的转换 --- src/renderer/src/types/mcp.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/src/renderer/src/types/mcp.ts b/src/renderer/src/types/mcp.ts index 18fd6683f2..cfc7271714 100644 --- a/src/renderer/src/types/mcp.ts +++ b/src/renderer/src/types/mcp.ts @@ -16,22 +16,18 @@ export type MCPConfigSample = z.infer * 允许 inMemory 作为合法字段,需要额外校验 name 是否 builtin */ export const McpServerTypeSchema = z - .union([ - z.literal('stdio'), - z.literal('sse'), - z.literal('streamableHttp'), - z.literal('http'), - z.literal('streamable_http'), - z.literal('inMemory') - ]) + .string() .transform((type) => { - if (type === 'streamable_http' || type === 'http') { + if (type.includes('http')) { return 'streamableHttp' } else { return type } }) - .default('stdio') // 大多数情况下默认使用 stdio + .pipe( + z.union([z.literal('stdio'), z.literal('sse'), z.literal('streamableHttp'), z.literal('inMemory')]).default('stdio') // 大多数情况下默认使用 stdio + ) + /** * 定义单个 MCP 服务器的配置。 * FIXME: 为了兼容性,暂时允许用户编辑任意字段,这可能会导致问题。 From 128b1fe9bc44a5f7ed4c7051e3bf1bd781660063 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Thu, 4 Sep 2025 13:01:39 +0800 Subject: [PATCH 04/41] fix(translate): wrong copy button state (#9867) --- src/renderer/src/i18n/locales/en-us.json | 2 +- src/renderer/src/i18n/locales/ja-jp.json | 2 +- src/renderer/src/i18n/locales/ru-ru.json | 2 +- src/renderer/src/i18n/locales/zh-cn.json | 2 +- src/renderer/src/i18n/locales/zh-tw.json | 2 +- src/renderer/src/i18n/translate/el-gr.json | 7 +++++-- src/renderer/src/i18n/translate/es-es.json | 7 +++++-- src/renderer/src/i18n/translate/fr-fr.json | 7 +++++-- src/renderer/src/i18n/translate/pt-pt.json | 7 +++++-- src/renderer/src/pages/translate/TranslatePage.tsx | 2 +- 10 files changed, 26 insertions(+), 14 deletions(-) diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index 7277772667..057abc94ec 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -4149,7 +4149,7 @@ "aborted": "Translation aborted" }, "input": { - "placeholder": "Text, files, or images (OCR supported) can be pasted or dragged in" + "placeholder": "Text, text files, or images (with OCR support) can be pasted or dragged in" }, "language": { "not_pair": "Source language is different from the set language", diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 0707af3a22..ebf88ef989 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -4149,7 +4149,7 @@ "aborted": "翻訳中止" }, "input": { - "placeholder": "テキスト、ファイル、画像(OCR対応)を貼り付けたりドラッグアンドドロップしたりできます" + "placeholder": "テキスト、テキストファイル、画像(OCR対応)を貼り付けたり、ドラッグして挿入したりできます" }, "language": { "not_pair": "ソース言語が設定された言語と異なります", diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index dc9aa30d7f..1b773c956d 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -4149,7 +4149,7 @@ "aborted": "Перевод прерван" }, "input": { - "placeholder": "Можно вставить или перетащить текст, файлы, изображения (поддержка OCR)" + "placeholder": "Можно вставить или перетащить текст, текстовые файлы, изображения (с поддержкой OCR)" }, "language": { "not_pair": "Исходный язык отличается от настроенного", diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index cde07a4f56..16d63aab77 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -4149,7 +4149,7 @@ "aborted": "翻译中止" }, "input": { - "placeholder": "可粘贴或拖入文本、文件、图片(支持OCR)" + "placeholder": "可粘贴或拖入文本、文本文件、图片(支持OCR)" }, "language": { "not_pair": "源语言与设置的语言不同", diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index aa9780c3be..af61bc2936 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -4149,7 +4149,7 @@ "aborted": "翻譯中止" }, "input": { - "placeholder": "可粘貼或拖入文字、檔案、圖片(支援OCR)" + "placeholder": "可粘貼或拖入文字、文字檔案、圖片(支援OCR)" }, "language": { "not_pair": "源語言與設定的語言不同", diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index e274906fbf..5960f6ac47 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -538,7 +538,10 @@ "tip": "Στη γραμμή εργαλείων των εκτελέσιμων blocks κώδικα θα εμφανίζεται το κουμπί εκτέλεσης· προσέξτε να μην εκτελέσετε επικίνδυνο κώδικα!", "title": "Εκτέλεση Κώδικα" }, - "code_image_tools": "Ενεργοποίηση εργαλείου προεπισκόπησης", + "code_image_tools": { + "label": "Ενεργοποίηση εργαλείου προεπισκόπησης", + "tip": "Ενεργοποίηση εργαλείου προεπισκόπησης για εικόνες που αποδίδονται από blocks κώδικα όπως το mermaid" + }, "code_wrappable": "Οι κώδικες μπορούν να γράφονται σε διαφορετική γραμμή", "context_count": { "label": "Πλήθος ενδιάμεσων", @@ -4146,7 +4149,7 @@ "aborted": "Η μετάφραση διακόπηκε" }, "input": { - "placeholder": "Μπορείτε να επικολλήσετε ή να σύρετε κείμενο, αρχεία, εικόνες (με υποστήριξη OCR)" + "placeholder": "Μπορείτε να επικολλήσετε ή να σύρετε κείμενο, αρχεία κειμένου, εικόνες (υποστηρίζεται η OCR)" }, "language": { "not_pair": "Η γλώσσα πηγής διαφέρει από την οριζόμενη γλώσσα", diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index f5307ba7d0..3b8e033172 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -538,7 +538,10 @@ "tip": "En la barra de herramientas de bloques de código ejecutables se mostrará un botón de ejecución. ¡Tenga cuidado en no ejecutar código peligroso!", "title": "Ejecución de Código" }, - "code_image_tools": "Activar herramientas de vista previa", + "code_image_tools": { + "label": "Habilitar herramienta de vista previa", + "tip": "Habilitar herramientas de vista previa para imágenes renderizadas de bloques de código como mermaid" + }, "code_wrappable": "Bloques de código reemplazables", "context_count": { "label": "Número de contextos", @@ -4146,7 +4149,7 @@ "aborted": "Traducción cancelada" }, "input": { - "placeholder": "Se puede pegar o arrastrar texto, archivos e imágenes (compatible con OCR)" + "placeholder": "Puede pegar o arrastrar texto, archivos de texto o imágenes (compatible con OCR)" }, "language": { "not_pair": "El idioma de origen es diferente al idioma configurado", diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index d2df604796..181f2b5f75 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -538,7 +538,10 @@ "tip": "Une bouton d'exécution s'affichera dans la barre d'outils des blocs de code exécutables. Attention à ne pas exécuter de code dangereux !", "title": "Exécution de code" }, - "code_image_tools": "Activer l'outil d'aperçu", + "code_image_tools": { + "label": "Activer l'outil d'aperçu", + "tip": "Activer les outils de prévisualisation pour les images rendues des blocs de code tels que mermaid" + }, "code_wrappable": "Blocs de code avec retours à la ligne", "context_count": { "label": "Nombre de contextes", @@ -4146,7 +4149,7 @@ "aborted": "Traduction annulée" }, "input": { - "placeholder": "Peut coller ou glisser du texte, des fichiers, des images (avec reconnaissance optique de caractères)" + "placeholder": "Peut coller ou glisser du texte, des fichiers texte ou des images (avec prise en charge de l'OCR)" }, "language": { "not_pair": "La langue source est différente de la langue définie", diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index f0f6b2f140..fa2faa1a24 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -538,7 +538,10 @@ "tip": "A barra de ferramentas de blocos de código executáveis exibirá um botão de execução; atenção para não executar códigos perigosos!", "title": "Execução de Código" }, - "code_image_tools": "Ativar ferramenta de pré-visualização", + "code_image_tools": { + "label": "Habilitar ferramenta de visualização", + "tip": "Ativar ferramentas de visualização para imagens renderizadas de blocos de código como mermaid" + }, "code_wrappable": "Bloco de código com quebra de linha", "context_count": { "label": "Número de contexto", @@ -4146,7 +4149,7 @@ "aborted": "Tradução interrompida" }, "input": { - "placeholder": "Pode colar ou arrastar e soltar texto, arquivos e imagens (suporte a OCR)" + "placeholder": "Pode colar ou arrastar texto, arquivos de texto ou imagens (com suporte a OCR)" }, "language": { "not_pair": "O idioma de origem é diferente do idioma definido", diff --git a/src/renderer/src/pages/translate/TranslatePage.tsx b/src/renderer/src/pages/translate/TranslatePage.tsx index 02c9757bff..6b31bae488 100644 --- a/src/renderer/src/pages/translate/TranslatePage.tsx +++ b/src/renderer/src/pages/translate/TranslatePage.tsx @@ -277,7 +277,7 @@ const TranslatePage: FC = () => { // 控制复制按钮 const onCopy = () => { navigator.clipboard.writeText(translatedContent) - setCopied(false) + setCopied(true) } // 控制历史记录点击 From 9ff4acf092b974a174cd48f1531f1cd45c3a1a28 Mon Sep 17 00:00:00 2001 From: Konv Suu <2583695112@qq.com> Date: Thu, 4 Sep 2025 13:15:02 +0800 Subject: [PATCH 05/41] fix: regex pattern error when update manual blacklist (#9871) --- .../WebSearchSettings/BlacklistSettings.tsx | 28 ++++++++++++++----- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/src/renderer/src/pages/settings/WebSearchSettings/BlacklistSettings.tsx b/src/renderer/src/pages/settings/WebSearchSettings/BlacklistSettings.tsx index de94587e2f..47852fd080 100644 --- a/src/renderer/src/pages/settings/WebSearchSettings/BlacklistSettings.tsx +++ b/src/renderer/src/pages/settings/WebSearchSettings/BlacklistSettings.tsx @@ -71,15 +71,27 @@ const BlacklistSettings: FC = () => { function updateManualBlacklist(blacklist: string) { const blacklistDomains = blacklist.split('\n').filter((url) => url.trim() !== '') - const validDomains: string[] = [] const hasError = blacklistDomains.some((domain) => { - const parsed = parseMatchPattern(domain.trim()) - if (parsed === null) { - return true // 有错误 + const trimmedDomain = domain.trim() + // 正则表达式 + if (trimmedDomain.startsWith('/') && trimmedDomain.endsWith('/')) { + try { + const regexPattern = trimmedDomain.slice(1, -1) + new RegExp(regexPattern, 'i') + validDomains.push(trimmedDomain) + return false + } catch (error) { + return true + } + } else { + const parsed = parseMatchPattern(trimmedDomain) + if (parsed === null) { + return true + } + validDomains.push(trimmedDomain) + return false } - validDomains.push(domain.trim()) - return false }) setErrFormat(hasError) @@ -237,7 +249,9 @@ const BlacklistSettings: FC = () => { - {errFormat && } + {errFormat && ( + + )} From a227f6dcb9437085a30551d99f735d0b920bea9c Mon Sep 17 00:00:00 2001 From: MyPrototypeWhat <43230886+MyPrototypeWhat@users.noreply.github.com> Date: Thu, 4 Sep 2025 14:03:04 +0800 Subject: [PATCH 06/41] Feat/aisdk package (#7404) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: enhance AI SDK middleware integration and support - Added AiSdkMiddlewareBuilder for dynamic middleware construction based on various conditions. - Updated ModernAiProvider to utilize new middleware configuration, improving flexibility in handling completions. - Refactored ApiService to pass middleware configuration during AI completions, enabling better control over processing. - Introduced new README documentation for the middleware builder, outlining usage and supported conditions. * feat: enhance AI core functionality with smoothStream integration - Added smoothStream to the middleware exports in index.ts for improved streaming capabilities. - Updated PluginEnabledAiClient to conditionally apply middlewares, removing the default simulateStreamingMiddleware. - Modified ModernAiProvider to utilize smoothStream in streamText, enhancing text processing with configurable chunking and delay options. * feat: enhance AI SDK documentation and client functionality - Added detailed usage examples for the native provider registry in the README.md, demonstrating how to create and utilize custom provider registries. - Updated ApiClientFactory to enforce type safety for model instances. - Refactored PluginEnabledAiClient methods to support both built-in logic and custom registry usage for text and object generation, improving flexibility and usability. * refactor: update AiSdkToChunkAdapter and middleware for improved chunk handling - Modified convertAndEmitChunk method to handle new chunk types and streamline processing. - Adjusted thinkingTimeMiddleware to remove unnecessary parameters and enhance clarity. - Updated middleware integration in AiSdkMiddlewareBuilder for better middleware management. * feat: add Cherry Studio transformation and settings plugins - Introduced cherryStudioTransformPlugin for converting Cherry Studio messages to AI SDK format, enhancing compatibility. - Added cherryStudioSettingsPlugin to manage Assistant settings like temperature and TopP. - Implemented createCherryStudioContext function for preparing context metadata for Cherry Studio calls. * fix: refine experimental_transform handling and improve chunking logic - Updated PluginEnabledAiClient to streamline the handling of experimental_transform parameters. - Adjusted ModernAiProvider's smoothStream configuration for better chunking of text, enhancing processing efficiency. - Re-enabled block updates in messageThunk for improved state management. * refactor: update ApiClientFactory and index_new for improved type handling and provider mapping - Changed the type of options in ClientConfig to 'any' for flexibility. - Overloaded createImageClient method to support different provider settings. - Added vertexai mapping to the provider type mapping in index_new.ts for enhanced compatibility. * feat: enhance AI SDK documentation and client functionality - Added detailed usage examples for the native provider registry in the README.md, demonstrating how to create and utilize custom provider registries. - Updated ApiClientFactory to enforce type safety for model instances. - Refactored PluginEnabledAiClient methods to support both built-in logic and custom registry usage for text and object generation, improving flexibility and usability. * feat: add openai-compatible provider and enhance provider configuration - Introduced the @ai-sdk/openai-compatible package to support compatibility with OpenAI. - Added a new ProviderConfigFactory and ProviderConfigBuilder for streamlined provider configuration. - Updated the provider registry to include the new Google Vertex AI import path. - Enhanced the index.ts to export new provider configuration utilities for better type safety and usability. - Refactored ApiService and middleware to integrate the new provider configurations effectively. * feat: enhance Vertex AI provider integration and configuration - Added support for Google Vertex AI credentials in the provider configuration. - Refactored the VertexAPIClient to handle both standard and VertexProvider types. - Implemented utility functions to check Vertex AI configuration completeness and create VertexProvider instances. - Updated provider mapping in index_new.ts to ensure proper handling of Vertex AI settings. * feat: enhance provider options and examples for AI SDK - Introduced new utility functions for creating and merging provider options, improving type safety and usability. - Added comprehensive examples for OpenAI, Anthropic, Google, and generic provider options to demonstrate usage. - Refactored existing code to streamline provider configuration and enhance clarity in the options management. - Updated the PluginEnabledAiClient to simplify the handling of model parameters and improve overall functionality. * feat: add patch for Google Vertex AI and enhance private key handling - Introduced a patch for the @ai-sdk/google-vertex package to improve URL handling based on region. - Added a new utility function to format private keys, ensuring correct PEM structure and validation. - Updated the ProviderConfigBuilder to utilize the new private key formatting function for Google credentials. - Created a pnpm workspace configuration to manage patched dependencies effectively. * feat: add OpenAI Compatible provider and enhance provider configuration - Introduced a new OpenAI Compatible provider to the AiProviderRegistry, allowing for integration with the @ai-sdk/openai-compatible package. - Updated provider configuration logic to support the new provider, including adjustments to API host formatting and options management. - Refactored middleware to streamline handling of OpenAI-specific configurations. * fix: enhance anthropic provider configuration and middleware handling - Updated providerToAiSdkConfig to support both OpenAI and Anthropic providers, improving flexibility in API host formatting. - Refactored thinkingTimeMiddleware to ensure all chunks are correctly enqueued, enhancing middleware functionality. - Corrected parameter naming in getAnthropicReasoningParams for consistency and clarity in configuration. * feat: enhance AI SDK chunk handling and tool call processing - Introduced ToolCallChunkHandler for managing tool call events and results, improving the handling of tool interactions. - Updated AiSdkToChunkAdapter to utilize the new handler, streamlining the processing of tool call chunks. - Refactored transformParameters to support dynamic tool integration and improved parameter handling. - Adjusted provider mapping in factory.ts to include new provider types, enhancing compatibility with various AI services. - Removed obsolete cherryStudioTransformPlugin to clean up the codebase and focus on more relevant functionality. * feat: enhance provider ID resolution in AI SDK - Updated getAiSdkProviderId function to include mapping for provider types, improving compatibility with third-party SDKs. - Refined return logic to ensure correct provider ID resolution, enhancing overall functionality and support for various providers. * refactor: restructure AI Core architecture and enhance client functionality - Updated the AI Core documentation to reflect the new architecture and design principles, emphasizing modularity and type safety. - Refactored the client structure by removing obsolete files and consolidating client creation logic into a more streamlined format. - Introduced a new core module for managing execution and middleware, improving the overall organization of the codebase. - Enhanced the orchestration layer to provide a clearer API for users, integrating the creation and execution processes more effectively. - Added comprehensive type definitions and utility functions for better type safety and usability across the SDK. * refactor: simplify AI Core architecture and enhance runtime execution - Restructured the AI Core documentation to reflect a simplified two-layer architecture, focusing on clear responsibilities between models and runtime layers. - Removed the orchestration layer and consolidated its functionality into the runtime layer, streamlining the API for users. - Introduced a new runtime executor for managing plugin-enhanced AI calls, improving the handling of execution and middleware. - Updated the core modules to enhance type safety and usability, including comprehensive type definitions for model creation and execution configurations. - Removed obsolete files and refactored existing code to improve organization and maintainability across the SDK. * feat: enhance AI Core runtime with advanced model handling and middleware support - Introduced new high-level APIs for model creation and configuration, improving usability for advanced users. - Enhanced the RuntimeExecutor to support both direct model usage and model ID resolution, allowing for more flexible execution options. - Updated existing methods to accept middleware configurations, streamlining the integration of custom processing logic. - Refactored the plugin system to better accommodate middleware, enhancing the overall extensibility of the AI Core. - Improved documentation to reflect the new capabilities and usage patterns for the runtime APIs. * feat: enhance plugin system with new reasoning and text plugins - Introduced `reasonPlugin` and `textPlugin` to improve chunk processing and handling of reasoning content. - Updated `transformStream` method signatures for better type safety and usability. - Enhanced `ThinkingTimeMiddleware` to accurately track thinking time using `performance.now()`. - Refactored `ThinkingBlock` component to utilize block thinking time directly, improving performance and clarity. - Added logging for middleware builder to assist in debugging and monitoring middleware configurations. * refactor: update RuntimeExecutor and introduce MCP Prompt Plugin - Changed `pluginClient` to `pluginEngine` in `RuntimeExecutor` for clarity and consistency. - Updated method calls in `RuntimeExecutor` to use the new `pluginEngine`. - Enhanced `AiSdkMiddlewareBuilder` to include `mcpTools` in the middleware configuration. - Added `MCPPromptPlugin` to support tool calls within prompts, enabling recursive processing and improved handling of tool interactions. - Updated `ApiService` to pass `mcpTools` during chat completion requests, enhancing integration with the new plugin system. * feat: enhance MCP Prompt plugin and recursive call capabilities - Updated `tsconfig.web.json` to support wildcard imports for `@cherrystudio/ai-core`. - Enhanced `package.json` to include type definitions and imports for built-in plugins. - Introduced recursive call functionality in `PluginManager` and `PluginEngine`, allowing for improved handling of tool interactions. - Added `MCPPromptPlugin` to facilitate tool calls within prompts, enabling recursive processing of tool results. - Refactored `transformStream` methods across plugins to accommodate new parameters and improve type safety. * :