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] 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"