From 375f966e9a6c10e680f6607ed2cb617be0bb3c73 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Sat, 23 Aug 2025 12:24:38 +0800 Subject: [PATCH 1/8] fix(AttachmentPreview): ext should not be case sensitive (#9426) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit fix(AttachmentPreview): 修复图片扩展名大小写敏感问题 --- src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx index fcf9b083fb..7df45c63ae 100644 --- a/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx +++ b/src/renderer/src/pages/home/Inputbar/AttachmentPreview.tsx @@ -83,7 +83,7 @@ export const getFileIcon = (type?: string) => { export const FileNameRender: FC<{ file: FileMetadata }> = ({ file }) => { const [visible, setVisible] = useState(false) const isImage = (ext: string) => { - return ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'].includes(ext) + return ['.png', '.jpg', '.jpeg', '.gif', '.bmp', '.webp'].includes(ext.toLocaleLowerCase()) } const fullName = FileManager.formatFileName(file) From 3be7c2e1a876bc33f5e90716f6d5745503bc0ff7 Mon Sep 17 00:00:00 2001 From: one Date: Sat, 23 Aug 2025 17:31:10 +0800 Subject: [PATCH 2/8] fix: HtmlArtifacts title overflow (#9434) * fix: HtmlArtifacts title overflow * style: fix lint errors --- .../components/CodeBlockView/HtmlArtifactsCard.tsx | 14 +++++++++----- .../CodeBlockView/HtmlArtifactsPopup.tsx | 10 +++++----- src/renderer/src/components/Tab/TabContainer.tsx | 2 +- 3 files changed, 15 insertions(+), 11 deletions(-) diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx index 3a82db90fa..acb4a9c4f1 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsCard.tsx @@ -157,6 +157,7 @@ const IconWrapper = styled.div<{ $isStreaming: boolean }>` display: flex; align-items: center; justify-content: center; + flex-shrink: 0; width: 44px; height: 44px; background: ${(props) => @@ -177,13 +178,16 @@ const TitleSection = styled.div` gap: 6px; ` -const Title = styled.h3` - margin: 0 !important; - font-size: 14px !important; - font-weight: 600; - color: var(--color-text); +const Title = styled.span` + font-size: 14px; + font-weight: bold; + color: var(--color-text-1); line-height: 1.4; font-family: 'Ubuntu'; + display: -webkit-box; + -webkit-line-clamp: 1; + -webkit-box-orient: vertical; + overflow: hidden; ` const TypeBadge = styled.div` diff --git a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx index a548d5e163..216e247701 100644 --- a/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx +++ b/src/renderer/src/components/CodeBlockView/HtmlArtifactsPopup.tsx @@ -1,7 +1,7 @@ import CodeEditor, { CodeEditorHandles } from '@renderer/components/CodeEditor' import { isLinux, isMac, isWin } from '@renderer/config/constant' import { classNames } from '@renderer/utils' -import { Button, Modal, Splitter, Tooltip } from 'antd' +import { Button, Modal, Splitter, Tooltip, Typography } from 'antd' import { Code, Eye, Maximize2, Minimize2, SaveIcon, SquareSplitHorizontal, X } from 'lucide-react' import { useEffect, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -43,7 +43,7 @@ const HtmlArtifactsPopup: React.FC = ({ open, title, ht const renderHeader = () => ( setIsFullscreen(!isFullscreen)} className={classNames({ drag: isFullscreen })}> - {title} + {title} @@ -266,13 +266,13 @@ const HeaderRight = styled.div<{ $isFullscreen?: boolean }>` padding-right: ${({ $isFullscreen }) => ($isFullscreen ? (isWin ? '136px' : isLinux ? '120px' : '12px') : '12px')}; ` -const TitleText = styled.span` +const TitleText = styled(Typography.Text)` font-size: 16px; - font-weight: 600; + font-weight: bold; color: var(--color-text); white-space: nowrap; overflow: hidden; - text-overflow: ellipsis; + width: 50%; ` const ViewControls = styled.div` diff --git a/src/renderer/src/components/Tab/TabContainer.tsx b/src/renderer/src/components/Tab/TabContainer.tsx index e3fd8291d3..b8065ed40e 100644 --- a/src/renderer/src/components/Tab/TabContainer.tsx +++ b/src/renderer/src/components/Tab/TabContainer.tsx @@ -209,7 +209,7 @@ const TabsBar = styled.div<{ $isFullscreen: boolean }>` height: var(--navbar-height); position: relative; -webkit-app-region: drag; - + /* 确保交互元素在拖拽区域之上 */ > * { position: relative; From d6866052c45d5e0c26f9d66cb454a0aeaa42ca92 Mon Sep 17 00:00:00 2001 From: beyondkmp Date: Sat, 23 Aug 2025 18:59:29 +0800 Subject: [PATCH 3/8] fix: add copilot header to fix json error (#9456) * add accept type in header * add header --- src/main/services/CopilotService.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/main/services/CopilotService.ts b/src/main/services/CopilotService.ts index f5c773a7cc..345ff39374 100644 --- a/src/main/services/CopilotService.ts +++ b/src/main/services/CopilotService.ts @@ -68,7 +68,11 @@ class CopilotService { constructor() { this.tokenFilePath = path.join(app.getPath('userData'), '.copilot_token') - this.headers = { ...CONFIG.DEFAULT_HEADERS } + this.headers = { + ...CONFIG.DEFAULT_HEADERS, + accept: 'application/json', + 'user-agent': 'Visual Studio Code (desktop)' + } } /** @@ -93,6 +97,7 @@ class CopilotService { 'Sec-Fetch-Site': 'none', 'Sec-Fetch-Mode': 'no-cors', 'Sec-Fetch-Dest': 'empty', + accept: 'application/json', authorization: `token ${token}` } }) From 17cee98617750f7cb7b17cdeee5f6a4761bdaa21 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Sun, 24 Aug 2025 13:42:10 +0800 Subject: [PATCH 4/8] fix(WebSearch): fix web search condition check (#9310) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(web搜索): 修正web搜索功能的条件判断和逻辑处理 修复web搜索启用条件的判断逻辑,统一使用webSearchProviderId作为启用标志 重命名相关函数以更准确表达其功能,并优化quickPanel打开逻辑 * fix(WebSearchButton): 修复快速面板点击逻辑 重构 web search provider 更新逻辑,提取为独立的 updateQuickPanelItem 方法 添加 onClick 处理函数统一管理按钮点击行为 * refactor(WebSearchButton): 更新依赖项数组 * refactor(WebSearchButton): 移除重复的颜色计算并简化图标组件 将颜色计算逻辑从WebSearchIcon组件中移出,统一在父组件中处理 --- .../pages/home/Inputbar/WebSearchButton.tsx | 88 ++++++++++--------- src/renderer/src/services/ApiService.ts | 3 +- 2 files changed, 49 insertions(+), 42 deletions(-) diff --git a/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx b/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx index 70b25084d5..3d84ef690a 100644 --- a/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx +++ b/src/renderer/src/pages/home/Inputbar/WebSearchButton.tsx @@ -28,48 +28,62 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { const { providers } = useWebSearchProviders() const { updateAssistant } = useAssistant(assistant.id) + // 注意:assistant.enableWebSearch 有不同的语义 + /** 表示是否启用网络搜索 */ const enableWebSearch = assistant?.webSearchProviderId || assistant.enableWebSearch const WebSearchIcon = useCallback( - ({ pid, size = 18 }: { pid?: WebSearchProviderId; size?: number }) => { - const iconColor = enableWebSearch ? 'var(--color-primary)' : 'var(--color-icon)' - + ({ pid, size = 18, color }: { pid?: WebSearchProviderId; size?: number; color?: string }) => { switch (pid) { case 'bocha': - return + return case 'exa': // size微调,视觉上和其他图标平衡一些 - return + return case 'tavily': - return + return case 'searxng': - return + return case 'local-baidu': - return + return case 'local-bing': - return + return case 'local-google': - return + return default: - return + return } }, [enableWebSearch] ) - const updateSelectedWebSearchProvider = useCallback( + const updateWebSearchProvider = useCallback( async (providerId?: WebSearchProvider['id']) => { // TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿 - const currentWebSearchProviderId = assistant.webSearchProviderId - const newWebSearchProviderId = currentWebSearchProviderId === providerId ? undefined : providerId startTransition(() => { - updateAssistant({ ...assistant, webSearchProviderId: newWebSearchProviderId, enableWebSearch: false }) + updateAssistant({ + ...assistant, + webSearchProviderId: providerId, + enableWebSearch: false + }) }) }, [assistant, updateAssistant] ) - const updateSelectedWebSearchBuiltin = useCallback(async () => { + const updateQuickPanelItem = useCallback( + async (providerId?: WebSearchProvider['id']) => { + // TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿 + if (providerId === assistant.webSearchProviderId) { + updateWebSearchProvider(undefined) + } else { + updateWebSearchProvider(providerId) + } + }, + [assistant.webSearchProviderId, updateWebSearchProvider] + ) + + const updateToModelBuiltinWebSearch = useCallback(async () => { // TODO: updateAssistant有性能问题,会导致关闭快捷面板卡顿 startTransition(() => { updateAssistant({ ...assistant, webSearchProviderId: undefined, enableWebSearch: !assistant.enableWebSearch }) @@ -90,7 +104,7 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { icon: , isSelected: p.id === assistant?.webSearchProviderId, disabled: !WebSearchService.isWebSearchEnabled(p.id), - action: () => updateSelectedWebSearchProvider(p.id) + action: () => updateQuickPanelItem(p.id) })) .filter((o) => !o.disabled) @@ -103,7 +117,7 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { icon: , isSelected: assistant.enableWebSearch, disabled: !isWebSearchModelEnabled, - action: () => updateSelectedWebSearchBuiltin() + action: () => updateToModelBuiltinWebSearch() }) } @@ -115,36 +129,18 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { assistant?.webSearchProviderId, providers, t, - updateSelectedWebSearchBuiltin, - updateSelectedWebSearchProvider + updateQuickPanelItem, + updateToModelBuiltinWebSearch ]) const openQuickPanel = useCallback(() => { - if (assistant.webSearchProviderId) { - updateSelectedWebSearchProvider(undefined) - return - } - - if (assistant.enableWebSearch) { - updateSelectedWebSearchBuiltin() - return - } - quickPanel.open({ title: t('chat.input.web_search.label'), list: providerItems, symbol: '?', pageSize: 9 }) - }, [ - assistant.webSearchProviderId, - assistant.enableWebSearch, - quickPanel, - t, - providerItems, - updateSelectedWebSearchProvider, - updateSelectedWebSearchBuiltin - ]) + }, [quickPanel, t, providerItems]) const handleOpenQuickPanel = useCallback(() => { if (quickPanel.isVisible && quickPanel.symbol === '?') { @@ -154,18 +150,28 @@ const WebSearchButton: FC = ({ ref, assistant, ToolbarButton }) => { } }, [openQuickPanel, quickPanel]) + const onClick = useCallback(() => { + if (enableWebSearch) { + updateWebSearchProvider(undefined) + } else { + handleOpenQuickPanel() + } + }, [enableWebSearch, handleOpenQuickPanel, updateWebSearchProvider]) + useImperativeHandle(ref, () => ({ openQuickPanel })) + const color = enableWebSearch ? 'var(--color-primary)' : 'var(--color-icon)' + return ( - - + + ) diff --git a/src/renderer/src/services/ApiService.ts b/src/renderer/src/services/ApiService.ts index e6d2c5655c..415b704225 100644 --- a/src/renderer/src/services/ApiService.ts +++ b/src/renderer/src/services/ApiService.ts @@ -477,8 +477,9 @@ export async function fetchChatCompletion({ assistant.settings?.reasoning_effort !== undefined) || (isReasoningModel(model) && (!isSupportedThinkingTokenModel(model) || !isSupportedReasoningEffortModel(model))) + // NOTE:assistant.enableWebSearch 的语义是是否启用模型内置搜索功能 const enableWebSearch = - (assistant.enableWebSearch && isWebSearchModel(model)) || + (assistant.webSearchProviderId && isWebSearchModel(model)) || isOpenRouterBuiltInWebSearchModel(model) || model.id.includes('sonar') || false From fba358c0fc68c4e14957468605a39fb67d0d5f77 Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Sun, 24 Aug 2025 17:00:49 +0800 Subject: [PATCH 5/8] fix(selection): fix missing settings (#9454) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(selection): 修复流式输出设置合并问题并添加调试日志 确保assistant的settings在设置streamOutput时保留原有属性 在ActionGeneral组件中添加处理消息前的调试日志 * style: 移除 TabContainer 组件中的多余空行 * fix(HomeWindow): 修复助手设置被覆盖的问题 * refactor(assistant): 优化助手设置处理逻辑,避免重复创建对象 统一处理助手设置逻辑,确保streamOutput属性存在 在多个地方避免直接修改currentAssistant,改为创建新对象 * fix: 使用cloneDeep替代对象展开并显式关闭功能 修复对象浅拷贝可能导致的问题,使用lodash的cloneDeep进行深拷贝 显式关闭web搜索、mcp服务和知识库功能以确保一致性 * refactor: 注释掉未使用的功能配置以提升代码可读性 --- src/renderer/src/windows/mini/home/HomeWindow.tsx | 14 ++++++++++++-- .../selection/action/components/ActionGeneral.tsx | 3 +++ .../selection/action/components/ActionUtils.ts | 13 ++++++++++++- 3 files changed, 27 insertions(+), 3 deletions(-) diff --git a/src/renderer/src/windows/mini/home/HomeWindow.tsx b/src/renderer/src/windows/mini/home/HomeWindow.tsx index 3444de7257..a8df8a6387 100644 --- a/src/renderer/src/windows/mini/home/HomeWindow.tsx +++ b/src/renderer/src/windows/mini/home/HomeWindow.tsx @@ -21,7 +21,7 @@ import { getMainTextContent } from '@renderer/utils/messageUtils/find' import { defaultLanguage } from '@shared/config/constant' import { IpcChannel } from '@shared/IpcChannel' import { Divider } from 'antd' -import { isEmpty } from 'lodash' +import { cloneDeep, isEmpty } from 'lodash' import { last } from 'lodash' import React, { FC, useCallback, useEffect, useMemo, useRef, useState } from 'react' import { useTranslation } from 'react-i18next' @@ -256,9 +256,19 @@ const HomeWindow: FC<{ draggable?: boolean }> = ({ draggable = true }) => { setIsFirstMessage(false) setUserInputText('') + const newAssistant = cloneDeep(currentAssistant) + if (!newAssistant.settings) { + newAssistant.settings = {} + } + newAssistant.settings.streamOutput = true + // 显式关闭这些功能 + // newAssistant.webSearchProviderId = undefined + newAssistant.mcpServers = undefined + // newAssistant.knowledge_bases = undefined + await fetchChatCompletion({ messages: messagesForContext, - assistant: { ...currentAssistant, settings: { streamOutput: true } }, + assistant: newAssistant, onChunkReceived: (chunk: Chunk) => { switch (chunk.type) { case ChunkType.THINKING_START: diff --git a/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx b/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx index 9f3762b15f..b1d3c18853 100644 --- a/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx +++ b/src/renderer/src/windows/selection/action/components/ActionGeneral.tsx @@ -1,4 +1,5 @@ import { LoadingOutlined } from '@ant-design/icons' +import { loggerService } from '@logger' import CopyButton from '@renderer/components/CopyButton' import { useTopicMessages } from '@renderer/hooks/useMessageOperations' import { useSettings } from '@renderer/hooks/useSettings' @@ -21,6 +22,7 @@ import styled from 'styled-components' import { processMessages } from './ActionUtils' import WindowFooter from './WindowFooter' +const logger = loggerService.withContext('ActionGeneral') interface Props { action: ActionItem scrollToBottom?: () => void @@ -112,6 +114,7 @@ const ActionGeneral: FC = React.memo(({ action, scrollToBottom }) => { } if (!assistantRef.current || !topicRef.current) return + logger.debug('Before peocess message', { assistant: assistantRef.current }) processMessages( assistantRef.current, topicRef.current, diff --git a/src/renderer/src/windows/selection/action/components/ActionUtils.ts b/src/renderer/src/windows/selection/action/components/ActionUtils.ts index 541a4e3bb0..5f41b9b107 100644 --- a/src/renderer/src/windows/selection/action/components/ActionUtils.ts +++ b/src/renderer/src/windows/selection/action/components/ActionUtils.ts @@ -10,6 +10,7 @@ import { Chunk, ChunkType } from '@renderer/types/chunk' import { AssistantMessageStatus, MessageBlockStatus } from '@renderer/types/newMessage' import { formatErrorMessage, isAbortError } from '@renderer/utils/error' import { createErrorBlock, createMainTextBlock, createThinkingBlock } from '@renderer/utils/messageUtils/create' +import { cloneDeep } from 'lodash' const logger = loggerService.withContext('ActionUtils') @@ -53,9 +54,19 @@ export const processMessages = async ( let finished = false + const newAssistant = cloneDeep(assistant) + if (!newAssistant.settings) { + newAssistant.settings = {} + } + newAssistant.settings.streamOutput = true + // 显式关闭这些功能 + newAssistant.webSearchProviderId = undefined + newAssistant.mcpServers = undefined + // newAssistant.knowledge_bases = undefined + await fetchChatCompletion({ messages: [userMessage], - assistant: { ...assistant, settings: { streamOutput: true } }, + assistant: newAssistant, onChunkReceived: (chunk: Chunk) => { if (finished) { return From 6d102ccef8677a76e35ef43a89128a89cff0e858 Mon Sep 17 00:00:00 2001 From: co63oc Date: Sun, 24 Aug 2025 17:15:35 +0800 Subject: [PATCH 6/8] chore: fix typos (#9477) --- src/main/services/ShortcutService.ts | 2 +- src/main/services/WindowService.ts | 4 ++-- src/renderer/src/config/prompts.ts | 6 +++--- src/renderer/src/databases/index.ts | 6 +++--- src/renderer/src/utils/mcp-tools.ts | 2 +- src/renderer/src/utils/translate.ts | 4 ++-- 6 files changed, 12 insertions(+), 12 deletions(-) diff --git a/src/main/services/ShortcutService.ts b/src/main/services/ShortcutService.ts index 12f786d797..97216e6a65 100644 --- a/src/main/services/ShortcutService.ts +++ b/src/main/services/ShortcutService.ts @@ -204,7 +204,7 @@ export function registerShortcuts(window: BrowserWindow) { selectionAssistantSelectTextAccelerator = formatShortcutKey(shortcut.shortcut) break - //the following ZOOMs will register shortcuts seperately, so will return + //the following ZOOMs will register shortcuts separately, so will return case 'zoom_in': globalShortcut.register('CommandOrControl+=', () => handler(window)) globalShortcut.register('CommandOrControl+numadd', () => handler(window)) diff --git a/src/main/services/WindowService.ts b/src/main/services/WindowService.ts index e2c2dc4866..66b4b8d955 100644 --- a/src/main/services/WindowService.ts +++ b/src/main/services/WindowService.ts @@ -555,9 +555,9 @@ export class WindowService { // [Windows] hacky fix // the window is minimized only when in Windows platform - // because it's a workround for Windows, see `hideMiniWindow()` + // because it's a workaround for Windows, see `hideMiniWindow()` if (this.miniWindow?.isMinimized()) { - // don't let the window being seen before we finish adusting the position across screens + // don't let the window being seen before we finish adjusting the position across screens this.miniWindow?.setOpacity(0) // DO NOT use `restore()` here, Electron has the bug with screens of different scale factor // We have to use `show()` here, then set the position and bounds diff --git a/src/renderer/src/config/prompts.ts b/src/renderer/src/config/prompts.ts index 8d52857553..a42b2452ca 100644 --- a/src/renderer/src/config/prompts.ts +++ b/src/renderer/src/config/prompts.ts @@ -166,7 +166,7 @@ export const SEARCH_SUMMARY_PROMPT = ` \` - 7. Follow up question: Based on knowledge, Fomula of Scaled Dot-Product Attention and Multi-Head Attention? + 7. Follow up question: Based on knowledge, Formula of Scaled Dot-Product Attention and Multi-Head Attention? Rephrased question: \` @@ -279,7 +279,7 @@ export const SEARCH_SUMMARY_PROMPT_WEB_ONLY = ` \` - 7. Follow up question: Based on knowledge, Fomula of Scaled Dot-Product Attention and Multi-Head Attention? + 7. Follow up question: Based on knowledge, Formula of Scaled Dot-Product Attention and Multi-Head Attention? Rephrased question: \` @@ -374,7 +374,7 @@ export const SEARCH_SUMMARY_PROMPT_KNOWLEDGE_ONLY = ` \` - 7. Follow up question: Based on knowledge, Fomula of Scaled Dot-Product Attention and Multi-Head Attention? + 7. Follow up question: Based on knowledge, Formula of Scaled Dot-Product Attention and Multi-Head Attention? Rephrased question: \` diff --git a/src/renderer/src/databases/index.ts b/src/renderer/src/databases/index.ts index fe0a5f5b5b..169988f4bf 100644 --- a/src/renderer/src/databases/index.ts +++ b/src/renderer/src/databases/index.ts @@ -66,7 +66,7 @@ db.version(6).stores({ // --- NEW VERSION 7 --- db.version(7) .stores({ - // Re-declare all tables for the new version + // Redeclare all tables for the new version files: 'id, name, origin_name, path, size, ext, type, created_at, count', topics: '&id', // Correct index for topics settings: '&id, value', @@ -79,7 +79,7 @@ db.version(7) db.version(8) .stores({ - // Re-declare all tables for the new version + // Redeclare all tables for the new version files: 'id, name, origin_name, path, size, ext, type, created_at, count', topics: '&id', // Correct index for topics settings: '&id, value', @@ -91,7 +91,7 @@ db.version(8) .upgrade((tx) => upgradeToV8(tx)) db.version(9).stores({ - // Re-declare all tables for the new version + // Redeclare all tables for the new version files: 'id, name, origin_name, path, size, ext, type, created_at, count', topics: '&id', // Correct index for topics settings: '&id, value', diff --git a/src/renderer/src/utils/mcp-tools.ts b/src/renderer/src/utils/mcp-tools.ts index a1f1a2b095..6534c2e103 100644 --- a/src/renderer/src/utils/mcp-tools.ts +++ b/src/renderer/src/utils/mcp-tools.ts @@ -185,7 +185,7 @@ export function mcpToolsToAnthropicTools(mcpTools: MCPTool[]): Array const t: ToolUnion = { name: tool.id, description: tool.description, - // @ts-ignore ignore type as it it unknow + // @ts-ignore ignore type as it it unknown input_schema: tool.inputSchema } return t diff --git a/src/renderer/src/utils/translate.ts b/src/renderer/src/utils/translate.ts index 9e7e48590b..979f38dc83 100644 --- a/src/renderer/src/utils/translate.ts +++ b/src/renderer/src/utils/translate.ts @@ -53,7 +53,7 @@ export const detectLanguage = async (inputText: string): Promise => { - logger.info('Detect langugage by llm') + logger.info('Detect language by llm') let detectedLang = '' await fetchLanguageDetection({ text: sliceByTokens(inputText, 0, 100), @@ -65,7 +65,7 @@ const detectLanguageByLLM = async (inputText: string): Promise { - logger.info('Detect langugage by franc') + logger.info('Detect language by franc') const iso3 = franc(inputText) const isoMap: Record = { From 107c01913d424b5bb208cdfda8bc5400844838da Mon Sep 17 00:00:00 2001 From: Phantom <59059173+EurFelux@users.noreply.github.com> Date: Sun, 24 Aug 2025 18:49:14 +0800 Subject: [PATCH 7/8] feat: error boundary (#9462) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * build: 添加 react-error-boundary 依赖 添加 react-error-boundary 包以增强 React 应用的错误处理能力 * feat(组件): 添加ErrorBoundary组件用于错误边界处理 * feat(home): 为HomeTabs和Chat组件添加错误边界处理 * refactor(ErrorBoundary): 移除多余的ErrorContainer包装并优化结构 * feat(ErrorBoundary): 添加重新加载按钮并优化错误边界样式 添加重新加载功能按钮,方便用户快速恢复应用 调整错误边界容器的布局样式,使其居中显示 * style(ErrorBoundary): 移除ErrorContainer的固定高度以改善布局灵活性 * test(ErrorBoundary): 添加测试错误边界组件的功能按钮 添加一个用于测试错误边界组件功能的按钮组件,该按钮点击后会抛出错误以验证错误边界是否正常工作。此组件仅用于测试,合并前需要删除。 * feat(路由): 为路由组件添加错误边界处理 在Router组件中包裹ErrorBoundary以捕获并处理子组件中的错误 * fix(ErrorBoundary): 修复错误边界中翻译键的拼写错误 * feat(i18n): 添加边界错误处理和主题不存在错误的多语言支持 * refactor(ErrorBoundary): 移除用于测试的ThrowError组件 --- package.json | 1 + src/renderer/src/Router.tsx | 27 +++++---- src/renderer/src/components/ErrorBoundary.tsx | 57 +++++++++++++++++++ src/renderer/src/i18n/locales/en-us.json | 7 +++ src/renderer/src/i18n/locales/ja-jp.json | 7 +++ src/renderer/src/i18n/locales/ru-ru.json | 7 +++ src/renderer/src/i18n/locales/zh-cn.json | 7 +++ src/renderer/src/i18n/locales/zh-tw.json | 7 +++ src/renderer/src/i18n/translate/el-gr.json | 10 ++++ src/renderer/src/i18n/translate/es-es.json | 10 ++++ src/renderer/src/i18n/translate/fr-fr.json | 10 ++++ src/renderer/src/i18n/translate/pt-pt.json | 10 ++++ src/renderer/src/pages/home/HomePage.tsx | 31 +++++----- yarn.lock | 12 ++++ 14 files changed, 178 insertions(+), 25 deletions(-) create mode 100644 src/renderer/src/components/ErrorBoundary.tsx diff --git a/package.json b/package.json index b866ba7138..13765cf642 100644 --- a/package.json +++ b/package.json @@ -227,6 +227,7 @@ "proxy-agent": "^6.5.0", "react": "^19.0.0", "react-dom": "^19.0.0", + "react-error-boundary": "^6.0.0", "react-hotkeys-hook": "^4.6.1", "react-i18next": "^14.1.2", "react-infinite-scroll-component": "^6.1.0", diff --git a/src/renderer/src/Router.tsx b/src/renderer/src/Router.tsx index 627fb37546..36d045aae5 100644 --- a/src/renderer/src/Router.tsx +++ b/src/renderer/src/Router.tsx @@ -4,6 +4,7 @@ import { FC, useMemo } from 'react' import { HashRouter, Route, Routes } from 'react-router-dom' import Sidebar from './components/app/Sidebar' +import { ErrorBoundary } from './components/ErrorBoundary' import TabsContainer from './components/Tab/TabContainer' import NavigationHandler from './handler/NavigationHandler' import { useNavbarPosition } from './hooks/useSettings' @@ -23,18 +24,20 @@ const Router: FC = () => { const routes = useMemo(() => { return ( - - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - + + + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + } /> + + ) }, []) diff --git a/src/renderer/src/components/ErrorBoundary.tsx b/src/renderer/src/components/ErrorBoundary.tsx new file mode 100644 index 0000000000..5bfeaeb620 --- /dev/null +++ b/src/renderer/src/components/ErrorBoundary.tsx @@ -0,0 +1,57 @@ +import { formatErrorMessage } from '@renderer/utils/error' +import { Alert, Button, Space } from 'antd' +import { ComponentType, ReactNode } from 'react' +import { ErrorBoundary, FallbackProps } from 'react-error-boundary' +import { useTranslation } from 'react-i18next' +import styled from 'styled-components' + +const DefaultFallback: ComponentType = (props: FallbackProps): ReactNode => { + const { t } = useTranslation() + const { error } = props + const debug = async () => { + await window.api.devTools.toggle() + } + const reload = async () => { + await window.api.reload() + } + return ( + + + + + + } + /> + + ) +} + +const ErrorBoundaryCustomized = ({ + children, + fallbackComponent +}: { + children: ReactNode + fallbackComponent?: ComponentType +}) => { + return {children} +} + +const ErrorContainer = styled.div` + display: flex; + justify-content: center; + align-items: center; + width: 100%; + padding: 8px; +` + +export { ErrorBoundaryCustomized as ErrorBoundary } diff --git a/src/renderer/src/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json index b440f49282..faeebeb541 100644 --- a/src/renderer/src/i18n/locales/en-us.json +++ b/src/renderer/src/i18n/locales/en-us.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Backup file format error" }, + "boundary": { + "default": { + "devtools": "Open debug panel", + "message": "It seems that something went wrong...", + "reload": "Reload" + } + }, "chat": { "chunk": { "non_json": "Returned an invalid data format" diff --git a/src/renderer/src/i18n/locales/ja-jp.json b/src/renderer/src/i18n/locales/ja-jp.json index 1b68659cf5..42dbead394 100644 --- a/src/renderer/src/i18n/locales/ja-jp.json +++ b/src/renderer/src/i18n/locales/ja-jp.json @@ -808,6 +808,13 @@ "backup": { "file_format": "バックアップファイルの形式エラー" }, + "boundary": { + "default": { + "devtools": "デバッグパネルを開く", + "message": "何か問題が発生したようです...", + "reload": "再読み込み" + } + }, "chat": { "chunk": { "non_json": "無効なデータ形式が返されました" diff --git a/src/renderer/src/i18n/locales/ru-ru.json b/src/renderer/src/i18n/locales/ru-ru.json index d2fa9a969a..28880dbf96 100644 --- a/src/renderer/src/i18n/locales/ru-ru.json +++ b/src/renderer/src/i18n/locales/ru-ru.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Ошибка формата файла резервной копии" }, + "boundary": { + "default": { + "devtools": "Открыть панель отладки", + "message": "Похоже, возникла какая-то проблема...", + "reload": "Перезагрузить" + } + }, "chat": { "chunk": { "non_json": "Вернулся недопустимый формат данных" diff --git a/src/renderer/src/i18n/locales/zh-cn.json b/src/renderer/src/i18n/locales/zh-cn.json index 816343ea89..8cd92d2649 100644 --- a/src/renderer/src/i18n/locales/zh-cn.json +++ b/src/renderer/src/i18n/locales/zh-cn.json @@ -808,6 +808,13 @@ "backup": { "file_format": "备份文件格式错误" }, + "boundary": { + "default": { + "devtools": "打开调试面板", + "message": "似乎出现了一些问题...", + "reload": "重新加载" + } + }, "chat": { "chunk": { "non_json": "返回了无效的数据格式" diff --git a/src/renderer/src/i18n/locales/zh-tw.json b/src/renderer/src/i18n/locales/zh-tw.json index 8825bbce7c..a193d23e07 100644 --- a/src/renderer/src/i18n/locales/zh-tw.json +++ b/src/renderer/src/i18n/locales/zh-tw.json @@ -808,6 +808,13 @@ "backup": { "file_format": "備份檔案格式錯誤" }, + "boundary": { + "default": { + "devtools": "打開除錯面板", + "message": "似乎出現了一些問題...", + "reload": "重新載入" + } + }, "chat": { "chunk": { "non_json": "返回了無效的資料格式" diff --git a/src/renderer/src/i18n/translate/el-gr.json b/src/renderer/src/i18n/translate/el-gr.json index 985544229b..91d2fc46f0 100644 --- a/src/renderer/src/i18n/translate/el-gr.json +++ b/src/renderer/src/i18n/translate/el-gr.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Λάθος μορφή αρχείου που επιστρέφεται" }, + "boundary": { + "default": { + "devtools": "Άνοιγμα πίνακα αποσφαλμάτωσης", + "message": "Φαίνεται ότι προέκυψε κάποιο πρόβλημα...", + "reload": "Επαναφόρτωση" + } + }, "chat": { "chunk": { "non_json": "Επέστρεψε μη έγκυρη μορφή δεδομένων" @@ -888,6 +895,9 @@ }, "history": { "continue_chat": "Συνεχίστε το συνομιλημένο", + "error": { + "topic_not_found": "Το θέμα δεν υπάρχει" + }, "locate": { "message": "Εφαρμογή στο μήνυμα" }, diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json index d0cd6bd565..99f7637098 100644 --- a/src/renderer/src/i18n/translate/es-es.json +++ b/src/renderer/src/i18n/translate/es-es.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Formato de archivo de copia de seguridad incorrecto" }, + "boundary": { + "default": { + "devtools": "Abrir el panel de depuración", + "message": "Parece que ha surgido un problema...", + "reload": "Recargar" + } + }, "chat": { "chunk": { "non_json": "Devuelve un formato de datos no válido" @@ -888,6 +895,9 @@ }, "history": { "continue_chat": "Continuar chat", + "error": { + "topic_not_found": "El tema no existe" + }, "locate": { "message": "Localizar mensaje" }, diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json index d91c04abe2..d8059651f3 100644 --- a/src/renderer/src/i18n/translate/fr-fr.json +++ b/src/renderer/src/i18n/translate/fr-fr.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Le format du fichier de sauvegarde est incorrect" }, + "boundary": { + "default": { + "devtools": "Ouvrir le panneau de débogage", + "message": "Il semble que quelques problèmes soient survenus...", + "reload": "Recharger" + } + }, "chat": { "chunk": { "non_json": "a renvoyé un format de données invalide" @@ -888,6 +895,9 @@ }, "history": { "continue_chat": "Continuer la conversation", + "error": { + "topic_not_found": "Le sujet n'existe pas" + }, "locate": { "message": "Localiser le message" }, diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json index b66907dfaa..c10a01f78a 100644 --- a/src/renderer/src/i18n/translate/pt-pt.json +++ b/src/renderer/src/i18n/translate/pt-pt.json @@ -808,6 +808,13 @@ "backup": { "file_format": "Formato do arquivo de backup está incorreto" }, + "boundary": { + "default": { + "devtools": "Abrir o painel de depuração", + "message": "Parece que ocorreu um problema...", + "reload": "Recarregar" + } + }, "chat": { "chunk": { "non_json": "Devolveu um formato de dados inválido" @@ -888,6 +895,9 @@ }, "history": { "continue_chat": "Continuar conversando", + "error": { + "topic_not_found": "Tópico inexistente" + }, "locate": { "message": "Localizar mensagem" }, diff --git a/src/renderer/src/pages/home/HomePage.tsx b/src/renderer/src/pages/home/HomePage.tsx index ece6389baa..a32eff2bb1 100644 --- a/src/renderer/src/pages/home/HomePage.tsx +++ b/src/renderer/src/pages/home/HomePage.tsx @@ -1,3 +1,4 @@ +import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { useAssistants } from '@renderer/hooks/useAssistant' import { useNavbarPosition, useSettings } from '@renderer/hooks/useSettings' import { useActiveTopic } from '@renderer/hooks/useTopic' @@ -100,20 +101,24 @@ const HomePage: FC = () => { )} {showAssistants && ( - + + + )} - + + + ) diff --git a/yarn.lock b/yarn.lock index b9833e0cc3..3ef1c7aa89 100644 --- a/yarn.lock +++ b/yarn.lock @@ -8538,6 +8538,7 @@ __metadata: proxy-agent: "npm:^6.5.0" react: "npm:^19.0.0" react-dom: "npm:^19.0.0" + react-error-boundary: "npm:^6.0.0" react-hotkeys-hook: "npm:^4.6.1" react-i18next: "npm:^14.1.2" react-infinite-scroll-component: "npm:^6.1.0" @@ -19026,6 +19027,17 @@ __metadata: languageName: node linkType: hard +"react-error-boundary@npm:^6.0.0": + version: 6.0.0 + resolution: "react-error-boundary@npm:6.0.0" + dependencies: + "@babel/runtime": "npm:^7.12.5" + peerDependencies: + react: ">=16.13.1" + checksum: 10c0/1914d600dee95a14f14af4afe9867b0d35c26c4f7826d23208800ba2a99728659029aad60a6ef95e13430b4d79c2c4c9b3585f50bf508450478760d2e4e732d8 + languageName: node + linkType: hard + "react-hotkeys-hook@npm:^4.6.1": version: 4.6.2 resolution: "react-hotkeys-hook@npm:4.6.2" From 6215dd378e4c59947f7db412e615b05f2f3cbcb5 Mon Sep 17 00:00:00 2001 From: icarus Date: Sun, 24 Aug 2025 18:57:24 +0800 Subject: [PATCH 8/8] =?UTF-8?q?refactor(OCR=E8=AE=BE=E7=BD=AE):=20?= =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E9=94=99=E8=AF=AF=E8=BE=B9=E7=95=8C=E5=A4=84?= =?UTF-8?q?=E7=90=86=E5=B9=B6=E7=A7=BB=E9=99=A4=E6=97=A0=E7=94=A8=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在OCR设置组件中添加ErrorBoundary以处理潜在错误 移除OcrTesseractSettings中的TODO注释 --- .../settings/DocProcessSettings/OcrProviderSettings.tsx | 3 ++- .../src/pages/settings/DocProcessSettings/OcrSettings.tsx | 5 +++-- .../settings/DocProcessSettings/OcrTesseractSettings.tsx | 1 - 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx index a9ba128d7a..501b8d792f 100644 --- a/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx +++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrProviderSettings.tsx @@ -1,4 +1,5 @@ // import { loggerService } from '@logger' +import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { isBuiltinOcrProvider, OcrProvider } from '@renderer/types' import { getOcrProviderLogo } from '@renderer/utils/ocr' import { Avatar, Divider, Flex } from 'antd' @@ -35,7 +36,7 @@ const OcrProviderSettings = ({ provider }: Props) => { - {getProviderSettings()} + {getProviderSettings()} ) } diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx index 9ad2d111ad..2a829e7925 100644 --- a/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx +++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrSettings.tsx @@ -1,4 +1,5 @@ import { PictureOutlined } from '@ant-design/icons' +import { ErrorBoundary } from '@renderer/components/ErrorBoundary' import { useTheme } from '@renderer/context/ThemeProvider' import { useAppSelector } from '@renderer/store' import { OcrProvider } from '@renderer/types' @@ -26,7 +27,7 @@ const OcrSettings: FC = () => { ] return ( - <> + {t('settings.tool.ocr.title')} @@ -35,7 +36,7 @@ const OcrSettings: FC = () => { - + ) } export default OcrSettings diff --git a/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx b/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx index 7e94a31194..8c85cee8bc 100644 --- a/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx +++ b/src/renderer/src/pages/settings/DocProcessSettings/OcrTesseractSettings.tsx @@ -13,7 +13,6 @@ export const OcrTesseractSettings = () => { const { t } = useTranslation() const { provider } = useOcrProvider(BuiltinOcrProviderIds.tesseract) - // TODO: use error boundary if (!isOcrTesseractProvider(provider)) { throw new Error('Not tesseract provider.') }