@lobehub/lobehub 2.0.0-next.291 → 2.0.0-next.293

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 (85) hide show
  1. package/.conductor/setup.sh +107 -0
  2. package/.cursor/rules/linear.mdc +53 -0
  3. package/.github/actions/desktop-build-setup/action.yml +29 -0
  4. package/.github/actions/desktop-upload-artifacts/action.yml +46 -0
  5. package/.github/workflows/release-desktop-beta.yml +76 -115
  6. package/.github/workflows/release-desktop-stable.yml +472 -0
  7. package/CHANGELOG.md +58 -0
  8. package/CLAUDE.md +2 -48
  9. package/apps/desktop/dev-app-update.yml +10 -0
  10. package/apps/desktop/electron-builder.mjs +40 -10
  11. package/apps/desktop/electron.vite.config.ts +3 -2
  12. package/apps/desktop/package.json +2 -1
  13. package/apps/desktop/scripts/update-test/README.md +222 -0
  14. package/apps/desktop/scripts/update-test/dev-app-update.local.yml +18 -0
  15. package/apps/desktop/scripts/update-test/generate-manifest.sh +277 -0
  16. package/apps/desktop/scripts/update-test/run-test.sh +105 -0
  17. package/apps/desktop/scripts/update-test/setup.sh +111 -0
  18. package/apps/desktop/scripts/update-test/start-server.sh +70 -0
  19. package/apps/desktop/scripts/update-test/stop-server.sh +33 -0
  20. package/apps/desktop/src/main/core/infrastructure/UpdaterManager.ts +120 -9
  21. package/apps/desktop/src/main/core/infrastructure/__tests__/UpdaterManager.test.ts +17 -1
  22. package/apps/desktop/src/main/env.ts +19 -11
  23. package/apps/desktop/src/main/modules/updater/configs.ts +14 -1
  24. package/changelog/v1.json +14 -0
  25. package/conductor.json +5 -0
  26. package/locales/en-US/subscription.json +2 -2
  27. package/locales/zh-CN/subscription.json +2 -2
  28. package/package.json +1 -1
  29. package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +16 -14
  30. package/packages/const/src/cacheControl.ts +1 -0
  31. package/packages/electron-client-ipc/src/useWatchBroadcast.ts +10 -4
  32. package/packages/model-bank/src/aiModels/qiniu.ts +6 -6
  33. package/packages/observability-otel/src/node.ts +39 -37
  34. package/scripts/electronWorkflow/mergeMacReleaseFiles.js +22 -8
  35. package/src/app/(backend)/api/desktop/latest/route.ts +115 -0
  36. package/src/app/(backend)/api/version/route.ts +13 -0
  37. package/src/app/(backend)/middleware/validate/createValidator.test.ts +61 -0
  38. package/src/app/(backend)/middleware/validate/createValidator.ts +79 -0
  39. package/src/app/(backend)/middleware/validate/index.ts +3 -0
  40. package/src/app/[variants]/(desktop)/desktop-onboarding/_layout/index.tsx +2 -1
  41. package/src/app/[variants]/(main)/_layout/index.tsx +2 -1
  42. package/src/app/[variants]/(main)/agent/cron/[cronId]/features/CronJobScheduleConfig.tsx +0 -1
  43. package/src/app/[variants]/(main)/agent/cron/[cronId]/index.tsx +5 -5
  44. package/src/app/[variants]/(main)/agent/features/Conversation/ThreadHydration.tsx +3 -1
  45. package/src/app/[variants]/(main)/group/features/Conversation/ThreadHydration.tsx +3 -1
  46. package/src/app/[variants]/(main)/settings/provider/features/ProviderConfig/Checker.tsx +3 -3
  47. package/src/app/[variants]/(mobile)/router/mobileRouter.config.tsx +1 -4
  48. package/src/app/[variants]/router/desktopRouter.config.tsx +1 -4
  49. package/src/components/HtmlPreview/PreviewDrawer.tsx +1 -1
  50. package/src/features/Conversation/Messages/AssistantGroup/components/GroupItem.tsx +12 -2
  51. package/src/features/Conversation/Messages/Tool/Tool/index.tsx +10 -1
  52. package/src/features/{ElectronTitlebar/hooks → Electron/navigation}/useNavigationHistory.ts +1 -1
  53. package/src/features/{ElectronTitlebar/NavigationBar/index.tsx → Electron/titlebar/NavigationBar.tsx} +1 -1
  54. package/src/features/{ElectronTitlebar/NavigationBar → Electron/titlebar}/RecentlyViewed.tsx +1 -1
  55. package/src/features/{ElectronTitlebar/index.tsx → Electron/titlebar/TitleBar.tsx} +19 -9
  56. package/src/features/Electron/titlebar/WinControl.tsx +5 -0
  57. package/src/features/Electron/updater/UpdateModal.tsx +299 -0
  58. package/src/features/LibraryModal/AddFilesToKnowledgeBase/index.test.tsx +24 -0
  59. package/src/features/LibraryModal/AddFilesToKnowledgeBase/index.tsx +21 -24
  60. package/src/features/LibraryModal/CreateNew/index.tsx +18 -22
  61. package/src/features/PluginDevModal/index.tsx +1 -1
  62. package/src/layout/GlobalProvider/AppTheme.tsx +1 -1
  63. package/src/libs/swr/index.ts +26 -30
  64. package/src/server/services/desktopRelease/index.test.ts +65 -0
  65. package/src/server/services/desktopRelease/index.ts +208 -0
  66. package/src/store/aiInfra/slices/aiProvider/action.ts +16 -17
  67. package/src/store/chat/slices/portal/action.test.ts +0 -2
  68. package/src/store/chat/slices/portal/action.ts +17 -44
  69. package/src/store/chat/slices/thread/action.test.ts +4 -1
  70. package/src/store/chat/slices/thread/action.ts +6 -1
  71. package/src/components/FunctionModal/createModalHooks.ts +0 -48
  72. package/src/components/FunctionModal/index.ts +0 -1
  73. package/src/components/FunctionModal/style.tsx +0 -44
  74. package/src/features/ElectronTitlebar/UpdateModal.tsx +0 -274
  75. package/src/features/ElectronTitlebar/WinControl/index.tsx +0 -90
  76. /package/src/features/{ElectronTitlebar/Connection/index.tsx → Electron/connection/Connection.tsx} +0 -0
  77. /package/src/features/{ElectronTitlebar/Connection → Electron/connection}/ConnectionMode.tsx +0 -0
  78. /package/src/features/{ElectronTitlebar/Connection → Electron/connection}/Option.tsx +0 -0
  79. /package/src/features/{ElectronTitlebar/Connection → Electron/connection}/RemoteStatus.tsx +0 -0
  80. /package/src/features/{ElectronTitlebar/Connection → Electron/connection}/Waiting.tsx +0 -0
  81. /package/src/features/{ElectronTitlebar/Connection → Electron/connection}/WaitingAnim.tsx +0 -0
  82. /package/src/features/{ElectronTitlebar/helpers → Electron/navigation}/routeMetadata.ts +0 -0
  83. /package/src/features/{ElectronTitlebar/hooks → Electron/system}/useWatchThemeUpdate.ts +0 -0
  84. /package/src/features/{ElectronTitlebar → Electron/titlebar}/SimpleTitleBar.tsx +0 -0
  85. /package/src/features/{ElectronTitlebar → Electron/updater}/UpdateNotification.tsx +0 -0
@@ -25,10 +25,20 @@ const GroupItem = memo<GroupItemProps>(
25
25
  toggleMessageEditing(item.id, true);
26
26
  }}
27
27
  >
28
- <ContentBlock {...item} assistantId={assistantId} disableEditing={disableEditing} error={error} />
28
+ <ContentBlock
29
+ {...item}
30
+ assistantId={assistantId}
31
+ disableEditing={disableEditing}
32
+ error={error}
33
+ />
29
34
  </Flexbox>
30
35
  ) : (
31
- <ContentBlock {...item} assistantId={assistantId} disableEditing={disableEditing} error={error} />
36
+ <ContentBlock
37
+ {...item}
38
+ assistantId={assistantId}
39
+ disableEditing={disableEditing}
40
+ error={error}
41
+ />
32
42
  );
33
43
  },
34
44
  isEqual,
@@ -33,7 +33,16 @@ export interface InspectorProps {
33
33
  * Tool message component - adapts Tool message data to use AssistantGroup/Tool components
34
34
  */
35
35
  const Tool = memo<InspectorProps>(
36
- ({ arguments: requestArgs, apiName, disableEditing, messageId, toolCallId, index, identifier, type }) => {
36
+ ({
37
+ arguments: requestArgs,
38
+ apiName,
39
+ disableEditing,
40
+ messageId,
41
+ toolCallId,
42
+ index,
43
+ identifier,
44
+ type,
45
+ }) => {
37
46
  const [showDebug, setShowDebug] = useState(false);
38
47
  const [showPluginRender, setShowPluginRender] = useState(false);
39
48
  const [expand, setExpand] = useState(true);
@@ -7,7 +7,7 @@ import { useLocation, useNavigate } from 'react-router-dom';
7
7
 
8
8
  import { useElectronStore } from '@/store/electron';
9
9
 
10
- import { getRouteMetadata } from '../helpers/routeMetadata';
10
+ import { getRouteMetadata } from './routeMetadata';
11
11
 
12
12
  /**
13
13
  * Hook to manage navigation history in Electron desktop app
@@ -10,7 +10,7 @@ import { systemStatusSelectors } from '@/store/global/selectors';
10
10
  import { electronStylish } from '@/styles/electron';
11
11
  import { isMacOS } from '@/utils/platform';
12
12
 
13
- import { useNavigationHistory } from '../hooks/useNavigationHistory';
13
+ import { useNavigationHistory } from '../navigation/useNavigationHistory';
14
14
  import RecentlyViewed from './RecentlyViewed';
15
15
 
16
16
  const isMac = isMacOS();
@@ -9,7 +9,7 @@ import { useNavigate } from 'react-router-dom';
9
9
  import { useElectronStore } from '@/store/electron';
10
10
  import type { HistoryEntry } from '@/store/electron/actions/navigationHistory';
11
11
 
12
- import { getRouteIcon } from '../helpers/routeMetadata';
12
+ import { getRouteIcon } from '../navigation/routeMetadata';
13
13
 
14
14
  const styles = createStaticStyles(({ css, cssVar }) => ({
15
15
  container: css`
@@ -1,18 +1,19 @@
1
1
  import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
2
+ import { useWatchBroadcast } from '@lobechat/electron-client-ipc';
2
3
  import { Flexbox } from '@lobehub/ui';
3
4
  import { Divider } from 'antd';
4
- import { memo, useMemo } from 'react';
5
+ import { memo, useMemo, useRef } from 'react';
5
6
 
6
7
  import { useElectronStore } from '@/store/electron';
7
8
  import { electronStylish } from '@/styles/electron';
8
9
  import { isMacOS } from '@/utils/platform';
9
10
 
10
- import Connection from './Connection';
11
+ import Connection from '../connection/Connection';
12
+ import { useWatchThemeUpdate } from '../system/useWatchThemeUpdate';
13
+ import { useUpdateModal } from '../updater/UpdateModal';
14
+ import { UpdateNotification } from '../updater/UpdateNotification';
11
15
  import NavigationBar from './NavigationBar';
12
- import { UpdateModal } from './UpdateModal';
13
- import { UpdateNotification } from './UpdateNotification';
14
16
  import WinControl from './WinControl';
15
- import { useWatchThemeUpdate } from './hooks/useWatchThemeUpdate';
16
17
 
17
18
  const isMac = isMacOS();
18
19
 
@@ -25,6 +26,19 @@ const TitleBar = memo(() => {
25
26
  initElectronAppState();
26
27
  useWatchThemeUpdate();
27
28
 
29
+ const { open: openUpdateModal } = useUpdateModal();
30
+ const updateModalOpenRef = useRef(false);
31
+
32
+ useWatchBroadcast('manualUpdateCheckStart', () => {
33
+ if (updateModalOpenRef.current) return;
34
+ updateModalOpenRef.current = true;
35
+ openUpdateModal({
36
+ onAfterClose: () => {
37
+ updateModalOpenRef.current = false;
38
+ },
39
+ });
40
+ });
41
+
28
42
  const showWinControl = isAppStateInit && !isMac;
29
43
 
30
44
  const padding = useMemo(() => {
@@ -59,12 +73,8 @@ const TitleBar = memo(() => {
59
73
  </>
60
74
  )}
61
75
  </Flexbox>
62
- <UpdateModal />
63
76
  </Flexbox>
64
77
  );
65
78
  });
66
79
 
67
80
  export default TitleBar;
68
-
69
- export { default as SimpleTitleBar } from './SimpleTitleBar';
70
- export { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
@@ -0,0 +1,5 @@
1
+ const WinControl = () => {
2
+ return <div style={{ width: 132 }} />;
3
+ };
4
+
5
+ export default WinControl;
@@ -0,0 +1,299 @@
1
+ import {
2
+ type ProgressInfo,
3
+ type UpdateInfo,
4
+ useWatchBroadcast,
5
+ } from '@lobechat/electron-client-ipc';
6
+ import { Button, Flexbox, type ModalInstance, createModal } from '@lobehub/ui';
7
+ import { App, Progress, Spin } from 'antd';
8
+ import React, { memo, useCallback, useEffect, useRef, useState } from 'react';
9
+ import { useTranslation } from 'react-i18next';
10
+
11
+ import { autoUpdateService } from '@/services/electron/autoUpdate';
12
+ import { formatSpeed } from '@/utils/format';
13
+
14
+ type UpdateStage = 'checking' | 'available' | 'latest' | 'downloading' | 'downloaded';
15
+
16
+ interface ModalUpdateOptions {
17
+ closable?: boolean;
18
+ keyboard?: boolean;
19
+ maskClosable?: boolean;
20
+ title?: React.ReactNode;
21
+ }
22
+
23
+ interface UpdateModalContentProps {
24
+ onClose: () => void;
25
+ setModalProps: (props: ModalUpdateOptions) => void;
26
+ }
27
+
28
+ const UpdateModalContent = memo<UpdateModalContentProps>(({ onClose, setModalProps }) => {
29
+ const { t } = useTranslation(['electron', 'common']);
30
+ const { modal } = App.useApp();
31
+ const errorHandledRef = useRef(false);
32
+ const isClosingRef = useRef(false);
33
+
34
+ const [stage, setStage] = useState<UpdateStage>('checking');
35
+ const [updateAvailableInfo, setUpdateAvailableInfo] = useState<UpdateInfo | null>(null);
36
+ const [downloadedInfo, setDownloadedInfo] = useState<UpdateInfo | null>(null);
37
+ const [progress, setProgress] = useState<ProgressInfo | null>(null);
38
+ const [latestVersionInfo, setLatestVersionInfo] = useState<UpdateInfo | null>(null);
39
+
40
+ useEffect(() => {
41
+ const isDownloading = stage === 'downloading';
42
+ const modalTitle = (() => {
43
+ switch (stage) {
44
+ case 'checking': {
45
+ return t('updater.checkingUpdate');
46
+ }
47
+ case 'available': {
48
+ return t('updater.newVersionAvailable');
49
+ }
50
+ case 'downloading': {
51
+ return t('updater.downloadingUpdate');
52
+ }
53
+ case 'downloaded': {
54
+ return t('updater.updateReady');
55
+ }
56
+ case 'latest': {
57
+ return t('updater.isLatestVersion');
58
+ }
59
+ default: {
60
+ return '';
61
+ }
62
+ }
63
+ })();
64
+
65
+ setModalProps({
66
+ closable: !isDownloading,
67
+ keyboard: !isDownloading,
68
+ maskClosable: !isDownloading,
69
+ title: modalTitle,
70
+ });
71
+ }, [setModalProps, stage, t]);
72
+
73
+ useWatchBroadcast('manualUpdateAvailable', (info: UpdateInfo) => {
74
+ if (isClosingRef.current) return;
75
+ setStage('available');
76
+ setUpdateAvailableInfo(info);
77
+ setDownloadedInfo(null);
78
+ setLatestVersionInfo(null);
79
+ });
80
+
81
+ useWatchBroadcast('manualUpdateNotAvailable', (info: UpdateInfo) => {
82
+ if (isClosingRef.current) return;
83
+ setStage('latest');
84
+ setLatestVersionInfo(info);
85
+ setUpdateAvailableInfo(null);
86
+ setDownloadedInfo(null);
87
+ setProgress(null);
88
+ });
89
+
90
+ useWatchBroadcast('updateDownloadStart', () => {
91
+ if (isClosingRef.current) return;
92
+ setStage('downloading');
93
+ setProgress({ bytesPerSecond: 0, percent: 0, total: 0, transferred: 0 });
94
+ setUpdateAvailableInfo(null);
95
+ setLatestVersionInfo(null);
96
+ });
97
+
98
+ useWatchBroadcast('updateDownloadProgress', (progressInfo: ProgressInfo) => {
99
+ if (isClosingRef.current) return;
100
+ setProgress(progressInfo);
101
+ });
102
+
103
+ useWatchBroadcast('updateDownloaded', (info: UpdateInfo) => {
104
+ if (isClosingRef.current) return;
105
+ setStage('downloaded');
106
+ setDownloadedInfo(info);
107
+ setProgress(null);
108
+ setUpdateAvailableInfo(null);
109
+ setLatestVersionInfo(null);
110
+ });
111
+
112
+ useWatchBroadcast('updateError', (message: string) => {
113
+ if (isClosingRef.current || errorHandledRef.current) return;
114
+ errorHandledRef.current = true;
115
+ isClosingRef.current = true;
116
+ onClose();
117
+ modal.error({ content: message, title: t('updater.updateError') });
118
+ });
119
+
120
+ const closeModal = () => {
121
+ if (isClosingRef.current) return;
122
+ errorHandledRef.current = true;
123
+ isClosingRef.current = true;
124
+ onClose();
125
+ };
126
+
127
+ const handleDownload = () => {
128
+ if (!updateAvailableInfo) return;
129
+ autoUpdateService.downloadUpdate();
130
+ };
131
+
132
+ const handleInstallNow = () => {
133
+ autoUpdateService.installNow();
134
+ closeModal();
135
+ };
136
+
137
+ const handleInstallLater = () => {
138
+ autoUpdateService.installLater();
139
+ closeModal();
140
+ };
141
+
142
+ const renderReleaseNotes = (notes?: UpdateInfo['releaseNotes']) => {
143
+ if (!notes) return null;
144
+ return (
145
+ <div
146
+ dangerouslySetInnerHTML={{ __html: notes as string }}
147
+ style={{
148
+ borderRadius: 4,
149
+ marginTop: 8,
150
+ maxHeight: 300,
151
+ overflow: 'auto',
152
+ padding: '8px 12px',
153
+ }}
154
+ />
155
+ );
156
+ };
157
+
158
+ const renderBody = () => {
159
+ switch (stage) {
160
+ case 'checking': {
161
+ return (
162
+ <Spin spinning>
163
+ <div style={{ padding: '20px', textAlign: 'center' }}>
164
+ {t('updater.checkingUpdateDesc')}
165
+ </div>
166
+ </Spin>
167
+ );
168
+ }
169
+ case 'available': {
170
+ return (
171
+ <>
172
+ <h4>
173
+ {t('updater.newVersionAvailableDesc', { version: updateAvailableInfo?.version })}
174
+ </h4>
175
+ {renderReleaseNotes(updateAvailableInfo?.releaseNotes)}
176
+ </>
177
+ );
178
+ }
179
+ case 'downloading': {
180
+ const percent = progress ? Math.round(progress.percent) : 0;
181
+ return (
182
+ <div style={{ padding: '20px 0' }}>
183
+ <Progress percent={percent} status="active" />
184
+ <div style={{ fontSize: 12, marginTop: 8, textAlign: 'center' }}>
185
+ {t('updater.downloadingUpdateDesc', { percent })}
186
+ {progress && progress.bytesPerSecond > 0 && (
187
+ <span>{formatSpeed(progress.bytesPerSecond)}</span>
188
+ )}
189
+ </div>
190
+ </div>
191
+ );
192
+ }
193
+ case 'downloaded': {
194
+ return (
195
+ <>
196
+ <h4>{t('updater.updateReadyDesc', { version: downloadedInfo?.version })}</h4>
197
+ {renderReleaseNotes(downloadedInfo?.releaseNotes)}
198
+ </>
199
+ );
200
+ }
201
+ case 'latest': {
202
+ return <p>{t('updater.isLatestVersionDesc', { version: latestVersionInfo?.version })}</p>;
203
+ }
204
+ default: {
205
+ return null;
206
+ }
207
+ }
208
+ };
209
+
210
+ const renderActions = () => {
211
+ if (stage === 'downloading') return null;
212
+
213
+ let actions: React.ReactNode[] = [];
214
+
215
+ if (stage === 'checking') {
216
+ actions = [
217
+ <Button key="cancel" onClick={closeModal}>
218
+ {t('cancel', { ns: 'common' })}
219
+ </Button>,
220
+ ];
221
+ }
222
+
223
+ if (stage === 'available') {
224
+ actions = [
225
+ <Button key="cancel" onClick={closeModal}>
226
+ {t('cancel', { ns: 'common' })}
227
+ </Button>,
228
+ <Button key="download" onClick={handleDownload} type="primary">
229
+ {t('updater.downloadNewVersion')}
230
+ </Button>,
231
+ ];
232
+ }
233
+
234
+ if (stage === 'downloaded') {
235
+ actions = [
236
+ <Button key="later" onClick={handleInstallLater}>
237
+ {t('updater.installLater')}
238
+ </Button>,
239
+ <Button key="now" onClick={handleInstallNow} type="primary">
240
+ {t('updater.restartAndInstall')}
241
+ </Button>,
242
+ ];
243
+ }
244
+
245
+ if (stage === 'latest') {
246
+ actions = [
247
+ <Button key="ok" onClick={closeModal} type="primary">
248
+ {t('ok', { ns: 'common' })}
249
+ </Button>,
250
+ ];
251
+ }
252
+
253
+ if (actions.length === 0) return null;
254
+
255
+ return (
256
+ <Flexbox gap={8} horizontal justify="end">
257
+ {actions}
258
+ </Flexbox>
259
+ );
260
+ };
261
+
262
+ return (
263
+ <Flexbox gap={16} style={{ padding: 16 }}>
264
+ <div>{renderBody()}</div>
265
+ {renderActions()}
266
+ </Flexbox>
267
+ );
268
+ });
269
+
270
+ UpdateModalContent.displayName = 'UpdateModalContent';
271
+
272
+ interface UpdateModalOpenProps {
273
+ onAfterClose?: () => void;
274
+ }
275
+
276
+ export const useUpdateModal = () => {
277
+ const instanceRef = useRef<ModalInstance | null>(null);
278
+
279
+ const open = useCallback((props?: UpdateModalOpenProps) => {
280
+ const setModalProps = (nextProps: ModalUpdateOptions) => {
281
+ instanceRef.current?.update?.(nextProps);
282
+ };
283
+
284
+ const handleClose = () => {
285
+ instanceRef.current?.close();
286
+ };
287
+
288
+ instanceRef.current = createModal({
289
+ afterClose: props?.onAfterClose,
290
+ children: <UpdateModalContent onClose={handleClose} setModalProps={setModalProps} />,
291
+ footer: null,
292
+ keyboard: true,
293
+ maskClosable: true,
294
+ title: '',
295
+ });
296
+ }, []);
297
+
298
+ return { open };
299
+ };
@@ -0,0 +1,24 @@
1
+ import { renderHook } from '@testing-library/react';
2
+ import { describe, expect, it, vi } from 'vitest';
3
+
4
+ import { useAddFilesToKnowledgeBaseModal } from './index';
5
+
6
+ const mockCreateModal = vi.hoisted(() => vi.fn());
7
+
8
+ vi.mock('@lobehub/ui', () => ({
9
+ Flexbox: () => null,
10
+ Icon: () => null,
11
+ createModal: mockCreateModal,
12
+ useModalContext: () => ({ close: vi.fn() }),
13
+ }));
14
+
15
+ describe('useAddFilesToKnowledgeBaseModal', () => {
16
+ it('should forward onClose to createModal afterClose', () => {
17
+ const onClose = vi.fn();
18
+ const { result } = renderHook(() => useAddFilesToKnowledgeBaseModal());
19
+
20
+ result.current.open({ fileIds: ['file-1'], onClose });
21
+
22
+ expect(mockCreateModal).toHaveBeenCalledWith(expect.objectContaining({ afterClose: onClose }));
23
+ });
24
+ });
@@ -1,10 +1,8 @@
1
- import { Flexbox, Icon } from '@lobehub/ui';
1
+ import { Flexbox, Icon, createModal, useModalContext } from '@lobehub/ui';
2
2
  import { BookUp2Icon } from 'lucide-react';
3
- import { Suspense, memo } from 'react';
3
+ import { Suspense, memo, useCallback } from 'react';
4
4
  import { useTranslation } from 'react-i18next';
5
5
 
6
- import { createModal } from '@/components/FunctionModal';
7
-
8
6
  import SelectForm from './SelectForm';
9
7
 
10
8
  interface AddFilesToKnowledgeBaseModalProps {
@@ -16,12 +14,11 @@ interface AddFilesToKnowledgeBaseModalProps {
16
14
  interface ModalContentProps {
17
15
  fileIds: string[];
18
16
  knowledgeBaseId?: string;
19
- onClose?: () => void;
20
17
  }
21
18
 
22
- const ModalContent = memo<ModalContentProps>(({ fileIds, knowledgeBaseId, onClose }) => {
19
+ const ModalContent = memo<ModalContentProps>(({ fileIds, knowledgeBaseId }) => {
23
20
  const { t } = useTranslation('knowledgeBase');
24
-
21
+ const { close } = useModalContext();
25
22
  return (
26
23
  <>
27
24
  <Flexbox gap={8} horizontal paddingBlock={16} paddingInline={16} style={{ paddingBottom: 0 }}>
@@ -29,7 +26,7 @@ const ModalContent = memo<ModalContentProps>(({ fileIds, knowledgeBaseId, onClos
29
26
  {t('addToKnowledgeBase.title')}
30
27
  </Flexbox>
31
28
  <Flexbox padding={16} style={{ paddingTop: 0 }}>
32
- <SelectForm fileIds={fileIds} knowledgeBaseId={knowledgeBaseId} onClose={onClose} />
29
+ <SelectForm fileIds={fileIds} knowledgeBaseId={knowledgeBaseId} onClose={close} />
33
30
  </Flexbox>
34
31
  </>
35
32
  );
@@ -37,19 +34,19 @@ const ModalContent = memo<ModalContentProps>(({ fileIds, knowledgeBaseId, onClos
37
34
 
38
35
  ModalContent.displayName = 'AddFilesToKnowledgeBaseModalContent';
39
36
 
40
- export const useAddFilesToKnowledgeBaseModal = createModal<AddFilesToKnowledgeBaseModalProps>(
41
- (instance, params) => ({
42
- content: (
43
- <Suspense fallback={<div style={{ minHeight: 200 }} />}>
44
- <ModalContent
45
- fileIds={params?.fileIds || []}
46
- knowledgeBaseId={params?.knowledgeBaseId}
47
- onClose={() => {
48
- instance.current?.destroy();
49
- params?.onClose?.();
50
- }}
51
- />
52
- </Suspense>
53
- ),
54
- }),
55
- );
37
+ export const useAddFilesToKnowledgeBaseModal = () => {
38
+ const open = useCallback((params?: AddFilesToKnowledgeBaseModalProps) => {
39
+ createModal({
40
+ afterClose: params?.onClose,
41
+ children: (
42
+ <Suspense fallback={<div style={{ minHeight: 200 }} />}>
43
+ <ModalContent fileIds={params?.fileIds || []} knowledgeBaseId={params?.knowledgeBaseId} />
44
+ </Suspense>
45
+ ),
46
+ footer: null,
47
+ title: null,
48
+ });
49
+ }, []);
50
+
51
+ return { open };
52
+ };
@@ -1,41 +1,37 @@
1
- import { Flexbox } from '@lobehub/ui';
2
- import { Suspense, memo } from 'react';
3
-
4
- import { createModal } from '@/components/FunctionModal';
1
+ import { Flexbox, createModal, useModalContext } from '@lobehub/ui';
2
+ import { Suspense, memo, useCallback } from 'react';
5
3
 
6
4
  import CreateForm from './CreateForm';
7
5
 
8
6
  interface ModalContentProps {
9
- onClose?: () => void;
10
7
  onSuccess?: (id: string) => void;
11
8
  }
12
9
 
13
- const ModalContent = memo<ModalContentProps>(({ onClose, onSuccess }) => {
10
+ const ModalContent = memo<ModalContentProps>(({ onSuccess }) => {
11
+ const { close } = useModalContext();
12
+
14
13
  return (
15
14
  <Flexbox paddingInline={16} style={{ paddingBottom: 16 }}>
16
- <CreateForm onClose={onClose} onSuccess={onSuccess} />
15
+ <CreateForm onClose={close} onSuccess={onSuccess} />
17
16
  </Flexbox>
18
17
  );
19
18
  });
20
19
 
21
20
  ModalContent.displayName = 'KnowledgeBaseCreateModalContent';
22
21
 
23
- // eslint-disable-next-line unused-imports/no-unused-vars
24
- export const useCreateNewModal = createModal<{ onSuccess?: (id: string) => void }>(
25
- (instance, props) => {
26
- return {
27
- content: (
22
+ export const useCreateNewModal = () => {
23
+ const open = useCallback((props?: { onSuccess?: (id: string) => void }) => {
24
+ createModal({
25
+ children: (
28
26
  <Suspense fallback={<div style={{ minHeight: 200 }} />}>
29
- <ModalContent
30
- onClose={() => {
31
- instance.current?.destroy();
32
- }}
33
- onSuccess={props?.onSuccess}
34
- />
27
+ <ModalContent onSuccess={props?.onSuccess} />
35
28
  </Suspense>
36
29
  ),
37
30
  focusTriggerAfterClose: true,
38
- footer: false,
39
- };
40
- },
41
- );
31
+ footer: null,
32
+ title: null,
33
+ });
34
+ }, []);
35
+
36
+ return { open };
37
+ };
@@ -1,3 +1,4 @@
1
+ import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
1
2
  import { Alert, Button, Drawer, Flexbox, Icon, Segmented, Tag } from '@lobehub/ui';
2
3
  import { App, Form, Popconfirm } from 'antd';
3
4
  import { useResponsive } from 'antd-style';
@@ -7,7 +8,6 @@ import { Trans, useTranslation } from 'react-i18next';
7
8
 
8
9
  import { WIKI_PLUGIN_GUIDE } from '@/const/url';
9
10
  import { isDesktop } from '@/const/version';
10
- import { TITLE_BAR_HEIGHT } from '@/features/ElectronTitlebar';
11
11
  import { type LobeToolCustomPlugin } from '@/types/tool/plugin';
12
12
 
13
13
  import MCPManifestForm from './MCPManifestForm';
@@ -1,5 +1,6 @@
1
1
  'use client';
2
2
 
3
+ import { TITLE_BAR_HEIGHT } from '@lobechat/desktop-bridge';
3
4
  import {
4
5
  ConfigProvider,
5
6
  FontLoader,
@@ -19,7 +20,6 @@ import { type ReactNode, memo, useEffect, useMemo, useState } from 'react';
19
20
  import AntdStaticMethods from '@/components/AntdStaticMethods';
20
21
  import { LOBE_THEME_NEUTRAL_COLOR, LOBE_THEME_PRIMARY_COLOR } from '@/const/theme';
21
22
  import { isDesktop } from '@/const/version';
22
- import { TITLE_BAR_HEIGHT } from '@/features/ElectronTitlebar';
23
23
  import { useIsDark } from '@/hooks/useIsDark';
24
24
  import { getUILocaleAndResources } from '@/libs/getUILocaleAndResources';
25
25
  import { useGlobalStore } from '@/store/global';