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"