diff --git a/package.json b/package.json
index 124bf61542..63b94808e9 100644
--- a/package.json
+++ b/package.json
@@ -228,6 +228,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/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}`
}
})
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/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/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/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/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/i18n/locales/en-us.json b/src/renderer/src/i18n/locales/en-us.json
index 2acbf399c8..b8e0f1def9 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 92a3742373..a4830cfa84 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 1cc850db67..488e2e51f4 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 90acff50ac..1d10d66034 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 b6f5456b17..881446f3d3 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 4a8ef581a6..3e5af2ed96 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": "Επέστρεψε μη έγκυρη μορφή δεδομένων"
diff --git a/src/renderer/src/i18n/translate/es-es.json b/src/renderer/src/i18n/translate/es-es.json
index e426fca943..9bd15cfcdf 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"
diff --git a/src/renderer/src/i18n/translate/fr-fr.json b/src/renderer/src/i18n/translate/fr-fr.json
index ad64477c78..da5a60f510 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"
diff --git a/src/renderer/src/i18n/translate/pt-pt.json b/src/renderer/src/i18n/translate/pt-pt.json
index 789f5163cd..afad9be58d 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"
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/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)
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/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.')
}
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
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 = {
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
diff --git a/yarn.lock b/yarn.lock
index 85a7d07e99..97a6971c04 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"
@@ -19057,6 +19058,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"