@lobehub/lobehub 2.0.0-next.254 → 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.
Files changed (148) 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/AssistantGroup/Tool/Render/Intervention/ApprovalActions.tsx +2 -2
  35. package/src/features/Conversation/Messages/AssistantGroup/Tool/index.tsx +18 -15
  36. package/src/features/Conversation/Messages/AssistantGroup/components/Group.tsx +3 -3
  37. package/src/features/Conversation/Messages/Contexts/MessageAggregationContext.ts +15 -0
  38. package/src/features/Conversation/Messages/Supervisor/components/Group.tsx +3 -3
  39. package/src/features/Conversation/Messages/User/Actions/index.tsx +5 -1
  40. package/src/features/EditorCanvas/AutoSaveHint.tsx +37 -0
  41. package/src/features/{PageEditor → EditorCanvas}/DiffAllToolbar.tsx +57 -16
  42. package/src/features/EditorCanvas/DocumentIdMode.tsx +111 -0
  43. package/src/features/EditorCanvas/EditorCanvas.tsx +148 -0
  44. package/src/features/EditorCanvas/EditorDataMode.tsx +64 -0
  45. package/src/features/EditorCanvas/ErrorBoundary.tsx +66 -0
  46. package/src/features/EditorCanvas/InlineToolbar.tsx +245 -0
  47. package/src/features/EditorCanvas/InternalEditor.tsx +134 -0
  48. package/src/features/{PageEditor/EditorCanvas → EditorCanvas}/actions.ts +10 -8
  49. package/src/features/EditorCanvas/index.ts +9 -0
  50. package/src/features/PageEditor/EditorCanvas/index.tsx +14 -111
  51. package/src/features/PageEditor/EditorCanvas/useAskCopilotItem.tsx +95 -0
  52. package/src/features/PageEditor/EditorCanvas/useSlashItems.tsx +1 -1
  53. package/src/features/PageEditor/Header/Breadcrumb.tsx +2 -2
  54. package/src/features/PageEditor/Header/index.tsx +15 -18
  55. package/src/features/PageEditor/Header/useMenu.tsx +12 -9
  56. package/src/features/PageEditor/PageEditor.tsx +45 -21
  57. package/src/features/PageEditor/PageEditorProvider.tsx +13 -1
  58. package/src/features/PageEditor/PageTitle/index.tsx +2 -2
  59. package/src/features/PageEditor/StoreUpdater.tsx +35 -308
  60. package/src/features/PageEditor/{Body/Title.tsx → TitleSection.tsx} +16 -16
  61. package/src/features/PageEditor/store/action.ts +96 -188
  62. package/src/features/PageEditor/store/initialState.ts +16 -21
  63. package/src/features/PageEditor/store/selectors.ts +3 -4
  64. package/src/features/PageExplorer/PageExplorerPlaceholder.tsx +22 -14
  65. package/src/features/PageExplorer/index.tsx +34 -67
  66. package/src/features/Portal/Artifacts/index.ts +0 -2
  67. package/src/features/Portal/Document/AutoSaveHint.tsx +7 -6
  68. package/src/features/Portal/Document/Body.tsx +1 -3
  69. package/src/features/Portal/Document/EditorCanvas.tsx +7 -50
  70. package/src/features/Portal/Document/Header.tsx +13 -10
  71. package/src/features/Portal/Document/TodoList.tsx +6 -4
  72. package/src/features/Portal/Document/Wrapper.tsx +3 -11
  73. package/src/features/Portal/Document/index.ts +0 -2
  74. package/src/features/Portal/FilePreview/index.ts +0 -2
  75. package/src/features/Portal/GroupThread/index.ts +0 -3
  76. package/src/features/Portal/MessageDetail/index.ts +0 -2
  77. package/src/features/Portal/Notebook/index.ts +0 -2
  78. package/src/features/Portal/Plugins/index.ts +0 -2
  79. package/src/features/Portal/Thread/index.ts +0 -3
  80. package/src/features/Portal/components/Header.tsx +18 -6
  81. package/src/features/Portal/router.tsx +34 -97
  82. package/src/features/Portal/type.ts +0 -2
  83. package/src/libs/next/config/define-config.ts +4 -1
  84. package/src/locales/default/plugin.ts +1 -0
  85. package/src/store/chat/slices/portal/action.test.ts +218 -15
  86. package/src/store/chat/slices/portal/action.ts +194 -41
  87. package/src/store/chat/slices/portal/initialState.ts +40 -1
  88. package/src/store/chat/slices/portal/selectors/thread.ts +44 -3
  89. package/src/store/chat/slices/portal/selectors.test.ts +119 -17
  90. package/src/store/chat/slices/portal/selectors.ts +117 -36
  91. package/src/store/document/index.ts +17 -5
  92. package/src/store/document/slices/document/action.ts +209 -0
  93. package/src/store/document/slices/document/index.ts +6 -0
  94. package/src/store/document/slices/editor/action.test.ts +340 -0
  95. package/src/store/document/slices/editor/action.ts +133 -149
  96. package/src/store/document/slices/editor/index.ts +9 -2
  97. package/src/store/document/slices/editor/initialState.ts +66 -29
  98. package/src/store/document/slices/editor/reducer.test.ts +217 -0
  99. package/src/store/document/slices/editor/reducer.ts +67 -0
  100. package/src/store/document/slices/editor/selectors.test.ts +395 -0
  101. package/src/store/document/slices/editor/selectors.ts +107 -5
  102. package/src/store/document/store.ts +12 -13
  103. package/src/store/file/slices/document/action.ts +19 -188
  104. package/src/store/file/slices/document/initialState.ts +0 -30
  105. package/src/store/file/slices/document/selectors.ts +25 -59
  106. package/src/store/notebook/index.ts +5 -4
  107. package/src/store/page/index.ts +2 -0
  108. package/src/store/page/initialState.ts +92 -0
  109. package/src/store/page/selectors.ts +5 -0
  110. package/src/store/page/slices/crud/action.ts +477 -0
  111. package/src/store/page/slices/crud/index.ts +2 -0
  112. package/src/store/page/slices/crud/initialState.ts +7 -0
  113. package/src/store/page/slices/internal/action.ts +32 -0
  114. package/src/store/page/slices/internal/index.ts +2 -0
  115. package/src/store/page/slices/internal/reducer.ts +105 -0
  116. package/src/store/page/slices/list/action.ts +206 -0
  117. package/src/store/page/slices/list/index.ts +3 -0
  118. package/src/store/page/slices/list/initialState.ts +29 -0
  119. package/src/store/page/slices/list/selectors.ts +90 -0
  120. package/src/store/page/slices/selection/action.ts +67 -0
  121. package/src/store/page/slices/selection/index.ts +2 -0
  122. package/src/store/page/slices/selection/initialState.ts +11 -0
  123. package/src/store/page/store.ts +29 -0
  124. package/src/store/tool/slices/lobehubSkillStore/selectors.ts +10 -20
  125. package/src/utils/identifier.ts +8 -2
  126. package/src/features/Conversation/Messages/AssistantGroup/components/GroupContext.ts +0 -15
  127. package/src/features/Conversation/Messages/Supervisor/components/GroupContext.ts +0 -15
  128. package/src/features/PageEditor/Body/index.tsx +0 -68
  129. package/src/features/PageEditor/EditorCanvas/InlineToolbar.tsx +0 -316
  130. package/src/features/PageEditor/Header/AutoSaveHint.tsx +0 -27
  131. package/src/features/Portal/Artifacts/useEnable.ts +0 -4
  132. package/src/features/Portal/Document/DocumentEditorProvider.tsx +0 -34
  133. package/src/features/Portal/Document/StoreUpdater.tsx +0 -80
  134. package/src/features/Portal/Document/Title.tsx +0 -54
  135. package/src/features/Portal/Document/store/action.ts +0 -114
  136. package/src/features/Portal/Document/store/index.ts +0 -21
  137. package/src/features/Portal/Document/store/initialState.ts +0 -24
  138. package/src/features/Portal/Document/useEnable.ts +0 -8
  139. package/src/features/Portal/FilePreview/useEnable.ts +0 -6
  140. package/src/features/Portal/GroupThread/hook.ts +0 -9
  141. package/src/features/Portal/MessageDetail/useEnable.ts +0 -4
  142. package/src/features/Portal/Notebook/useEnable.ts +0 -6
  143. package/src/features/Portal/Plugins/useEnable.ts +0 -6
  144. package/src/features/Portal/Thread/hook.ts +0 -8
  145. package/src/store/document/slices/notebook/action.ts +0 -119
  146. package/src/store/document/slices/notebook/index.ts +0 -3
  147. package/src/store/document/slices/notebook/initialState.ts +0 -12
  148. package/src/store/document/slices/notebook/selectors.ts +0 -26
@@ -1,3 +1,10 @@
1
- export { createEditorSlice, type EditorAction } from './action';
2
- export { type EditorState,initialEditorState } from './initialState';
1
+ export { createEditorSlice, type EditorAction, type SaveMetadata } from './action';
2
+ export {
3
+ createInitialEditorContentState,
4
+ type DocumentSourceType,
5
+ type EditorContentState,
6
+ type EditorState,
7
+ initialEditorState,
8
+ } from './initialState';
9
+ export { type DocumentDispatch,documentReducer } from './reducer';
3
10
  export { editorSelectors } from './selectors';
@@ -1,26 +1,31 @@
1
+ 'use client';
2
+
1
3
  import { type IEditor } from '@lobehub/editor';
4
+ import { type EditorState as LobehubEditorState } from '@lobehub/editor/react';
2
5
 
3
- export interface EditorState {
4
- /**
5
- * Current document content (markdown)
6
- */
7
- activeContent: string;
8
- /**
9
- * Current document ID being edited
10
- */
11
- activeDocumentId: string | undefined;
6
+ /**
7
+ * Document source type - determines which service to use for persistence
8
+ */
9
+ export type DocumentSourceType = 'notebook' | 'page';
10
+
11
+ /**
12
+ * Editor content state for a single document
13
+ * Only contains editor-related state, NOT document metadata (title, emoji, etc.)
14
+ */
15
+ export interface EditorContentState {
12
16
  /**
13
- * Current topic ID for the active document
17
+ * Whether auto-save is enabled for this document
18
+ * Defaults to true. Set to false if the consumer handles saving themselves.
14
19
  */
15
- activeTopicId: string | undefined;
20
+ autoSave?: boolean;
16
21
  /**
17
- * Editor instance from @lobehub/editor
22
+ * Document content (markdown)
18
23
  */
19
- editor: IEditor | undefined;
24
+ content: string;
20
25
  /**
21
- * Editor state from useEditorState hook
26
+ * Editor JSON data (BlockNote format)
22
27
  */
23
- editorState: any;
28
+ editorData: any;
24
29
  /**
25
30
  * Whether there are unsaved changes
26
31
  */
@@ -33,30 +38,62 @@ export interface EditorState {
33
38
  * Last updated time
34
39
  */
35
40
  lastUpdatedTime: Date | null;
36
- /**
37
- * Edit mode: 'edit' or 'preview'
38
- */
39
- mode: 'edit' | 'preview';
40
41
  /**
41
42
  * Current save status
42
43
  */
43
44
  saveStatus: 'idle' | 'saving' | 'saved';
44
45
  /**
45
- * Current document title
46
+ * Document source type - determines which service to call for persistence
46
47
  */
47
- title: string;
48
+ sourceType: DocumentSourceType;
49
+ /**
50
+ * Topic ID (for notebook documents, used for save routing)
51
+ */
52
+ topicId?: string;
48
53
  }
49
54
 
50
- export const initialEditorState: EditorState = {
51
- activeContent: '',
52
- activeDocumentId: undefined,
53
- activeTopicId: undefined,
54
- editor: undefined,
55
- editorState: undefined,
55
+ /**
56
+ * Global editor state
57
+ */
58
+ export interface EditorState {
59
+ /**
60
+ * Currently active document ID
61
+ */
62
+ activeDocumentId: string | undefined;
63
+ /**
64
+ * Map of editor content states by document ID
65
+ */
66
+ documents: Record<string, EditorContentState>;
67
+ /**
68
+ * Shared editor instance
69
+ */
70
+ editor: IEditor | undefined;
71
+ /**
72
+ * Editor state from useEditorState hook
73
+ */
74
+ editorState: LobehubEditorState | undefined;
75
+ }
76
+
77
+ /**
78
+ * Create initial state for a new document's editor content
79
+ */
80
+ export const createInitialEditorContentState = (
81
+ sourceType: DocumentSourceType,
82
+ overrides?: Partial<EditorContentState>,
83
+ ): EditorContentState => ({
84
+ content: '',
85
+ editorData: null,
56
86
  isDirty: false,
57
87
  lastSavedContent: '',
58
88
  lastUpdatedTime: null,
59
- mode: 'edit',
60
89
  saveStatus: 'idle',
61
- title: '',
90
+ sourceType,
91
+ ...overrides,
92
+ });
93
+
94
+ export const initialEditorState: EditorState = {
95
+ activeDocumentId: undefined,
96
+ documents: {},
97
+ editor: undefined,
98
+ editorState: undefined,
62
99
  };
@@ -0,0 +1,217 @@
1
+ import { describe, expect, it } from 'vitest';
2
+
3
+ import { type EditorContentState } from './initialState';
4
+ import { type DocumentDispatch, documentReducer } from './reducer';
5
+
6
+ describe('documentReducer', () => {
7
+ let state: Record<string, EditorContentState>;
8
+
9
+ beforeEach(() => {
10
+ state = {};
11
+ });
12
+
13
+ describe('addDocument', () => {
14
+ it('should add a new document to state', () => {
15
+ const payload: DocumentDispatch = {
16
+ id: 'doc-1',
17
+ type: 'addDocument',
18
+ value: { sourceType: 'page' },
19
+ };
20
+
21
+ const newState = documentReducer(state, payload);
22
+
23
+ expect(newState['doc-1']).toBeDefined();
24
+ expect(newState['doc-1'].sourceType).toBe('page');
25
+ expect(newState['doc-1'].isDirty).toBe(false);
26
+ expect(newState['doc-1'].saveStatus).toBe('idle');
27
+ });
28
+
29
+ it('should add document with all provided values', () => {
30
+ const payload: DocumentDispatch = {
31
+ id: 'doc-1',
32
+ type: 'addDocument',
33
+ value: {
34
+ autoSave: false,
35
+ content: '# Test content',
36
+ editorData: { type: 'doc' },
37
+ lastSavedContent: '# Test content',
38
+ sourceType: 'notebook',
39
+ topicId: 'topic-1',
40
+ },
41
+ };
42
+
43
+ const newState = documentReducer(state, payload);
44
+
45
+ expect(newState['doc-1']).toMatchObject({
46
+ autoSave: false,
47
+ content: '# Test content',
48
+ editorData: { type: 'doc' },
49
+ isDirty: false,
50
+ lastSavedContent: '# Test content',
51
+ sourceType: 'notebook',
52
+ topicId: 'topic-1',
53
+ });
54
+ });
55
+
56
+ it('should merge with existing document when ID exists', () => {
57
+ // First add a document
58
+ const initialPayload: DocumentDispatch = {
59
+ id: 'doc-1',
60
+ type: 'addDocument',
61
+ value: { content: 'Original', sourceType: 'page' },
62
+ };
63
+ state = documentReducer(state, initialPayload);
64
+
65
+ // Then add again with same ID
66
+ const updatePayload: DocumentDispatch = {
67
+ id: 'doc-1',
68
+ type: 'addDocument',
69
+ value: { content: 'Updated', sourceType: 'page' },
70
+ };
71
+ const newState = documentReducer(state, updatePayload);
72
+
73
+ expect(newState['doc-1'].content).toBe('Updated');
74
+ });
75
+ });
76
+
77
+ describe('updateDocument', () => {
78
+ it('should update an existing document', () => {
79
+ // First add a document
80
+ const addPayload: DocumentDispatch = {
81
+ id: 'doc-1',
82
+ type: 'addDocument',
83
+ value: { content: 'Original', sourceType: 'page' },
84
+ };
85
+ state = documentReducer(state, addPayload);
86
+
87
+ // Then update it
88
+ const updatePayload: DocumentDispatch = {
89
+ id: 'doc-1',
90
+ type: 'updateDocument',
91
+ value: { content: 'Updated', isDirty: true },
92
+ };
93
+ const newState = documentReducer(state, updatePayload);
94
+
95
+ expect(newState['doc-1'].content).toBe('Updated');
96
+ expect(newState['doc-1'].isDirty).toBe(true);
97
+ });
98
+
99
+ it('should not modify state if document does not exist', () => {
100
+ const payload: DocumentDispatch = {
101
+ id: 'non-existent',
102
+ type: 'updateDocument',
103
+ value: { content: 'Test' },
104
+ };
105
+
106
+ const newState = documentReducer(state, payload);
107
+
108
+ expect(newState['non-existent']).toBeUndefined();
109
+ expect(newState).toBe(state);
110
+ });
111
+
112
+ it('should not create new state reference if values are equal', () => {
113
+ // First add a document
114
+ const addPayload: DocumentDispatch = {
115
+ id: 'doc-1',
116
+ type: 'addDocument',
117
+ value: { content: 'Test', sourceType: 'page' },
118
+ };
119
+ state = documentReducer(state, addPayload);
120
+
121
+ // Update with same value
122
+ const updatePayload: DocumentDispatch = {
123
+ id: 'doc-1',
124
+ type: 'updateDocument',
125
+ value: { content: 'Test' },
126
+ };
127
+ const newState = documentReducer(state, updatePayload);
128
+
129
+ // Should be the same reference since nothing changed
130
+ expect(newState).toBe(state);
131
+ });
132
+ });
133
+
134
+ describe('deleteDocument', () => {
135
+ it('should delete an existing document', () => {
136
+ // First add a document
137
+ const addPayload: DocumentDispatch = {
138
+ id: 'doc-1',
139
+ type: 'addDocument',
140
+ value: { sourceType: 'page' },
141
+ };
142
+ state = documentReducer(state, addPayload);
143
+ expect(state['doc-1']).toBeDefined();
144
+
145
+ // Then delete it
146
+ const deletePayload: DocumentDispatch = {
147
+ id: 'doc-1',
148
+ type: 'deleteDocument',
149
+ };
150
+ const newState = documentReducer(state, deletePayload);
151
+
152
+ expect(newState['doc-1']).toBeUndefined();
153
+ });
154
+
155
+ it('should not affect other documents when deleting one', () => {
156
+ // Add two documents
157
+ state = documentReducer(state, {
158
+ id: 'doc-1',
159
+ type: 'addDocument',
160
+ value: { sourceType: 'page' },
161
+ });
162
+ state = documentReducer(state, {
163
+ id: 'doc-2',
164
+ type: 'addDocument',
165
+ value: { sourceType: 'notebook' },
166
+ });
167
+
168
+ // Delete one
169
+ const newState = documentReducer(state, {
170
+ id: 'doc-1',
171
+ type: 'deleteDocument',
172
+ });
173
+
174
+ expect(newState['doc-1']).toBeUndefined();
175
+ expect(newState['doc-2']).toBeDefined();
176
+ });
177
+ });
178
+
179
+ describe('default', () => {
180
+ it('should return the original state for unknown action type', () => {
181
+ const payload = {
182
+ id: 'doc-1',
183
+ type: 'unknown',
184
+ } as unknown as DocumentDispatch;
185
+
186
+ const newState = documentReducer(state, payload);
187
+
188
+ expect(newState).toBe(state);
189
+ });
190
+ });
191
+
192
+ describe('immutability', () => {
193
+ it('should generate immutable state object', () => {
194
+ const payload: DocumentDispatch = {
195
+ id: 'doc-1',
196
+ type: 'addDocument',
197
+ value: { sourceType: 'page' },
198
+ };
199
+
200
+ const newState = documentReducer(state, payload);
201
+
202
+ expect(newState).not.toBe(state);
203
+ });
204
+
205
+ it('should not modify the original state object', () => {
206
+ const payload: DocumentDispatch = {
207
+ id: 'doc-1',
208
+ type: 'addDocument',
209
+ value: { sourceType: 'page' },
210
+ };
211
+
212
+ documentReducer(state, payload);
213
+
214
+ expect(state).toEqual({});
215
+ });
216
+ });
217
+ });
@@ -0,0 +1,67 @@
1
+ import { produce } from 'immer';
2
+
3
+ import { type EditorContentState, createInitialEditorContentState } from './initialState';
4
+
5
+ // ============ Action Types ============
6
+
7
+ interface AddDocumentAction {
8
+ id: string;
9
+ type: 'addDocument';
10
+ value: Partial<EditorContentState> & { sourceType: EditorContentState['sourceType'] };
11
+ }
12
+
13
+ interface UpdateDocumentAction {
14
+ id: string;
15
+ type: 'updateDocument';
16
+ value: Partial<EditorContentState>;
17
+ }
18
+
19
+ interface DeleteDocumentAction {
20
+ id: string;
21
+ type: 'deleteDocument';
22
+ }
23
+
24
+ export type DocumentDispatch = AddDocumentAction | UpdateDocumentAction | DeleteDocumentAction;
25
+
26
+ // ============ Reducer ============
27
+
28
+ export const documentReducer = (
29
+ state: Record<string, EditorContentState> = {},
30
+ payload: DocumentDispatch,
31
+ ): Record<string, EditorContentState> => {
32
+ switch (payload.type) {
33
+ case 'addDocument': {
34
+ return produce(state, (draft) => {
35
+ const { id, value } = payload;
36
+ const existingDoc = draft[id];
37
+
38
+ // Create new document state, merging with existing if present
39
+ draft[id] = existingDoc
40
+ ? { ...existingDoc, ...value }
41
+ : createInitialEditorContentState(value.sourceType, value);
42
+ });
43
+ }
44
+
45
+ case 'updateDocument': {
46
+ return produce(state, (draft) => {
47
+ const { id, value } = payload;
48
+ const currentDoc = draft[id];
49
+
50
+ if (currentDoc) {
51
+ // Directly assign to let immer handle change detection
52
+ Object.assign(draft[id], value);
53
+ }
54
+ });
55
+ }
56
+
57
+ case 'deleteDocument': {
58
+ return produce(state, (draft) => {
59
+ delete draft[payload.id];
60
+ });
61
+ }
62
+
63
+ default: {
64
+ return state;
65
+ }
66
+ }
67
+ };