@lobehub/chat 1.74.2 → 1.74.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (90) hide show
  1. package/CHANGELOG.md +58 -0
  2. package/README.md +1 -1
  3. package/README.zh-CN.md +1 -1
  4. package/changelog/v1.json +18 -0
  5. package/docs/developer/database-schema.dbml +54 -2
  6. package/locales/ar/common.json +51 -0
  7. package/locales/ar/models.json +69 -3
  8. package/locales/ar/providers.json +6 -0
  9. package/locales/bg-BG/common.json +51 -0
  10. package/locales/bg-BG/models.json +69 -3
  11. package/locales/bg-BG/providers.json +6 -0
  12. package/locales/de-DE/common.json +51 -0
  13. package/locales/de-DE/models.json +69 -3
  14. package/locales/de-DE/providers.json +6 -0
  15. package/locales/en-US/common.json +51 -0
  16. package/locales/en-US/models.json +69 -3
  17. package/locales/en-US/providers.json +6 -3
  18. package/locales/es-ES/common.json +51 -0
  19. package/locales/es-ES/models.json +69 -3
  20. package/locales/es-ES/providers.json +6 -0
  21. package/locales/fa-IR/common.json +51 -0
  22. package/locales/fa-IR/models.json +69 -3
  23. package/locales/fa-IR/providers.json +6 -0
  24. package/locales/fr-FR/common.json +51 -0
  25. package/locales/fr-FR/models.json +69 -3
  26. package/locales/fr-FR/providers.json +6 -0
  27. package/locales/it-IT/common.json +51 -0
  28. package/locales/it-IT/models.json +69 -3
  29. package/locales/it-IT/providers.json +6 -0
  30. package/locales/ja-JP/common.json +51 -0
  31. package/locales/ja-JP/models.json +78 -4
  32. package/locales/ja-JP/providers.json +6 -0
  33. package/locales/ko-KR/common.json +51 -0
  34. package/locales/ko-KR/models.json +69 -3
  35. package/locales/ko-KR/providers.json +6 -0
  36. package/locales/nl-NL/common.json +51 -0
  37. package/locales/nl-NL/models.json +69 -3
  38. package/locales/nl-NL/providers.json +6 -0
  39. package/locales/pl-PL/common.json +51 -0
  40. package/locales/pl-PL/models.json +69 -3
  41. package/locales/pl-PL/providers.json +6 -0
  42. package/locales/pt-BR/common.json +51 -0
  43. package/locales/pt-BR/models.json +69 -3
  44. package/locales/pt-BR/providers.json +6 -0
  45. package/locales/ru-RU/common.json +51 -0
  46. package/locales/ru-RU/models.json +69 -3
  47. package/locales/ru-RU/providers.json +6 -0
  48. package/locales/tr-TR/common.json +51 -0
  49. package/locales/tr-TR/models.json +69 -3
  50. package/locales/tr-TR/providers.json +6 -0
  51. package/locales/vi-VN/common.json +51 -0
  52. package/locales/vi-VN/models.json +69 -3
  53. package/locales/vi-VN/providers.json +6 -0
  54. package/locales/zh-CN/common.json +53 -2
  55. package/locales/zh-CN/models.json +79 -13
  56. package/locales/zh-CN/providers.json +6 -4
  57. package/locales/zh-TW/common.json +51 -0
  58. package/locales/zh-TW/models.json +81 -4
  59. package/locales/zh-TW/providers.json +6 -0
  60. package/package.json +5 -5
  61. package/packages/web-crawler/src/utils/__tests__/withTimeout.test.ts +0 -1
  62. package/packages/web-crawler/src/utils/html/yingchao.html +0 -16
  63. package/packages/web-crawler/src/utils/htmlToMarkdown.test.ts +1 -1
  64. package/src/app/(backend)/webapi/plugin/store/route.ts +6 -2
  65. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/Checker.tsx +4 -0
  66. package/src/database/client/db.ts +102 -11
  67. package/src/database/client/migrations.json +38 -8
  68. package/src/database/migrations/0018_add_client_id_for_entities.sql +32 -0
  69. package/src/database/migrations/meta/0018_snapshot.json +4212 -0
  70. package/src/database/migrations/meta/_journal.json +7 -0
  71. package/src/database/models/drizzleMigration.ts +23 -0
  72. package/src/database/schemas/agent.ts +48 -31
  73. package/src/database/schemas/file.ts +32 -16
  74. package/src/database/schemas/message.ts +91 -54
  75. package/src/database/schemas/rag.ts +65 -32
  76. package/src/database/schemas/session.ts +6 -3
  77. package/src/database/schemas/topic.ts +31 -24
  78. package/src/features/InitClientDB/ErrorResult.tsx +53 -32
  79. package/src/features/InitClientDB/features/DatabaseRepair/Backup.tsx +77 -0
  80. package/src/features/InitClientDB/features/DatabaseRepair/Diagnosis.tsx +98 -0
  81. package/src/features/InitClientDB/features/DatabaseRepair/Repair.tsx +220 -0
  82. package/src/features/InitClientDB/features/DatabaseRepair/index.tsx +85 -0
  83. package/src/features/ModelSwitchPanel/index.tsx +13 -7
  84. package/src/locales/default/common.ts +53 -1
  85. package/src/store/global/actions/clientDb.ts +19 -3
  86. package/src/store/global/initialState.ts +6 -1
  87. package/src/store/global/selectors/clientDB.ts +43 -0
  88. package/src/store/global/selectors/index.ts +1 -0
  89. package/src/store/user/slices/settings/selectors/general.test.ts +90 -0
  90. package/src/types/clientDB.ts +13 -0
@@ -1,18 +1,21 @@
1
- import { Highlighter, Icon, Modal } from '@lobehub/ui';
2
- import { Button } from 'antd';
1
+ import { Alert, Highlighter, Icon, Modal } from '@lobehub/ui';
2
+ import { Button, Spin } from 'antd';
3
3
  import { createStyles } from 'antd-style';
4
4
  import isEqual from 'fast-deep-equal';
5
- import { TriangleAlert } from 'lucide-react';
6
- import Link from 'next/link';
5
+ import { PenToolIcon, TriangleAlert } from 'lucide-react';
6
+ import dynamic from 'next/dynamic';
7
7
  import { ReactNode, memo, useState } from 'react';
8
- import { Trans, useTranslation } from 'react-i18next';
8
+ import { useTranslation } from 'react-i18next';
9
9
  import { Center, Flexbox } from 'react-layout-kit';
10
- import Balancer from 'react-wrap-balancer';
11
10
 
12
- import { GITHUB_ISSUES } from '@/const/url';
13
11
  import { githubService } from '@/services/github';
14
12
  import { useGlobalStore } from '@/store/global';
15
13
 
14
+ const DatabaseRepair = dynamic(() => import('./features/DatabaseRepair'), {
15
+ loading: () => <Spin fullscreen />,
16
+ ssr: false,
17
+ });
18
+
16
19
  const useStyles = createStyles(({ css, token }) => ({
17
20
  bg: css`
18
21
  cursor: pointer;
@@ -33,7 +36,6 @@ const useStyles = createStyles(({ css, token }) => ({
33
36
  transform: scale(1);
34
37
  }
35
38
  `,
36
-
37
39
  text: css`
38
40
  font-size: 15px;
39
41
  color: ${token.colorText};
@@ -56,6 +58,7 @@ const ErrorResult = memo<FailedModalProps>(({ children }) => {
56
58
  const initializeClientDB = useGlobalStore((s) => s.initializeClientDB);
57
59
  const error = useGlobalStore((s) => s.initClientDBError, isEqual);
58
60
  const [open, setOpen] = useState(false);
61
+ const [showRepair, setShowRepair] = useState(true);
59
62
 
60
63
  return (
61
64
  <>
@@ -88,35 +91,53 @@ const ErrorResult = memo<FailedModalProps>(({ children }) => {
88
91
  open={open}
89
92
  title={t('clientDB.error.title')}
90
93
  >
91
- <Center gap={24}>
92
- <Balancer>
93
- <Trans i18nKey="clientDB.error.desc" ns={'common'}>
94
- 非常抱歉,Pglite 数据库初始化过程中发生异常。请尝试重试,或
95
- <Link
96
- aria-label={'issue'}
97
- href={GITHUB_ISSUES}
94
+ {showRepair ? (
95
+ <DatabaseRepair setShowRepair={setShowRepair} />
96
+ ) : (
97
+ <Center gap={24}>
98
+ <div>{t('clientDB.error.desc')}</div>
99
+ <Flexbox width={'100%'}>
100
+ <Alert
101
+ description={`[${error?.name}] ${error?.message}`}
102
+ extra={
103
+ <Flexbox gap={8} style={{ marginTop: 8, overflow: 'scroll' }} width={'100%'}>
104
+ <Highlighter copyButtonSize={'small'} language={'json'}>
105
+ {JSON.stringify(error, null, 2)}
106
+ </Highlighter>
107
+ </Flexbox>
108
+ }
109
+ message={t('clientDB.error.detailTitle')}
110
+ type={'error'}
111
+ />
112
+ </Flexbox>
113
+
114
+ <Flexbox horizontal justify={'space-between'}>
115
+ <Button
98
116
  onClick={(e) => {
99
117
  e.preventDefault();
100
118
  githubService.submitPgliteInitError(error);
101
119
  }}
102
- target="_blank"
120
+ target={'_blank'}
121
+ type={'text'}
103
122
  >
104
- 提交问题
105
- </Link>
106
- 我们将会第一时间帮你排查问题。
107
- </Trans>
108
- </Balancer>
109
- <Button onClick={() => initializeClientDB()} size={'large'} type={'primary'}>
110
- {t('clientDB.error.retry')}
111
- </Button>
112
-
113
- <Flexbox gap={8} style={{ marginTop: 8 }} width={'100%'}>
114
- {t('clientDB.error.detail', { message: error?.message, type: error?.name })}
115
- <Highlighter copyButtonSize={'small'} language={'json'}>
116
- {JSON.stringify(error, null, 2)}
117
- </Highlighter>
118
- </Flexbox>
119
- </Center>
123
+ {t('clientDB.error.report')}
124
+ </Button>
125
+ <Flexbox gap={8} horizontal>
126
+ <Button
127
+ icon={<Icon icon={PenToolIcon} />}
128
+ onClick={() => {
129
+ setShowRepair(true);
130
+ }}
131
+ >
132
+ {t('clientDB.error.selfSolve')}
133
+ </Button>
134
+ <Button onClick={() => initializeClientDB()} type={'primary'}>
135
+ {t('clientDB.error.retry')}
136
+ </Button>
137
+ </Flexbox>
138
+ </Flexbox>
139
+ </Center>
140
+ )}
120
141
  </Modal>
121
142
  </>
122
143
  );
@@ -0,0 +1,77 @@
1
+ import { Alert } from '@lobehub/ui';
2
+ import { App, Button, Card, Typography } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import { AlertCircle } from 'lucide-react';
5
+ import { useTranslation } from 'react-i18next';
6
+ import { Flexbox } from 'react-layout-kit';
7
+
8
+ import { resetClientDatabase } from '@/database/client/db';
9
+
10
+ const { Text, Paragraph } = Typography;
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ card: css`
14
+ border-radius: ${token.borderRadiusLG}px;
15
+ `,
16
+ }));
17
+
18
+ const Backup = () => {
19
+ const { t } = useTranslation('common');
20
+ const { styles } = useStyles();
21
+
22
+ const { modal } = App.useApp();
23
+ return (
24
+ <Flexbox gap={24}>
25
+ <Card
26
+ className={styles.card}
27
+ extra={<Text type="secondary">{t('clientDB.solve.backup.desc')}</Text>}
28
+ title={t('clientDB.solve.backup.title')}
29
+ >
30
+ <Paragraph>{t('clientDB.solve.backup.exportDesc')}</Paragraph>
31
+ <Button block type={'primary'}>
32
+ {t('clientDB.solve.backup.export')}
33
+ </Button>
34
+ </Card>
35
+
36
+ <Card
37
+ className={styles.card}
38
+ extra={<Text type="secondary">{t('clientDB.solve.backup.reset.desc')}</Text>}
39
+ title={t('clientDB.solve.backup.reset.title')}
40
+ >
41
+ <Flexbox gap={24}>
42
+ <Alert
43
+ description={t('clientDB.solve.backup.reset.alertDesc')}
44
+ icon={<AlertCircle size={16} />}
45
+ message={t('clientDB.solve.backup.reset.alert')}
46
+ showIcon
47
+ type="error"
48
+ variant={'pure'}
49
+ />
50
+
51
+ <Button
52
+ block
53
+ danger
54
+ onClick={() => {
55
+ modal.confirm({
56
+ content: t('clientDB.solve.backup.reset.confirm.desc'),
57
+ okButtonProps: {
58
+ danger: true,
59
+ },
60
+ onOk: async () => {
61
+ await resetClientDatabase();
62
+
63
+ location.reload();
64
+ },
65
+ title: t('clientDB.solve.backup.reset.confirm.title'),
66
+ });
67
+ }}
68
+ >
69
+ {t('clientDB.solve.backup.reset.button')}
70
+ </Button>
71
+ </Flexbox>
72
+ </Card>
73
+ </Flexbox>
74
+ );
75
+ };
76
+
77
+ export default Backup;
@@ -0,0 +1,98 @@
1
+ import { Highlighter } from '@lobehub/ui';
2
+ import { Card, List, Popover, Tag } from 'antd';
3
+ import { createStyles } from 'antd-style';
4
+ import dayjs from 'dayjs';
5
+ import isEqual from 'fast-deep-equal';
6
+ import { useTranslation } from 'react-i18next';
7
+ import { Flexbox } from 'react-layout-kit';
8
+
9
+ import { useGlobalStore } from '@/store/global';
10
+ import { clientDBSelectors } from '@/store/global/selectors';
11
+
12
+ const useStyles = createStyles(({ css, token }) => ({
13
+ card: css`
14
+ margin-block-end: ${token.marginMD}px;
15
+ border-radius: ${token.borderRadiusLG}px;
16
+ `,
17
+ hash: css`
18
+ font-family: ${token.fontFamilyCode};
19
+ `,
20
+ sql: css`
21
+ overflow: hidden;
22
+ display: -webkit-box;
23
+ -webkit-box-orient: vertical;
24
+ -webkit-line-clamp: 3;
25
+
26
+ font-size: 12px;
27
+ `,
28
+ time: css`
29
+ font-size: 12px;
30
+ color: ${token.colorTextTertiary};
31
+ `,
32
+ }));
33
+
34
+ const Diagnosis = () => {
35
+ const { t } = useTranslation('common');
36
+ const { styles, cx } = useStyles();
37
+
38
+ const migrations = useGlobalStore(clientDBSelectors.displayMigrationStatus, isEqual);
39
+
40
+ return (
41
+ <Card
42
+ className={styles.card}
43
+ styles={{ body: { padding: 0 } }}
44
+ title={t('clientDB.solve.diagnosis.title')}
45
+ >
46
+ <List
47
+ dataSource={migrations}
48
+ renderItem={(migration) => (
49
+ <List.Item
50
+ key={migration.id}
51
+ style={{ display: 'flex', gap: 12, justifyContent: 'space-between' }}
52
+ >
53
+ <Flexbox gap={4} horizontal>
54
+ <Flexbox style={{ minWidth: 20 }} width={20}>
55
+ {migration.index}
56
+ </Flexbox>
57
+ <Popover
58
+ content={
59
+ <Flexbox gap={8}>
60
+ <div className={styles.hash}>Hash: {migration.id}</div>
61
+ <Highlighter
62
+ language={'sql'}
63
+ style={{ maxHeight: '30vh', maxWidth: '70vw', overflow: 'scroll' }}
64
+ >
65
+ {migration.sql.join('\n')}
66
+ </Highlighter>
67
+ </Flexbox>
68
+ }
69
+ title={t('clientDB.solve.diagnosis.sql')}
70
+ >
71
+ <Flexbox gap={8}>
72
+ <Flexbox
73
+ className={cx(styles.sql, migration.status === 'success' && styles.time)}
74
+ >
75
+ {migration.desc}
76
+ </Flexbox>
77
+ <Flexbox>
78
+ <Flexbox className={styles.time}>
79
+ {t('clientDB.solve.diagnosis.createdAt')}
80
+ {dayjs(migration.createdAt).format('YYYY-MM-DD hh:mm:ss')} ·{' '}
81
+ {t('clientDB.solve.diagnosis.migratedAt')}{' '}
82
+ {dayjs(migration.migratedAt).format('YYYY-MM-DD hh:mm:ss')}
83
+ </Flexbox>
84
+ </Flexbox>
85
+ </Flexbox>
86
+ </Popover>
87
+ </Flexbox>
88
+ <Tag bordered={false} color={migration.status}>
89
+ {migration.status}
90
+ </Tag>
91
+ </List.Item>
92
+ )}
93
+ size="small"
94
+ />
95
+ </Card>
96
+ );
97
+ };
98
+ export default Diagnosis;
@@ -0,0 +1,220 @@
1
+ 'use client';
2
+
3
+ import { Alert, CodeEditor, Icon } from '@lobehub/ui';
4
+ import { Button, Card, List, Typography } from 'antd';
5
+ import { createStyles } from 'antd-style';
6
+ import dayjs from 'dayjs';
7
+ import isEqual from 'fast-deep-equal';
8
+ import { AlertCircle, CheckCircle, Play } from 'lucide-react';
9
+ import { memo, useState } from 'react';
10
+ import { useTranslation } from 'react-i18next';
11
+ import { Flexbox } from 'react-layout-kit';
12
+
13
+ import { clientDB, updateMigrationRecord } from '@/database/client/db';
14
+ import { useGlobalStore } from '@/store/global';
15
+ import { clientDBSelectors } from '@/store/global/selectors';
16
+
17
+ const { Text } = Typography;
18
+
19
+ // 使用 antd-style 创建样式
20
+ const useStyles = createStyles(({ css, token }) => ({
21
+ actionBar: css`
22
+ display: flex;
23
+ justify-content: flex-end;
24
+ margin-block-start: ${token.marginSM}px;
25
+ `,
26
+ card: css`
27
+ margin-block-end: ${token.marginMD}px;
28
+ border-radius: ${token.borderRadiusLG}px;
29
+ `,
30
+ codeBlock: css`
31
+ overflow-x: auto;
32
+
33
+ margin-block: ${token.marginSM}px;
34
+ margin-inline: 0;
35
+ padding: ${token.paddingSM}px;
36
+ border-radius: ${token.borderRadiusSM}px;
37
+
38
+ font-family: ${token.fontFamilyCode};
39
+ font-size: 12px;
40
+
41
+ background: ${token.colorBgTextHover};
42
+ `,
43
+ container: css`
44
+ overflow-y: auto;
45
+ padding: 0;
46
+ `,
47
+ header: css`
48
+ display: flex;
49
+ align-items: center;
50
+ margin-block-end: ${token.marginMD}px;
51
+ `,
52
+ migrationItem: css`
53
+ display: flex;
54
+ align-items: center;
55
+ justify-content: space-between;
56
+
57
+ padding-block: ${token.paddingSM}px;
58
+ padding-inline: 0;
59
+ border-block-end: none;
60
+ `,
61
+ spotlight: css`
62
+ margin-block-end: ${token.marginLG}px;
63
+ `,
64
+
65
+ templateCard: css`
66
+ margin-block-end: ${token.marginSM}px;
67
+ `,
68
+ }));
69
+
70
+ interface QueryResult {
71
+ message: string;
72
+ success: boolean;
73
+ }
74
+ const Repair = memo(() => {
75
+ const { t } = useTranslation('common');
76
+
77
+ const { styles } = useStyles();
78
+ const [sqlQuery, setSqlQuery] = useState('');
79
+ const [queryResult, setQueryResult] = useState<QueryResult | null>(null);
80
+ const [showSQLHash, setShowSQLHash] = useState('');
81
+
82
+ const errorMigrations = useGlobalStore(clientDBSelectors.errorMigrations, isEqual);
83
+
84
+ const handleExecuteSQL = async () => {
85
+ try {
86
+ // 移除 BEGIN 和 COMMIT
87
+ let processedSQL = sqlQuery.replaceAll(/BEGIN;|COMMIT;/g, '');
88
+
89
+ // 分割 SQL 语句(使用 statement-breakpoint 作为分隔符)
90
+ const migrationQueries = processedSQL.split('--> statement-breakpoint');
91
+
92
+ // 手动管理事务
93
+ await clientDB.execute('BEGIN;');
94
+
95
+ try {
96
+ for (const query of migrationQueries) {
97
+ if (query.trim()) {
98
+ await clientDB.execute(query);
99
+ }
100
+ }
101
+ await clientDB.execute('COMMIT;');
102
+
103
+ setQueryResult({
104
+ message: `SQL executed successfully.`,
105
+ success: true,
106
+ });
107
+ } catch (error) {
108
+ await clientDB.execute('ROLLBACK;');
109
+
110
+ throw error;
111
+ }
112
+ } catch (error) {
113
+ setQueryResult({ message: (error as Error).message, success: false });
114
+ }
115
+ };
116
+
117
+ return (
118
+ <div>
119
+ <Card
120
+ className={styles.card}
121
+ extra={<Text type="secondary">{t('clientDB.solve.repair.desc')}</Text>}
122
+ title={t('clientDB.solve.repair.title')}
123
+ variant={'borderless'}
124
+ >
125
+ <List
126
+ dataSource={errorMigrations}
127
+ renderItem={(migration) => (
128
+ <>
129
+ <List.Item
130
+ actions={[
131
+ <Button
132
+ icon={<Icon icon={Play} />}
133
+ key="retry"
134
+ onClick={() => {
135
+ setShowSQLHash(!!showSQLHash ? '' : migration.hash);
136
+ setSqlQuery(migration.sql.join('--> statement-breakpoint\n'));
137
+ }}
138
+ >
139
+ {t('clientDB.solve.repair.runSQL')}
140
+ </Button>,
141
+ ]}
142
+ className={styles.migrationItem}
143
+ key={migration.hash}
144
+ >
145
+ <List.Item.Meta
146
+ description={
147
+ <Flexbox>
148
+ {t('clientDB.solve.diagnosis.createdAt')}
149
+ {dayjs(migration.folderMillis).format('YYYY-MM-DD hh:mm:ss')} ·
150
+ </Flexbox>
151
+ }
152
+ title={migration.sql[0]}
153
+ />
154
+ </List.Item>
155
+ {showSQLHash === migration.hash && (
156
+ <Card
157
+ className={styles.card}
158
+ extra={<Text type="secondary">{t('clientDB.solve.repair.sql.desc')}</Text>}
159
+ title={t('clientDB.solve.repair.sql.title')}
160
+ >
161
+ <Flexbox gap={16}>
162
+ <CodeEditor
163
+ language={'sql'}
164
+ onValueChange={(e) => setSqlQuery(e)}
165
+ placeholder={t('clientDB.solve.repair.sql.placeholder')}
166
+ style={{ fontFamily: 'monospace', height: 300 }}
167
+ value={sqlQuery}
168
+ />
169
+ <Flexbox horizontal style={{ justifyContent: 'space-between' }}>
170
+ <Button onClick={() => setSqlQuery('')}>
171
+ {t('clientDB.solve.repair.sql.clear')}
172
+ </Button>
173
+ <Button disabled={!sqlQuery.trim()} onClick={handleExecuteSQL} type="primary">
174
+ {t('clientDB.solve.repair.sql.run')}
175
+ </Button>
176
+ </Flexbox>
177
+
178
+ {queryResult && (
179
+ <Alert
180
+ action={
181
+ queryResult.success && (
182
+ <Button
183
+ key="complete"
184
+ onClick={() => updateMigrationRecord(migration.hash)}
185
+ size={'small'}
186
+ type="primary"
187
+ >
188
+ {t('clientDB.solve.repair.sql.markFinished')}
189
+ </Button>
190
+ )
191
+ }
192
+ description={
193
+ <pre style={{ marginTop: '8px', whiteSpace: 'pre-wrap' }}>
194
+ {queryResult.message}
195
+ </pre>
196
+ }
197
+ icon={
198
+ queryResult.success ? (
199
+ <AlertCircle size={16} />
200
+ ) : (
201
+ <CheckCircle size={16} />
202
+ )
203
+ }
204
+ message={t('clientDB.solve.repair.sql.result')}
205
+ showIcon
206
+ type={queryResult.success ? 'success' : 'error'}
207
+ />
208
+ )}
209
+ </Flexbox>
210
+ </Card>
211
+ )}
212
+ </>
213
+ )}
214
+ />
215
+ </Card>
216
+ </div>
217
+ );
218
+ });
219
+
220
+ export default Repair;
@@ -0,0 +1,85 @@
1
+ 'use client';
2
+
3
+ import { Button, Segmented } from 'antd';
4
+ import { createStyles } from 'antd-style';
5
+ import { AlertCircle, ArrowLeft, PenToolIcon as Tool } from 'lucide-react';
6
+ import { memo, useState } from 'react';
7
+ import { useTranslation } from 'react-i18next';
8
+ import { Flexbox } from 'react-layout-kit';
9
+
10
+ import Backup from './Backup';
11
+ import Diagnosis from './Diagnosis';
12
+ import Repair from './Repair';
13
+
14
+ const useStyles = createStyles(({ css, token }) => ({
15
+ backButton: css`
16
+ margin-inline-end: ${token.marginSM}px;
17
+ padding-block: 4px;
18
+ padding-inline: 8px;
19
+ `,
20
+ }));
21
+
22
+ interface DatabaseRepairProps {
23
+ setShowRepair: (value: boolean) => void;
24
+ }
25
+
26
+ const DatabaseRepair = memo<DatabaseRepairProps>(({ setShowRepair }) => {
27
+ const { t } = useTranslation('common');
28
+ const { styles } = useStyles();
29
+ const [activeTab, setActiveTab] = useState('diagnosis');
30
+
31
+ return (
32
+ <Flexbox gap={12}>
33
+ <Flexbox horizontal justify={'space-between'}>
34
+ <Button
35
+ className={styles.backButton}
36
+ icon={<ArrowLeft size={16} />}
37
+ onClick={() => setShowRepair(false)}
38
+ type="text"
39
+ >
40
+ {t('back')}
41
+ </Button>
42
+ <Segmented
43
+ onChange={setActiveTab}
44
+ options={[
45
+ {
46
+ label: (
47
+ <Flexbox align={'center'} horizontal>
48
+ <AlertCircle size={16} style={{ marginRight: 8 }} />
49
+ {t('clientDB.solve.tabs.diagnosis')}
50
+ </Flexbox>
51
+ ),
52
+ value: 'diagnosis',
53
+ },
54
+ {
55
+ label: (
56
+ <Flexbox align={'center'} horizontal>
57
+ <Tool size={16} style={{ marginRight: 8 }} />
58
+ {t('clientDB.solve.tabs.repair')}
59
+ </Flexbox>
60
+ ),
61
+ value: 'repair',
62
+ },
63
+ // {
64
+ // label: (
65
+ // <Flexbox align={'center'} horizontal>
66
+ // <Shield size={16} style={{ marginRight: 8 }} />
67
+ // {t('clientDB.solve.tabs.backup')}
68
+ // </Flexbox>
69
+ // ),
70
+ // value: 'backup',
71
+ // },
72
+ ]}
73
+ value={activeTab}
74
+ />
75
+ <Flexbox width={36} />
76
+ </Flexbox>
77
+
78
+ {activeTab === 'diagnosis' && <Diagnosis />}
79
+ {activeTab === 'repair' && <Repair />}
80
+ {activeTab === 'backup' && <Backup />}
81
+ </Flexbox>
82
+ );
83
+ });
84
+
85
+ export default DatabaseRepair;
@@ -15,6 +15,7 @@ import { useEnabledChatModels } from '@/hooks/useEnabledChatModels';
15
15
  import { useIsMobile } from '@/hooks/useIsMobile';
16
16
  import { useAgentStore } from '@/store/agent';
17
17
  import { agentSelectors } from '@/store/agent/slices/chat';
18
+ import { featureFlagsSelectors, useServerConfigStore } from '@/store/serverConfig';
18
19
  import { EnabledProviderWithModels } from '@/types/aiProvider';
19
20
 
20
21
  const useStyles = createStyles(({ css, prefixCls }) => ({
@@ -49,6 +50,7 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
49
50
  s.updateAgentConfig,
50
51
  ]);
51
52
 
53
+ const { showLLM } = useServerConfigStore(featureFlagsSelectors);
52
54
  const isMobile = useIsMobile();
53
55
 
54
56
  const router = useRouter();
@@ -115,13 +117,17 @@ const ModelSwitchPanel = memo<PropsWithChildren>(({ children }) => {
115
117
  provider={provider.id}
116
118
  source={provider.source}
117
119
  />
118
- <Link href={isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`}>
119
- <ActionIcon
120
- icon={LucideBolt}
121
- size={'small'}
122
- title={t('ModelSwitchPanel.goToSettings')}
123
- />
124
- </Link>
120
+ {showLLM && (
121
+ <Link
122
+ href={isDeprecatedEdition ? '/settings/llm' : `/settings/provider/${provider.id}`}
123
+ >
124
+ <ActionIcon
125
+ icon={LucideBolt}
126
+ size={'small'}
127
+ title={t('ModelSwitchPanel.goToSettings')}
128
+ />
129
+ </Link>
130
+ )}
125
131
  </Flexbox>
126
132
  ),
127
133
  type: 'group',