|
|
@@ -38,6 +38,7 @@ const AUTO_SAVE_DELAY = 3000; // 3秒无操作后自动保存
|
|
38
|
38
|
// 最大重试次数
|
|
39
|
39
|
const MAX_RETRY_ATTEMPTS = 3;
|
|
40
|
40
|
const MAX_HISTORY_ENTRIES = 100;
|
|
|
41
|
+const HISTORY_COALESCE_WINDOW = 500;
|
|
41
|
42
|
|
|
42
|
43
|
function waitForStructuralOperations(isReady: () => boolean): Promise<void> {
|
|
43
|
44
|
return new Promise((resolve) => {
|
|
|
@@ -140,6 +141,8 @@ interface EditorStore {
|
|
140
|
141
|
selectedBlockId: string | null;
|
|
141
|
142
|
past: EditorHistoryEntry[];
|
|
142
|
143
|
future: EditorHistoryEntry[];
|
|
|
144
|
+ lastHistoryEditBlockId: string | null;
|
|
|
145
|
+ lastHistoryEditAt: number | null;
|
|
143
|
146
|
isHistoryApplying: boolean;
|
|
144
|
147
|
pendingStructuralOperations: number;
|
|
145
|
148
|
|
|
|
@@ -359,6 +362,8 @@ const initialState = {
|
|
359
|
362
|
selectedBlockId: null,
|
|
360
|
363
|
past: [],
|
|
361
|
364
|
future: [],
|
|
|
365
|
+ lastHistoryEditBlockId: null,
|
|
|
366
|
+ lastHistoryEditAt: null,
|
|
362
|
367
|
isHistoryApplying: false,
|
|
363
|
368
|
pendingStructuralOperations: 0,
|
|
364
|
369
|
isLoading: false,
|
|
|
@@ -388,9 +393,36 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
388
|
393
|
-MAX_HISTORY_ENTRIES
|
|
389
|
394
|
),
|
|
390
|
395
|
future: [],
|
|
|
396
|
+ lastHistoryEditBlockId: null,
|
|
|
397
|
+ lastHistoryEditAt: null,
|
|
391
|
398
|
}));
|
|
392
|
399
|
};
|
|
393
|
400
|
|
|
|
401
|
+ const pushBlockEditHistory = (
|
|
|
402
|
+ blocks: DocumentBlock[],
|
|
|
403
|
+ selectedBlockId: string | null,
|
|
|
404
|
+ blockId: string
|
|
|
405
|
+ ) => {
|
|
|
406
|
+ const now = Date.now();
|
|
|
407
|
+ set((state) => {
|
|
|
408
|
+ const shouldCoalesce =
|
|
|
409
|
+ state.lastHistoryEditBlockId === blockId &&
|
|
|
410
|
+ state.lastHistoryEditAt !== null &&
|
|
|
411
|
+ now - state.lastHistoryEditAt < HISTORY_COALESCE_WINDOW;
|
|
|
412
|
+
|
|
|
413
|
+ return {
|
|
|
414
|
+ past: shouldCoalesce
|
|
|
415
|
+ ? state.past
|
|
|
416
|
+ : [...state.past, { blocks: snapshotBlocks(blocks), selectedBlockId }].slice(
|
|
|
417
|
+ -MAX_HISTORY_ENTRIES
|
|
|
418
|
+ ),
|
|
|
419
|
+ future: [],
|
|
|
420
|
+ lastHistoryEditBlockId: blockId,
|
|
|
421
|
+ lastHistoryEditAt: now,
|
|
|
422
|
+ };
|
|
|
423
|
+ });
|
|
|
424
|
+ };
|
|
|
425
|
+
|
|
394
|
426
|
const restoreHistoryEntry = async (entry: EditorHistoryEntry) => {
|
|
395
|
427
|
const { blockHashes, autoSaveEnabled, blocks: currentBlocks, documentId } = get();
|
|
396
|
428
|
let restoredBlocks = entry.blocks;
|
|
|
@@ -540,6 +572,8 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
540
|
572
|
loadAbortController: null,
|
|
541
|
573
|
past: [],
|
|
542
|
574
|
future: [],
|
|
|
575
|
+ lastHistoryEditBlockId: null,
|
|
|
576
|
+ lastHistoryEditAt: null,
|
|
543
|
577
|
});
|
|
544
|
578
|
} catch (error: unknown) {
|
|
545
|
579
|
if (controller.signal.aborted || isCanceledRequest(error)) return;
|
|
|
@@ -1149,6 +1183,15 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1149
|
1183
|
return;
|
|
1150
|
1184
|
}
|
|
1151
|
1185
|
|
|
|
1186
|
+ const currentBlock = blocks[blockIndex];
|
|
|
1187
|
+ const hasChanged = Object.entries(updates).some(([key, value]) => {
|
|
|
1188
|
+ const currentValue = (currentBlock as unknown as Record<string, unknown>)[key];
|
|
|
1189
|
+ return !Object.is(currentValue, value);
|
|
|
1190
|
+ });
|
|
|
1191
|
+ if (!hasChanged) {
|
|
|
1192
|
+ return;
|
|
|
1193
|
+ }
|
|
|
1194
|
+
|
|
1152
|
1195
|
const previousBlocks = blocks;
|
|
1153
|
1196
|
|
|
1154
|
1197
|
const newBlocks = blocks.slice();
|
|
|
@@ -1179,7 +1222,7 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1179
|
1222
|
hasModified: newDirtyBlocks.size > 0,
|
|
1180
|
1223
|
});
|
|
1181
|
1224
|
if (newHash !== previousHash) {
|
|
1182
|
|
- pushHistory(previousBlocks, get().selectedBlockId);
|
|
|
1225
|
+ pushBlockEditHistory(previousBlocks, get().selectedBlockId, id);
|
|
1183
|
1226
|
}
|
|
1184
|
1227
|
|
|
1185
|
1228
|
// 触发自动保存
|
|
|
@@ -1402,6 +1445,8 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1402
|
1445
|
future: [...state.future, { blocks: snapshotBlocks(blocks), selectedBlockId }].slice(
|
|
1403
|
1446
|
-MAX_HISTORY_ENTRIES
|
|
1404
|
1447
|
),
|
|
|
1448
|
+ lastHistoryEditBlockId: null,
|
|
|
1449
|
+ lastHistoryEditAt: null,
|
|
1405
|
1450
|
isHistoryApplying: false,
|
|
1406
|
1451
|
}));
|
|
1407
|
1452
|
} catch (error: unknown) {
|
|
|
@@ -1434,6 +1479,8 @@ export const useEditorStore = create<EditorStore>((set, get) => {
|
|
1434
|
1479
|
past: [...state.past, { blocks: snapshotBlocks(blocks), selectedBlockId }].slice(
|
|
1435
|
1480
|
-MAX_HISTORY_ENTRIES
|
|
1436
|
1481
|
),
|
|
|
1482
|
+ lastHistoryEditBlockId: null,
|
|
|
1483
|
+ lastHistoryEditAt: null,
|
|
1437
|
1484
|
isHistoryApplying: false,
|
|
1438
|
1485
|
}));
|
|
1439
|
1486
|
} catch (error: unknown) {
|