@lobehub/lobehub 2.0.0-next.255 → 2.0.0-next.257

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 (144) hide show
  1. package/CHANGELOG.md +50 -0
  2. package/changelog/v1.json +18 -0
  3. package/locales/en-US/plugin.json +1 -0
  4. package/locales/zh-CN/plugin.json +1 -0
  5. package/package.json +1 -1
  6. package/packages/builtin-tool-notebook/src/client/Placeholder/CreateDocument.tsx +6 -6
  7. package/packages/builtin-tool-notebook/src/client/Render/CreateDocument/DocumentCard.tsx +23 -10
  8. package/packages/builtin-tool-notebook/src/client/Streaming/CreateDocument/index.tsx +1 -1
  9. package/packages/database/src/models/__tests__/agent.test.ts +91 -4
  10. package/packages/database/src/models/agent.ts +15 -7
  11. package/packages/editor-runtime/src/EditorRuntime.ts +1 -1
  12. package/packages/editor-runtime/src/__tests__/EditorRuntime.real.test.ts +65 -4
  13. package/packages/editor-runtime/src/__tests__/__snapshots__/EditorRuntime.real.test.ts.snap +108 -17
  14. package/packages/editor-runtime/src/__tests__/fixtures/remove-then-add.json +636 -0
  15. package/packages/editor-runtime/src/__tests__/fixtures/remove.json +1 -0
  16. package/packages/types/src/agent/agentConfig.ts +8 -8
  17. package/src/app/[variants]/(main)/chat/features/Portal/_layout/Mobile.tsx +2 -1
  18. package/src/app/[variants]/(main)/group/features/Portal/_layout/Mobile.tsx +2 -1
  19. package/src/app/[variants]/(main)/home/_layout/hooks/useCreateMenuItems.tsx +2 -2
  20. package/src/app/[variants]/(main)/page/{features/PageTitle → PageTitle}/index.tsx +0 -1
  21. package/src/app/[variants]/(main)/page/[id]/index.tsx +43 -1
  22. package/src/app/[variants]/(main)/page/_layout/Body/AllPagesDrawer/Content.tsx +15 -15
  23. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/Editing.tsx +3 -3
  24. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/index.tsx +7 -12
  25. package/src/app/[variants]/(main)/page/_layout/Body/List/Item/useDropdownMenu.tsx +5 -5
  26. package/src/app/[variants]/(main)/page/_layout/Body/List/index.tsx +7 -7
  27. package/src/app/[variants]/(main)/page/_layout/Body/index.tsx +15 -9
  28. package/src/app/[variants]/(main)/page/_layout/Body/useDropdownMenu.tsx +3 -3
  29. package/src/app/[variants]/(main)/page/_layout/DataSync.tsx +15 -0
  30. package/src/app/[variants]/(main)/page/_layout/Header/AddButton.tsx +2 -2
  31. package/src/app/[variants]/(main)/page/_layout/index.tsx +2 -0
  32. package/src/app/[variants]/(main)/page/index.tsx +3 -7
  33. package/src/components/Editor/AutoSaveHint.tsx +1 -1
  34. package/src/features/Conversation/Messages/User/Actions/index.tsx +5 -1
  35. package/src/features/EditorCanvas/AutoSaveHint.tsx +37 -0
  36. package/src/features/{PageEditor → EditorCanvas}/DiffAllToolbar.tsx +57 -16
  37. package/src/features/EditorCanvas/DocumentIdMode.tsx +111 -0
  38. package/src/features/EditorCanvas/EditorCanvas.tsx +148 -0
  39. package/src/features/EditorCanvas/EditorDataMode.tsx +64 -0
  40. package/src/features/EditorCanvas/ErrorBoundary.tsx +66 -0
  41. package/src/features/EditorCanvas/InlineToolbar.tsx +245 -0
  42. package/src/features/EditorCanvas/InternalEditor.tsx +134 -0
  43. package/src/features/{PageEditor/EditorCanvas → EditorCanvas}/actions.ts +10 -8
  44. package/src/features/EditorCanvas/index.ts +9 -0
  45. package/src/features/PageEditor/Copilot/index.tsx +16 -1
  46. package/src/features/PageEditor/EditorCanvas/index.tsx +14 -111
  47. package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +95 -0
  48. package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +1 -1
  49. package/src/features/PageEditor/Header/Breadcrumb.tsx +2 -2
  50. package/src/features/PageEditor/Header/index.tsx +15 -18
  51. package/src/features/PageEditor/Header/useMenu.tsx +12 -9
  52. package/src/features/PageEditor/PageEditor.tsx +45 -21
  53. package/src/features/PageEditor/PageEditorProvider.tsx +13 -1
  54. package/src/features/PageEditor/PageTitle/index.tsx +2 -2
  55. package/src/features/PageEditor/StoreUpdater.tsx +35 -308
  56. package/src/features/PageEditor/{Body/Title.tsx → TitleSection.tsx} +16 -16
  57. package/src/features/PageEditor/store/action.ts +96 -188
  58. package/src/features/PageEditor/store/initialState.ts +16 -21
  59. package/src/features/PageEditor/store/selectors.ts +3 -4
  60. package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +22 -14
  61. package/src/features/PageExplorer/index.tsx +34 -67
  62. package/src/features/Portal/Artifacts/index.ts +0 -2
  63. package/src/features/Portal/Document/AutoSaveHint.tsx +7 -6
  64. package/src/features/Portal/Document/Body.tsx +1 -3
  65. package/src/features/Portal/Document/EditorCanvas.tsx +7 -50
  66. package/src/features/Portal/Document/Header.tsx +13 -10
  67. package/src/features/Portal/Document/TodoList.tsx +6 -4
  68. package/src/features/Portal/Document/Wrapper.tsx +3 -11
  69. package/src/features/Portal/Document/index.ts +0 -2
  70. package/src/features/Portal/FilePreview/index.ts +0 -2
  71. package/src/features/Portal/GroupThread/index.ts +0 -3
  72. package/src/features/Portal/MessageDetail/index.ts +0 -2
  73. package/src/features/Portal/Notebook/index.ts +0 -2
  74. package/src/features/Portal/Plugins/index.ts +0 -2
  75. package/src/features/Portal/Thread/index.ts +0 -3
  76. package/src/features/Portal/components/Header.tsx +18 -6
  77. package/src/features/Portal/router.tsx +34 -97
  78. package/src/features/Portal/type.ts +0 -2
  79. package/src/features/RightPanel/index.tsx +11 -2
  80. package/src/locales/default/plugin.ts +1 -0
  81. package/src/store/chat/slices/portal/action.test.ts +218 -15
  82. package/src/store/chat/slices/portal/action.ts +194 -41
  83. package/src/store/chat/slices/portal/initialState.ts +40 -1
  84. package/src/store/chat/slices/portal/selectors/thread.ts +44 -3
  85. package/src/store/chat/slices/portal/selectors.test.ts +119 -17
  86. package/src/store/chat/slices/portal/selectors.ts +117 -36
  87. package/src/store/document/index.ts +17 -5
  88. package/src/store/document/slices/document/action.ts +209 -0
  89. package/src/store/document/slices/document/index.ts +6 -0
  90. package/src/store/document/slices/editor/action.test.ts +340 -0
  91. package/src/store/document/slices/editor/action.ts +133 -149
  92. package/src/store/document/slices/editor/index.ts +9 -2
  93. package/src/store/document/slices/editor/initialState.ts +66 -29
  94. package/src/store/document/slices/editor/reducer.test.ts +217 -0
  95. package/src/store/document/slices/editor/reducer.ts +67 -0
  96. package/src/store/document/slices/editor/selectors.test.ts +395 -0
  97. package/src/store/document/slices/editor/selectors.ts +107 -5
  98. package/src/store/document/store.ts +12 -13
  99. package/src/store/file/slices/document/action.ts +19 -188
  100. package/src/store/file/slices/document/initialState.ts +0 -30
  101. package/src/store/file/slices/document/selectors.ts +25 -59
  102. package/src/store/global/initialState.ts +2 -0
  103. package/src/store/global/selectors/systemStatus.ts +2 -0
  104. package/src/store/notebook/index.ts +5 -4
  105. package/src/store/page/index.ts +2 -0
  106. package/src/store/page/initialState.ts +92 -0
  107. package/src/store/page/selectors.ts +5 -0
  108. package/src/store/page/slices/crud/action.ts +477 -0
  109. package/src/store/page/slices/crud/index.ts +2 -0
  110. package/src/store/page/slices/crud/initialState.ts +7 -0
  111. package/src/store/page/slices/internal/action.ts +32 -0
  112. package/src/store/page/slices/internal/index.ts +2 -0
  113. package/src/store/page/slices/internal/reducer.ts +105 -0
  114. package/src/store/page/slices/list/action.ts +206 -0
  115. package/src/store/page/slices/list/index.ts +3 -0
  116. package/src/store/page/slices/list/initialState.ts +29 -0
  117. package/src/store/page/slices/list/selectors.ts +90 -0
  118. package/src/store/page/slices/selection/action.ts +67 -0
  119. package/src/store/page/slices/selection/index.ts +2 -0
  120. package/src/store/page/slices/selection/initialState.ts +11 -0
  121. package/src/store/page/store.ts +29 -0
  122. package/src/store/tool/slices/lobehubSkillStore/selectors.ts +10 -20
  123. package/src/utils/identifier.ts +8 -2
  124. package/src/features/PageEditor/Body/index.tsx +0 -68
  125. package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +0 -316
  126. package/src/features/PageEditor/Header/AutoSaveHint.tsx +0 -27
  127. package/src/features/Portal/Artifacts/useEnable.ts +0 -4
  128. package/src/features/Portal/Document/DocumentEditorProvider.tsx +0 -34
  129. package/src/features/Portal/Document/StoreUpdater.tsx +0 -80
  130. package/src/features/Portal/Document/Title.tsx +0 -54
  131. package/src/features/Portal/Document/store/action.ts +0 -114
  132. package/src/features/Portal/Document/store/index.ts +0 -21
  133. package/src/features/Portal/Document/store/initialState.ts +0 -24
  134. package/src/features/Portal/Document/useEnable.ts +0 -8
  135. package/src/features/Portal/FilePreview/useEnable.ts +0 -6
  136. package/src/features/Portal/GroupThread/hook.ts +0 -9
  137. package/src/features/Portal/MessageDetail/useEnable.ts +0 -4
  138. package/src/features/Portal/Notebook/useEnable.ts +0 -6
  139. package/src/features/Portal/Plugins/useEnable.ts +0 -6
  140. package/src/features/Portal/Thread/hook.ts +0 -8
  141. package/src/store/document/slices/notebook/action.ts +0 -119
  142. package/src/store/document/slices/notebook/index.ts +0 -3
  143. package/src/store/document/slices/notebook/initialState.ts +0 -12
  144. 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, selectedPageId } = get();
409
+ const { localDocumentMap, documents } = get();
522
410
  const newMap = new Map(localDocumentMap);
523
411
  newMap.delete(documentId);
524
412
 
525
- // Also remove from pages array to update the list immediately
526
- const newPages = documents.filter((doc) => doc.id !== documentId);
413
+ // Also remove from documents array to update the list immediately
414
+ const newDocuments = documents.filter((doc) => doc.id !== documentId);
527
415
 
528
- // Clear selected page ID if the deleted page is currently selected
529
- const updates: Partial<FileStore> = {
530
- documents: newPages,
531
- localDocumentMap: newMap,
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 pages table
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 page:', error);
546
- // Restore the page in local map and pages array on error
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
- const restoreUpdates: Partial<FileStore> = {
549
- documents,
550
- localDocumentMap: restoredMap,
551
- };
552
- if (selectedPageId === documentId) {
553
- restoreUpdates.selectedPageId = documentId;
554
- updateUrl(documentId);
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
- const getFilteredPages = (s: FilesStoreState): LobeDocument[] => {
27
- // Merge local optimistic map with server documents
28
- const localPages = Array.from(s.localDocumentMap.values());
29
- const serverPages = s.documents.filter((doc) => !s.localDocumentMap.has(doc.id));
30
- const pages = [...localPages, ...serverPages];
31
-
32
- const { searchKeywords, showOnlyPagesNotInLibrary } = s;
33
-
34
- let result = pages;
35
-
36
- // Filter out documents with sourceType='file'
37
- result = result.filter((page: LobeDocument) => page.sourceType !== 'file');
38
-
39
- // Filter by library membership
40
- if (showOnlyPagesNotInLibrary) {
41
- result = result.filter((page: LobeDocument) => {
42
- // Show only pages that are NOT in any library
43
- // Pages in a library have metadata.knowledgeBaseId set
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
- // Filter by search keywords
49
- if (searchKeywords.trim()) {
50
- const lowerKeywords = searchKeywords.toLowerCase();
51
- result = result.filter((page: LobeDocument) => {
52
- const content = page.content?.toLowerCase() || '';
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
- // Sort by creation date (newest first)
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
- getFilteredPages,
96
- getFilteredPagesLimited,
63
+ getOptimisticDocuments,
97
64
  hasMoreDocuments,
98
- hasMoreFilteredPages,
99
65
  isLoadingMoreDocuments,
100
66
  };
@@ -122,6 +122,7 @@ export interface SystemStatus {
122
122
  */
123
123
  modelSwitchPanelWidth?: number;
124
124
  noWideScreen?: boolean;
125
+ pageAgentPanelWidth?: number;
125
126
  /**
126
127
  * number of pages (documents) to display per page
127
128
  */
@@ -198,6 +199,7 @@ export const INITIAL_STATUS = {
198
199
  modelSwitchPanelGroupMode: 'byProvider',
199
200
  modelSwitchPanelWidth: 430,
200
201
  noWideScreen: true,
202
+ pageAgentPanelWidth: 360,
201
203
  pagePageSize: 20,
202
204
  portalWidth: 400,
203
205
  resourceManagerColumnWidths: {
@@ -27,6 +27,7 @@ const language = (s: GlobalState) => s.status.language || 'auto';
27
27
  const modelSwitchPanelGroupMode = (s: GlobalState) =>
28
28
  s.status.modelSwitchPanelGroupMode || 'byProvider';
29
29
  const modelSwitchPanelWidth = (s: GlobalState) => s.status.modelSwitchPanelWidth || 430;
30
+ const pageAgentPanelWidth = (s: GlobalState) => s.status.pageAgentPanelWidth || 360;
30
31
 
31
32
  const showChatHeader = (s: GlobalState) => !s.status.zenMode;
32
33
  const inZenMode = (s: GlobalState) => s.status.zenMode;
@@ -76,6 +77,7 @@ export const systemStatusSelectors = {
76
77
  mobileShowTopic,
77
78
  modelSwitchPanelGroupMode,
78
79
  modelSwitchPanelWidth,
80
+ pageAgentPanelWidth,
79
81
  pagePageSize,
80
82
  portalWidth,
81
83
  sessionGroupKeys,
@@ -1,4 +1,5 @@
1
- /**
2
- * @deprecated Use `@/store/document` instead. This is a compatibility layer.
3
- */
4
- export { notebookSelectors, useDocumentStore as useNotebookStore } from '../document';
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,2 @@
1
+ export * from './selectors';
2
+ export { getPageStoreState, type PageStore,usePageStore } 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
+ };
@@ -0,0 +1,5 @@
1
+ import { listSelectors } from './slices/list';
2
+
3
+ export const pageSelectors = {
4
+ ...listSelectors,
5
+ };