@lobehub/lobehub 2.0.0-next.255 → 2.0.0-next.256
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.
- package/CHANGELOG.md +25 -0
- package/changelog/v1.json +9 -0
- package/locales/en-US/plugin.json +1 -0
- package/locales/zh-CN/plugin.json +1 -0
- package/package.json +1 -1
- package/packages/builtin-tool-notebook/src/client/Placeholder/CreateDocument.tsx +6 -6
- package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +23 -10
- package/packages/builtin-tool-notebook/src/client/Streaming/CreateDocument/index.tsx +1 -1
- package/packages/database/src/models/__tests__/agent.test.ts +91 -4
- package/packages/database/src/models/agent.ts +15 -7
- package/packages/editor-runtime/src/EditorRuntime.ts +1 -1
- package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +65 -4
- package/packages/editor-runtime/src/__tests__/__snapshots__/EditorRuntime.real.test.ts.snap +108 -17
- package/packages/editor-runtime/src/__tests__/fixtures/remove-then-add.json +636 -0
- package/packages/editor-runtime/src/__tests__/fixtures/remove.json +1 -0
- package/packages/types/src/agent/agentConfig.ts +8 -8
- package/src/app/[variants]/(main)/chat/features/Portal/_layout/Mobile.tsx +2 -1
- package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +2 -1
- package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +2 -2
- package/src/app/[variants]/(main)/page/{features/PageTitle → PageTitle}/index.tsx +0 -1
- package/src/app/[variants]/(main)/page/[id]/index.tsx +43 -1
- package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/Content.tsx +15 -15
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Editing.tsx +3 -3
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/index.tsx +7 -12
- package/src/app/[variants]/(main)/page/_layout/Body/List/Item/useDropdownMenu.tsx +5 -5
- package/src/app/[variants]/(main)/page/_layout/Body/List/index.tsx +7 -7
- package/src/app/[variants]/(main)/page/_layout/Body/index.tsx +15 -9
- package/src/app/[variants]/(main)/page/_layout/Body/useDropdownMenu.tsx +3 -3
- package/src/app/[variants]/(main)/page/_layout/DataSync.tsx +15 -0
- package/src/app/[variants]/(main)/page/_layout/Header/AddButton.tsx +2 -2
- package/src/app/[variants]/(main)/page/_layout/index.tsx +2 -0
- package/src/app/[variants]/(main)/page/index.tsx +3 -7
- package/src/components/Editor/AutoSaveHint.tsx +1 -1
- package/src/features/Conversation/Messages/User/Actions/index.tsx +5 -1
- package/src/features/EditorCanvas/AutoSaveHint.tsx +37 -0
- package/src/features/{PageEditor → EditorCanvas}/DiffAllToolbar.tsx +57 -16
- package/src/features/EditorCanvas/DocumentIdMode.tsx +111 -0
- package/src/features/EditorCanvas/EditorCanvas.tsx +148 -0
- package/src/features/EditorCanvas/EditorDataMode.tsx +64 -0
- package/src/features/EditorCanvas/ErrorBoundary.tsx +66 -0
- package/src/features/EditorCanvas/InlineToolbar.tsx +245 -0
- package/src/features/EditorCanvas/InternalEditor.tsx +134 -0
- package/src/features/{PageEditor/EditorCanvas → EditorCanvas}/actions.ts +10 -8
- package/src/features/EditorCanvas/index.ts +9 -0
- package/src/features/PageEditor/EditorCanvas/index.tsx +14 -111
- package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +95 -0
- package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +1 -1
- package/src/features/PageEditor/Header/Breadcrumb.tsx +2 -2
- package/src/features/PageEditor/Header/index.tsx +15 -18
- package/src/features/PageEditor/Header/useMenu.tsx +12 -9
- package/src/features/PageEditor/PageEditor.tsx +45 -21
- package/src/features/PageEditor/PageEditorProvider.tsx +13 -1
- package/src/features/PageEditor/PageTitle/index.tsx +2 -2
- package/src/features/PageEditor/StoreUpdater.tsx +35 -308
- package/src/features/PageEditor/{Body/Title.tsx → TitleSection.tsx} +16 -16
- package/src/features/PageEditor/store/action.ts +96 -188
- package/src/features/PageEditor/store/initialState.ts +16 -21
- package/src/features/PageEditor/store/selectors.ts +3 -4
- package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +22 -14
- package/src/features/PageExplorer/index.tsx +34 -67
- package/src/features/Portal/Artifacts/index.ts +0 -2
- package/src/features/Portal/Document/AutoSaveHint.tsx +7 -6
- package/src/features/Portal/Document/Body.tsx +1 -3
- package/src/features/Portal/Document/EditorCanvas.tsx +7 -50
- package/src/features/Portal/Document/Header.tsx +13 -10
- package/src/features/Portal/Document/TodoList.tsx +6 -4
- package/src/features/Portal/Document/Wrapper.tsx +3 -11
- package/src/features/Portal/Document/index.ts +0 -2
- package/src/features/Portal/FilePreview/index.ts +0 -2
- package/src/features/Portal/GroupThread/index.ts +0 -3
- package/src/features/Portal/MessageDetail/index.ts +0 -2
- package/src/features/Portal/Notebook/index.ts +0 -2
- package/src/features/Portal/Plugins/index.ts +0 -2
- package/src/features/Portal/Thread/index.ts +0 -3
- package/src/features/Portal/components/Header.tsx +18 -6
- package/src/features/Portal/router.tsx +34 -97
- package/src/features/Portal/type.ts +0 -2
- package/src/locales/default/plugin.ts +1 -0
- package/src/store/chat/slices/portal/action.test.ts +218 -15
- package/src/store/chat/slices/portal/action.ts +194 -41
- package/src/store/chat/slices/portal/initialState.ts +40 -1
- package/src/store/chat/slices/portal/selectors/thread.ts +44 -3
- package/src/store/chat/slices/portal/selectors.test.ts +119 -17
- package/src/store/chat/slices/portal/selectors.ts +117 -36
- package/src/store/document/index.ts +17 -5
- package/src/store/document/slices/document/action.ts +209 -0
- package/src/store/document/slices/document/index.ts +6 -0
- package/src/store/document/slices/editor/action.test.ts +340 -0
- package/src/store/document/slices/editor/action.ts +133 -149
- package/src/store/document/slices/editor/index.ts +9 -2
- package/src/store/document/slices/editor/initialState.ts +66 -29
- package/src/store/document/slices/editor/reducer.test.ts +217 -0
- package/src/store/document/slices/editor/reducer.ts +67 -0
- package/src/store/document/slices/editor/selectors.test.ts +395 -0
- package/src/store/document/slices/editor/selectors.ts +107 -5
- package/src/store/document/store.ts +12 -13
- package/src/store/file/slices/document/action.ts +19 -188
- package/src/store/file/slices/document/initialState.ts +0 -30
- package/src/store/file/slices/document/selectors.ts +25 -59
- package/src/store/notebook/index.ts +5 -4
- package/src/store/page/index.ts +2 -0
- package/src/store/page/initialState.ts +92 -0
- package/src/store/page/selectors.ts +5 -0
- package/src/store/page/slices/crud/action.ts +477 -0
- package/src/store/page/slices/crud/index.ts +2 -0
- package/src/store/page/slices/crud/initialState.ts +7 -0
- package/src/store/page/slices/internal/action.ts +32 -0
- package/src/store/page/slices/internal/index.ts +2 -0
- package/src/store/page/slices/internal/reducer.ts +105 -0
- package/src/store/page/slices/list/action.ts +206 -0
- package/src/store/page/slices/list/index.ts +3 -0
- package/src/store/page/slices/list/initialState.ts +29 -0
- package/src/store/page/slices/list/selectors.ts +90 -0
- package/src/store/page/slices/selection/action.ts +67 -0
- package/src/store/page/slices/selection/index.ts +2 -0
- package/src/store/page/slices/selection/initialState.ts +11 -0
- package/src/store/page/store.ts +29 -0
- package/src/store/tool/slices/lobehubSkillStore/selectors.ts +10 -20
- package/src/utils/identifier.ts +8 -2
- package/src/features/PageEditor/Body/index.tsx +0 -68
- package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +0 -316
- package/src/features/PageEditor/Header/AutoSaveHint.tsx +0 -27
- package/src/features/Portal/Artifacts/useEnable.ts +0 -4
- package/src/features/Portal/Document/DocumentEditorProvider.tsx +0 -34
- package/src/features/Portal/Document/StoreUpdater.tsx +0 -80
- package/src/features/Portal/Document/Title.tsx +0 -54
- package/src/features/Portal/Document/store/action.ts +0 -114
- package/src/features/Portal/Document/store/index.ts +0 -21
- package/src/features/Portal/Document/store/initialState.ts +0 -24
- package/src/features/Portal/Document/useEnable.ts +0 -8
- package/src/features/Portal/FilePreview/useEnable.ts +0 -6
- package/src/features/Portal/GroupThread/hook.ts +0 -9
- package/src/features/Portal/MessageDetail/useEnable.ts +0 -4
- package/src/features/Portal/Notebook/useEnable.ts +0 -6
- package/src/features/Portal/Plugins/useEnable.ts +0 -6
- package/src/features/Portal/Thread/hook.ts +0 -8
- package/src/store/document/slices/notebook/action.ts +0 -119
- package/src/store/document/slices/notebook/index.ts +0 -3
- package/src/store/document/slices/notebook/initialState.ts +0 -12
- package/src/store/document/slices/notebook/selectors.ts +0 -26
|
@@ -6,7 +6,6 @@ import { useClientDataSWRWithSync } from '@/libs/swr';
|
|
|
6
6
|
import { documentService } from '@/services/document';
|
|
7
7
|
import { useGlobalStore } from '@/store/global';
|
|
8
8
|
import { DocumentSourceType, type LobeDocument } from '@/types/document';
|
|
9
|
-
import { standardizeIdentifier } from '@/utils/identifier';
|
|
10
9
|
import { setNamespace } from '@/utils/storeDebug';
|
|
11
10
|
|
|
12
11
|
import { type FileStore } from '../../store';
|
|
@@ -18,13 +17,6 @@ const ALLOWED_DOCUMENT_SOURCE_TYPES = new Set(['editor', 'file', 'api']);
|
|
|
18
17
|
const ALLOWED_DOCUMENT_FILE_TYPES = new Set(['custom/document', 'application/pdf']);
|
|
19
18
|
const EDITOR_DOCUMENT_FILE_TYPE = 'custom/document';
|
|
20
19
|
|
|
21
|
-
const updateUrl = (docId: string | null) => {
|
|
22
|
-
const dryPageId = standardizeIdentifier(docId ?? '') ?? '';
|
|
23
|
-
|
|
24
|
-
const newPath = dryPageId ? `/page/${dryPageId}` : '/page';
|
|
25
|
-
window.history.replaceState({}, '', newPath);
|
|
26
|
-
};
|
|
27
|
-
|
|
28
20
|
/**
|
|
29
21
|
* Check if a page should be displayed in the page list
|
|
30
22
|
*/
|
|
@@ -36,7 +28,6 @@ const isAllowedDocument = (page: { fileType: string; sourceType: string }) => {
|
|
|
36
28
|
};
|
|
37
29
|
|
|
38
30
|
export interface DocumentAction {
|
|
39
|
-
closeAllPagesDrawer: () => void;
|
|
40
31
|
/**
|
|
41
32
|
* Create a new document with markdown content (not optimistic, waits for server response)
|
|
42
33
|
* Returns the created document
|
|
@@ -52,20 +43,11 @@ export interface DocumentAction {
|
|
|
52
43
|
* Returns the created folder's ID
|
|
53
44
|
*/
|
|
54
45
|
createFolder: (name: string, parentId?: string, knowledgeBaseId?: string) => Promise<string>;
|
|
55
|
-
/**
|
|
56
|
-
* Create a new page with optimistic update (for page explorer)
|
|
57
|
-
* Returns the ID of the created page
|
|
58
|
-
*/
|
|
59
|
-
createNewPage: (title: string) => Promise<string>;
|
|
60
46
|
/**
|
|
61
47
|
* Create a new optimistic document immediately in local map
|
|
62
48
|
* Returns the temporary ID for the new document
|
|
63
49
|
*/
|
|
64
50
|
createOptimisticDocument: (title?: string) => string;
|
|
65
|
-
/**
|
|
66
|
-
* Delete a page and update selection if needed
|
|
67
|
-
*/
|
|
68
|
-
deletePage: (documentId: string) => Promise<void>;
|
|
69
51
|
/**
|
|
70
52
|
* Duplicate an existing document
|
|
71
53
|
* Returns the created document
|
|
@@ -87,7 +69,6 @@ export interface DocumentAction {
|
|
|
87
69
|
* Load more documents (next page)
|
|
88
70
|
*/
|
|
89
71
|
loadMoreDocuments: () => Promise<void>;
|
|
90
|
-
openAllPagesDrawer: () => void;
|
|
91
72
|
/**
|
|
92
73
|
* Remove a document (deletes from documents table)
|
|
93
74
|
*/
|
|
@@ -96,34 +77,10 @@ export interface DocumentAction {
|
|
|
96
77
|
* Remove a temp document from local map
|
|
97
78
|
*/
|
|
98
79
|
removeTempDocument: (tempId: string) => void;
|
|
99
|
-
/**
|
|
100
|
-
* Rename a page
|
|
101
|
-
*/
|
|
102
|
-
renamePage: (documentId: string, title: string, emoji?: string) => Promise<void>;
|
|
103
80
|
/**
|
|
104
81
|
* Replace a temp document with real document data (for smooth UX when creating documents)
|
|
105
82
|
*/
|
|
106
83
|
replaceTempDocumentWithReal: (tempId: string, realDocument: LobeDocument) => void;
|
|
107
|
-
/**
|
|
108
|
-
* Select or deselect a page
|
|
109
|
-
*/
|
|
110
|
-
selectPage: (documentId: string) => void;
|
|
111
|
-
/**
|
|
112
|
-
* Set the ID of the page being renamed
|
|
113
|
-
*/
|
|
114
|
-
setRenamingPageId: (pageId: string | null) => void;
|
|
115
|
-
/**
|
|
116
|
-
* Set search keywords
|
|
117
|
-
*/
|
|
118
|
-
setSearchKeywords: (keywords: string) => void;
|
|
119
|
-
/**
|
|
120
|
-
* Set selected page ID (used for external navigation)
|
|
121
|
-
*/
|
|
122
|
-
setSelectedPageId: (pageId: string | null, updateHistory?: boolean) => void;
|
|
123
|
-
/**
|
|
124
|
-
* Toggle filter to show only pages not in any library
|
|
125
|
-
*/
|
|
126
|
-
setShowOnlyPagesNotInLibrary: (show: boolean) => void;
|
|
127
84
|
/**
|
|
128
85
|
* Update document directly (no optimistic update)
|
|
129
86
|
*/
|
|
@@ -147,10 +104,6 @@ export const createDocumentSlice: StateCreator<
|
|
|
147
104
|
[],
|
|
148
105
|
DocumentAction
|
|
149
106
|
> = (set, get) => ({
|
|
150
|
-
closeAllPagesDrawer: () => {
|
|
151
|
-
set({ allPagesDrawerOpen: false }, false, n('closeAllPagesDrawer'));
|
|
152
|
-
},
|
|
153
|
-
|
|
154
107
|
createDocument: async ({ title, content, knowledgeBaseId, parentId }) => {
|
|
155
108
|
const now = Date.now();
|
|
156
109
|
|
|
@@ -201,58 +154,6 @@ export const createDocumentSlice: StateCreator<
|
|
|
201
154
|
return folder.id;
|
|
202
155
|
},
|
|
203
156
|
|
|
204
|
-
// Page explorer actions
|
|
205
|
-
createNewPage: async (title: string) => {
|
|
206
|
-
const { createOptimisticDocument, createDocument, replaceTempDocumentWithReal } = get();
|
|
207
|
-
|
|
208
|
-
// Create optimistic page immediately
|
|
209
|
-
const tempPageId = createOptimisticDocument(title);
|
|
210
|
-
set({ isCreatingNew: true, selectedPageId: tempPageId }, false, n('createNewPage/start'));
|
|
211
|
-
|
|
212
|
-
try {
|
|
213
|
-
// Create real page
|
|
214
|
-
const newPage = await createDocument({
|
|
215
|
-
content: '',
|
|
216
|
-
title,
|
|
217
|
-
});
|
|
218
|
-
|
|
219
|
-
// Convert to LobeDocument
|
|
220
|
-
const realPage: LobeDocument = {
|
|
221
|
-
content: newPage.content || '',
|
|
222
|
-
createdAt: newPage.createdAt ? new Date(newPage.createdAt) : new Date(),
|
|
223
|
-
editorData:
|
|
224
|
-
typeof newPage.editorData === 'string'
|
|
225
|
-
? JSON.parse(newPage.editorData)
|
|
226
|
-
: newPage.editorData || null,
|
|
227
|
-
fileType: 'custom/document',
|
|
228
|
-
filename: newPage.title || title,
|
|
229
|
-
id: newPage.id,
|
|
230
|
-
metadata: newPage.metadata || {},
|
|
231
|
-
source: 'document',
|
|
232
|
-
sourceType: DocumentSourceType.EDITOR,
|
|
233
|
-
title: newPage.title || title,
|
|
234
|
-
totalCharCount: newPage.content?.length || 0,
|
|
235
|
-
totalLineCount: 0,
|
|
236
|
-
updatedAt: newPage.updatedAt ? new Date(newPage.updatedAt) : new Date(),
|
|
237
|
-
};
|
|
238
|
-
|
|
239
|
-
// Replace optimistic with real
|
|
240
|
-
replaceTempDocumentWithReal(tempPageId, realPage);
|
|
241
|
-
set({ isCreatingNew: false, selectedPageId: newPage.id }, false, n('createNewPage/success'));
|
|
242
|
-
|
|
243
|
-
// Update URL to navigate to the new page
|
|
244
|
-
updateUrl(newPage.id);
|
|
245
|
-
|
|
246
|
-
return newPage.id;
|
|
247
|
-
} catch (error) {
|
|
248
|
-
console.error('Failed to create page:', error);
|
|
249
|
-
get().removeTempDocument(tempPageId);
|
|
250
|
-
set({ isCreatingNew: false, selectedPageId: null }, false, n('createNewPage/error'));
|
|
251
|
-
updateUrl(null);
|
|
252
|
-
throw error;
|
|
253
|
-
}
|
|
254
|
-
},
|
|
255
|
-
|
|
256
157
|
createOptimisticDocument: (title = 'Untitled') => {
|
|
257
158
|
const { localDocumentMap } = get();
|
|
258
159
|
|
|
@@ -284,15 +185,6 @@ export const createDocumentSlice: StateCreator<
|
|
|
284
185
|
return tempId;
|
|
285
186
|
},
|
|
286
187
|
|
|
287
|
-
deletePage: async (documentId: string) => {
|
|
288
|
-
const { selectedPageId } = get();
|
|
289
|
-
|
|
290
|
-
if (selectedPageId === documentId) {
|
|
291
|
-
set({ isCreatingNew: false, selectedPageId: null }, false, n('deletePage'));
|
|
292
|
-
window.history.replaceState({}, '', '/page');
|
|
293
|
-
}
|
|
294
|
-
},
|
|
295
|
-
|
|
296
188
|
duplicateDocument: async (documentId) => {
|
|
297
189
|
// Fetch the source page
|
|
298
190
|
const sourcePage = await documentService.getDocumentById(documentId);
|
|
@@ -512,48 +404,37 @@ export const createDocumentSlice: StateCreator<
|
|
|
512
404
|
}
|
|
513
405
|
},
|
|
514
406
|
|
|
515
|
-
openAllPagesDrawer: () => {
|
|
516
|
-
set({ allPagesDrawerOpen: true }, false, n('openAllPagesDrawer'));
|
|
517
|
-
},
|
|
518
|
-
|
|
519
407
|
removeDocument: async (documentId) => {
|
|
520
408
|
// Remove from local optimistic map first (optimistic update)
|
|
521
|
-
const { localDocumentMap, documents
|
|
409
|
+
const { localDocumentMap, documents } = get();
|
|
522
410
|
const newMap = new Map(localDocumentMap);
|
|
523
411
|
newMap.delete(documentId);
|
|
524
412
|
|
|
525
|
-
// Also remove from
|
|
526
|
-
const
|
|
413
|
+
// Also remove from documents array to update the list immediately
|
|
414
|
+
const newDocuments = documents.filter((doc) => doc.id !== documentId);
|
|
527
415
|
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
if (selectedPageId === documentId) {
|
|
534
|
-
updates.selectedPageId = null;
|
|
535
|
-
updateUrl(null);
|
|
536
|
-
}
|
|
537
|
-
|
|
538
|
-
set(updates, false, n('removeDocument/optimistic'));
|
|
416
|
+
set(
|
|
417
|
+
{ documents: newDocuments, localDocumentMap: newMap },
|
|
418
|
+
false,
|
|
419
|
+
n('removeDocument/optimistic'),
|
|
420
|
+
);
|
|
539
421
|
|
|
540
422
|
try {
|
|
541
|
-
// Delete from
|
|
423
|
+
// Delete from documents table
|
|
542
424
|
await documentService.deleteDocument(documentId);
|
|
543
425
|
// No need to call fetchDocuments() - optimistic update is enough
|
|
544
426
|
} catch (error) {
|
|
545
|
-
console.error('Failed to delete
|
|
546
|
-
// Restore the
|
|
427
|
+
console.error('Failed to delete document:', error);
|
|
428
|
+
// Restore the document in local map and documents array on error
|
|
547
429
|
const restoredMap = new Map(localDocumentMap);
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
set(restoreUpdates, false, n('removeDocument/restore'));
|
|
430
|
+
set(
|
|
431
|
+
{
|
|
432
|
+
documents,
|
|
433
|
+
localDocumentMap: restoredMap,
|
|
434
|
+
},
|
|
435
|
+
false,
|
|
436
|
+
n('removeDocument/restore'),
|
|
437
|
+
);
|
|
557
438
|
throw error;
|
|
558
439
|
}
|
|
559
440
|
},
|
|
@@ -565,21 +446,6 @@ export const createDocumentSlice: StateCreator<
|
|
|
565
446
|
set({ localDocumentMap: newMap }, false, n('removeTempDocument'));
|
|
566
447
|
},
|
|
567
448
|
|
|
568
|
-
renamePage: async (documentId: string, title: string, emoji?: string) => {
|
|
569
|
-
const { updateDocumentOptimistically } = get();
|
|
570
|
-
|
|
571
|
-
try {
|
|
572
|
-
await updateDocumentOptimistically(documentId, {
|
|
573
|
-
metadata: { emoji },
|
|
574
|
-
title,
|
|
575
|
-
});
|
|
576
|
-
} catch (error) {
|
|
577
|
-
console.error('Failed to rename page:', error);
|
|
578
|
-
} finally {
|
|
579
|
-
set({ renamingPageId: null }, false, n('renamePage'));
|
|
580
|
-
}
|
|
581
|
-
},
|
|
582
|
-
|
|
583
449
|
replaceTempDocumentWithReal: (tempId, realPage) => {
|
|
584
450
|
const { localDocumentMap } = get();
|
|
585
451
|
const newMap = new Map(localDocumentMap);
|
|
@@ -593,41 +459,6 @@ export const createDocumentSlice: StateCreator<
|
|
|
593
459
|
set({ localDocumentMap: newMap }, false, n('replaceTempDocumentWithReal'));
|
|
594
460
|
},
|
|
595
461
|
|
|
596
|
-
selectPage: (documentId: string) => {
|
|
597
|
-
const { selectedPageId } = get();
|
|
598
|
-
|
|
599
|
-
if (selectedPageId === documentId) {
|
|
600
|
-
// Don't allow deselecting the current page, required from @canisminor
|
|
601
|
-
//
|
|
602
|
-
// set({ isCreatingNew: false, selectedPageId: null }, false, n('selectPage/deselect'));
|
|
603
|
-
// updateUrl(null);
|
|
604
|
-
} else {
|
|
605
|
-
// Select
|
|
606
|
-
set({ isCreatingNew: false, selectedPageId: documentId }, false, n('selectPage/select'));
|
|
607
|
-
updateUrl(documentId);
|
|
608
|
-
}
|
|
609
|
-
},
|
|
610
|
-
|
|
611
|
-
setRenamingPageId: (pageId: string | null) => {
|
|
612
|
-
set({ renamingPageId: pageId }, false, n('setRenamingPageId'));
|
|
613
|
-
},
|
|
614
|
-
|
|
615
|
-
setSearchKeywords: (keywords: string) => {
|
|
616
|
-
set({ searchKeywords: keywords }, false, n('setSearchKeywords'));
|
|
617
|
-
},
|
|
618
|
-
|
|
619
|
-
setSelectedPageId: (pageId: string | null, updateHistory = true) => {
|
|
620
|
-
set({ selectedPageId: pageId }, false, n('setSelectedPageId'));
|
|
621
|
-
if (updateHistory) {
|
|
622
|
-
const newPath = pageId ? `/page/${pageId}` : '/page';
|
|
623
|
-
window.history.replaceState({}, '', newPath);
|
|
624
|
-
}
|
|
625
|
-
},
|
|
626
|
-
|
|
627
|
-
setShowOnlyPagesNotInLibrary: (show: boolean) => {
|
|
628
|
-
set({ showOnlyPagesNotInLibrary: show }, false, n('setShowOnlyPagesNotInLibrary'));
|
|
629
|
-
},
|
|
630
|
-
|
|
631
462
|
updateDocument: async (id, updates) => {
|
|
632
463
|
await documentService.updateDocument({
|
|
633
464
|
content: updates.content ?? undefined,
|
|
@@ -6,10 +6,6 @@ export interface DocumentQueryFilter {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export interface DocumentState {
|
|
9
|
-
/**
|
|
10
|
-
* whether all pages drawer is open
|
|
11
|
-
*/
|
|
12
|
-
allPagesDrawerOpen: boolean;
|
|
13
9
|
/**
|
|
14
10
|
* current page number (0-based)
|
|
15
11
|
*/
|
|
@@ -30,10 +26,6 @@ export interface DocumentState {
|
|
|
30
26
|
* whether there are more documents to load
|
|
31
27
|
*/
|
|
32
28
|
hasMoreDocuments: boolean;
|
|
33
|
-
/**
|
|
34
|
-
* Whether currently creating a new document
|
|
35
|
-
*/
|
|
36
|
-
isCreatingNew: boolean;
|
|
37
29
|
/**
|
|
38
30
|
* Loading state for document fetching
|
|
39
31
|
*/
|
|
@@ -46,37 +38,15 @@ export interface DocumentState {
|
|
|
46
38
|
* Local optimistic document map for immediate UI updates
|
|
47
39
|
*/
|
|
48
40
|
localDocumentMap: Map<string, LobeDocument>;
|
|
49
|
-
/**
|
|
50
|
-
* ID of the page being renamed (null if none)
|
|
51
|
-
*/
|
|
52
|
-
renamingPageId: string | null;
|
|
53
|
-
/**
|
|
54
|
-
* Search keywords for filtering pages
|
|
55
|
-
*/
|
|
56
|
-
searchKeywords: string;
|
|
57
|
-
/**
|
|
58
|
-
* Currently selected page ID
|
|
59
|
-
*/
|
|
60
|
-
selectedPageId: string | null;
|
|
61
|
-
/**
|
|
62
|
-
* Filter to show only pages not in any library (false = show all pages)
|
|
63
|
-
*/
|
|
64
|
-
showOnlyPagesNotInLibrary: boolean;
|
|
65
41
|
}
|
|
66
42
|
|
|
67
43
|
export const initialDocumentState: DocumentState = {
|
|
68
|
-
allPagesDrawerOpen: false,
|
|
69
44
|
currentPage: 0,
|
|
70
45
|
documentQueryFilter: undefined,
|
|
71
46
|
documents: [],
|
|
72
47
|
documentsTotal: 0,
|
|
73
48
|
hasMoreDocuments: false,
|
|
74
|
-
isCreatingNew: false,
|
|
75
49
|
isDocumentListLoading: false,
|
|
76
50
|
isLoadingMoreDocuments: false,
|
|
77
51
|
localDocumentMap: new Map(),
|
|
78
|
-
renamingPageId: null,
|
|
79
|
-
searchKeywords: '',
|
|
80
|
-
selectedPageId: null,
|
|
81
|
-
showOnlyPagesNotInLibrary: false,
|
|
82
52
|
};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { useGlobalStore } from '@/store/global';
|
|
2
1
|
import { type LobeDocument } from '@/types/document';
|
|
3
2
|
|
|
4
3
|
import { type FilesStoreState } from '../../initialState';
|
|
@@ -23,51 +22,33 @@ const getDocumentById = (documentId: string | undefined) => (s: FilesStoreState)
|
|
|
23
22
|
return localDocument || serverDocument;
|
|
24
23
|
};
|
|
25
24
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
return !page.metadata?.knowledgeBaseId;
|
|
45
|
-
});
|
|
46
|
-
}
|
|
25
|
+
/**
|
|
26
|
+
* Get all documents merged from local optimistic map and server data
|
|
27
|
+
*/
|
|
28
|
+
const getOptimisticDocuments = (s: FilesStoreState): LobeDocument[] => {
|
|
29
|
+
// Track which documents we've added
|
|
30
|
+
const addedIds = new Set<string>();
|
|
31
|
+
|
|
32
|
+
// Create result array - start with server documents
|
|
33
|
+
const result: LobeDocument[] = s.documents.map((doc) => {
|
|
34
|
+
addedIds.add(doc.id);
|
|
35
|
+
// Check if we have a local optimistic update for this document
|
|
36
|
+
const localUpdate = s.localDocumentMap.get(doc.id);
|
|
37
|
+
// If local update exists and is newer, use it; otherwise use server version
|
|
38
|
+
if (localUpdate && new Date(localUpdate.updatedAt) >= new Date(doc.updatedAt)) {
|
|
39
|
+
return localUpdate;
|
|
40
|
+
}
|
|
41
|
+
return doc;
|
|
42
|
+
});
|
|
47
43
|
|
|
48
|
-
//
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
const title = page.title?.toLowerCase() || '';
|
|
54
|
-
return content.includes(lowerKeywords) || title.includes(lowerKeywords);
|
|
55
|
-
});
|
|
44
|
+
// Add any optimistic documents that aren't in server list yet (e.g., newly created temp documents)
|
|
45
|
+
for (const [id, doc] of s.localDocumentMap.entries()) {
|
|
46
|
+
if (!addedIds.has(id)) {
|
|
47
|
+
result.unshift(doc); // Add new documents to the beginning
|
|
48
|
+
}
|
|
56
49
|
}
|
|
57
50
|
|
|
58
|
-
|
|
59
|
-
return result.sort((a: LobeDocument, b: LobeDocument) => {
|
|
60
|
-
const dateA = a.createdAt ? new Date(a.createdAt).getTime() : 0;
|
|
61
|
-
const dateB = b.createdAt ? new Date(b.createdAt).getTime() : 0;
|
|
62
|
-
return dateB - dateA;
|
|
63
|
-
});
|
|
64
|
-
};
|
|
65
|
-
|
|
66
|
-
// Limited filtered pages for sidebar display
|
|
67
|
-
const getFilteredPagesLimited = (s: FilesStoreState): LobeDocument[] => {
|
|
68
|
-
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
69
|
-
const allPages = getFilteredPages(s);
|
|
70
|
-
return allPages.slice(0, pageSize);
|
|
51
|
+
return result;
|
|
71
52
|
};
|
|
72
53
|
|
|
73
54
|
const hasMoreDocuments = (s: FilesStoreState): boolean => s.hasMoreDocuments;
|
|
@@ -76,25 +57,10 @@ const isLoadingMoreDocuments = (s: FilesStoreState): boolean => s.isLoadingMoreD
|
|
|
76
57
|
|
|
77
58
|
const documentsTotal = (s: FilesStoreState): number => s.documentsTotal;
|
|
78
59
|
|
|
79
|
-
// Check if filtered pages have more than displayed
|
|
80
|
-
const hasMoreFilteredPages = (s: FilesStoreState): boolean => {
|
|
81
|
-
const pageSize = useGlobalStore.getState().status.pagePageSize || 20;
|
|
82
|
-
const allPages = getFilteredPages(s);
|
|
83
|
-
return allPages.length > pageSize;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
|
-
// Get total count of filtered pages
|
|
87
|
-
const filteredPagesCount = (s: FilesStoreState): number => {
|
|
88
|
-
return getFilteredPages(s).length;
|
|
89
|
-
};
|
|
90
|
-
|
|
91
60
|
export const documentSelectors = {
|
|
92
61
|
documentsTotal,
|
|
93
|
-
filteredPagesCount,
|
|
94
62
|
getDocumentById,
|
|
95
|
-
|
|
96
|
-
getFilteredPagesLimited,
|
|
63
|
+
getOptimisticDocuments,
|
|
97
64
|
hasMoreDocuments,
|
|
98
|
-
hasMoreFilteredPages,
|
|
99
65
|
isLoadingMoreDocuments,
|
|
100
66
|
};
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
export {
|
|
1
|
+
export type { NotebookAction } from './action';
|
|
2
|
+
export type { NotebookState } from './initialState';
|
|
3
|
+
export { notebookSelectors } from './selectors';
|
|
4
|
+
export type { NotebookStore } from './store';
|
|
5
|
+
export { getNotebookStoreState, useNotebookStore } from './store';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { NavigateFunction } from 'react-router-dom';
|
|
2
|
+
|
|
3
|
+
import { type LobeDocument } from '@/types/document';
|
|
4
|
+
|
|
5
|
+
export interface PageQueryFilter {
|
|
6
|
+
fileTypes?: string[];
|
|
7
|
+
sourceTypes?: string[];
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
export interface PageState {
|
|
11
|
+
// ===== Selection & Navigation =====
|
|
12
|
+
/**
|
|
13
|
+
* Whether all pages drawer is open
|
|
14
|
+
*/
|
|
15
|
+
allPagesDrawerOpen: boolean;
|
|
16
|
+
// ===== List Management =====
|
|
17
|
+
/**
|
|
18
|
+
* Current page number (0-based) for pagination
|
|
19
|
+
*/
|
|
20
|
+
currentPage: number;
|
|
21
|
+
/**
|
|
22
|
+
* Server documents fetched from document service
|
|
23
|
+
* undefined means not yet loaded (loading state)
|
|
24
|
+
*/
|
|
25
|
+
documents?: LobeDocument[];
|
|
26
|
+
/**
|
|
27
|
+
* Total count of documents
|
|
28
|
+
*/
|
|
29
|
+
documentsTotal: number;
|
|
30
|
+
/**
|
|
31
|
+
* Whether there are more documents to load
|
|
32
|
+
*/
|
|
33
|
+
hasMoreDocuments: boolean;
|
|
34
|
+
// ===== UI State =====
|
|
35
|
+
/**
|
|
36
|
+
* Whether currently creating a new page
|
|
37
|
+
*/
|
|
38
|
+
isCreatingNew: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Loading state for pagination (load more)
|
|
41
|
+
*/
|
|
42
|
+
isLoadingMoreDocuments: boolean;
|
|
43
|
+
|
|
44
|
+
navigate?: NavigateFunction;
|
|
45
|
+
/**
|
|
46
|
+
* Filters used in the last query
|
|
47
|
+
*/
|
|
48
|
+
queryFilter?: PageQueryFilter;
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* ID of the page being renamed (null if none)
|
|
52
|
+
*/
|
|
53
|
+
renamingPageId: string | null;
|
|
54
|
+
/**
|
|
55
|
+
* Search keywords for filtering pages
|
|
56
|
+
*/
|
|
57
|
+
searchKeywords: string;
|
|
58
|
+
/**
|
|
59
|
+
* Currently selected page ID
|
|
60
|
+
*/
|
|
61
|
+
selectedPageId: string | null;
|
|
62
|
+
/**
|
|
63
|
+
* Filter to show only pages not in any library
|
|
64
|
+
*/
|
|
65
|
+
showOnlyPagesNotInLibrary: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export const initialState: PageState = {
|
|
69
|
+
// Selection & Navigation
|
|
70
|
+
allPagesDrawerOpen: false,
|
|
71
|
+
|
|
72
|
+
// List Management
|
|
73
|
+
currentPage: 0,
|
|
74
|
+
|
|
75
|
+
documents: undefined,
|
|
76
|
+
|
|
77
|
+
documentsTotal: 0,
|
|
78
|
+
|
|
79
|
+
hasMoreDocuments: false,
|
|
80
|
+
|
|
81
|
+
// UI State
|
|
82
|
+
isCreatingNew: false,
|
|
83
|
+
|
|
84
|
+
isLoadingMoreDocuments: false,
|
|
85
|
+
|
|
86
|
+
queryFilter: undefined,
|
|
87
|
+
|
|
88
|
+
renamingPageId: null,
|
|
89
|
+
searchKeywords: '',
|
|
90
|
+
selectedPageId: null,
|
|
91
|
+
showOnlyPagesNotInLibrary: false,
|
|
92
|
+
};
|