@tmagic/editor 1.8.0-beta.7 → 1.8.0-beta.9

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 (137) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +11 -3
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +3 -3
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +1 -1
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +24 -11
  5. package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  6. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +2 -2
  7. package/dist/es/components/Icon.vue_vue_type_script_setup_true_lang.js +1 -1
  8. package/dist/es/components/Resizer.vue_vue_type_script_setup_true_lang.js +1 -1
  9. package/dist/es/components/ScrollBar.vue_vue_type_script_setup_true_lang.js +1 -1
  10. package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
  11. package/dist/es/components/SearchInput.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/components/SplitView.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +1 -1
  18. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  19. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +1 -1
  20. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +3 -3
  22. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +1 -1
  23. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +3 -3
  24. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +2 -2
  25. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +1 -1
  26. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +1 -1
  27. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +3 -3
  28. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  29. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +1 -1
  30. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  31. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +1 -1
  32. package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  33. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +1 -1
  34. package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +1 -1
  35. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +1 -1
  37. package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +1 -1
  38. package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +1 -1
  39. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +1 -1
  40. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +1 -1
  41. package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
  42. package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
  43. package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
  44. package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
  45. package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
  46. package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +1 -1
  47. package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +1 -1
  48. package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +1 -1
  49. package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +1 -1
  50. package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +1 -1
  51. package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +1 -1
  52. package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +1 -1
  53. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +1 -1
  54. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +1 -1
  55. package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
  56. package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
  57. package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
  58. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +1 -1
  59. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +1 -1
  60. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +1 -1
  61. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +1 -1
  62. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +1 -1
  63. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +1 -1
  64. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +1 -1
  65. package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  66. package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  67. package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  68. package/dist/es/index.js +8 -4
  69. package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
  70. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +1 -1
  71. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
  72. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +3 -3
  73. package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  74. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +1 -1
  75. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +39 -27
  77. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +10 -3
  78. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +167 -302
  79. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +1 -1
  80. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +1 -1
  81. package/dist/es/layouts/history-list/composables.js +23 -55
  82. package/dist/es/layouts/history-list/useHistoryList.js +56 -0
  83. package/dist/es/layouts/history-list/useHistoryRevert.js +307 -0
  84. package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  85. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +1 -1
  86. package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  87. package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
  88. package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
  89. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  90. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  91. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  92. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +1 -1
  93. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +1 -1
  94. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  95. package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  96. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +3 -3
  97. package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
  98. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  99. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  100. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +1 -1
  101. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +1 -1
  102. package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
  103. package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
  104. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  105. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +1 -1
  106. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
  107. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +1 -1
  108. package/dist/es/services/codeBlock.js +32 -28
  109. package/dist/es/services/dataSource.js +43 -34
  110. package/dist/es/services/editor.js +31 -26
  111. package/dist/es/services/history.js +268 -384
  112. package/dist/es/style.css +12 -0
  113. package/dist/es/utils/dep/worker.js +1 -1
  114. package/dist/es/utils/history.js +35 -47
  115. package/dist/style.css +12 -0
  116. package/dist/tmagic-editor.umd.cjs +5524 -4807
  117. package/package.json +8 -8
  118. package/src/Editor.vue +10 -1
  119. package/src/components/CompareForm.vue +28 -7
  120. package/src/fields/DataSourceInput.vue +3 -3
  121. package/src/index.ts +4 -0
  122. package/src/layouts/NavMenu.vue +2 -2
  123. package/src/layouts/history-list/GroupRow.vue +10 -0
  124. package/src/layouts/history-list/HistoryDiffDialog.vue +8 -1
  125. package/src/layouts/history-list/HistoryListPanel.vue +64 -251
  126. package/src/layouts/history-list/PageTab.vue +4 -4
  127. package/src/layouts/history-list/composables.ts +52 -90
  128. package/src/layouts/history-list/useHistoryList.ts +60 -0
  129. package/src/layouts/history-list/useHistoryRevert.ts +406 -0
  130. package/src/services/codeBlock.ts +30 -29
  131. package/src/services/dataSource.ts +37 -37
  132. package/src/services/editor.ts +32 -29
  133. package/src/services/history.ts +340 -430
  134. package/src/theme/history-list-panel.scss +14 -0
  135. package/src/type.ts +185 -92
  136. package/src/utils/history.ts +52 -67
  137. package/types/index.d.ts +466 -297
@@ -39,7 +39,7 @@ import type {
39
39
  import { CODE_DRAFT_STORAGE_KEY } from '@editor/type';
40
40
  import { getEditorConfig } from '@editor/utils/config';
41
41
  import { COPY_CODE_STORAGE_KEY } from '@editor/utils/editor';
42
- import { describeRevertStep, getLastPushedHistoryIds } from '@editor/utils/history';
42
+ import { createStackStep, describeRevertStep, getLastPushedHistoryIds } from '@editor/utils/history';
43
43
 
44
44
  import BaseService from './BaseService';
45
45
 
@@ -188,14 +188,14 @@ class CodeBlock extends BaseService {
188
188
  const newContent = cloneDeep(codeDsl[id]);
189
189
 
190
190
  if (!doNotPushHistory) {
191
- this.lastPushedHistoryId =
192
- historyService.pushCodeBlock(id, {
193
- oldContent,
194
- newContent,
195
- changeRecords,
196
- historyDescription,
197
- source: historySource,
198
- })?.uuid ?? null;
191
+ const step = createStackStep<CodeBlockContent, CodeBlockStepValue>(id, {
192
+ oldValue: oldContent,
193
+ newValue: newContent,
194
+ changeRecords,
195
+ historyDescription,
196
+ source: historySource,
197
+ });
198
+ this.lastPushedHistoryId = (step ? historyService.push('codeBlock', step, id) : null)?.uuid ?? null;
199
199
  }
200
200
 
201
201
  this.emit('addOrUpdate', id, codeDsl[id]);
@@ -301,17 +301,18 @@ class CodeBlock extends BaseService {
301
301
 
302
302
  codeIds.forEach((id) => {
303
303
  // 历史记录:删除前快照内容;不存在的 id 直接跳过历史推入
304
- const oldContent: CodeBlockContent | null = currentDsl[id] ? cloneDeep(currentDsl[id]) : null;
304
+ const oldValue: CodeBlockContent | null = currentDsl[id] ? cloneDeep(currentDsl[id]) : null;
305
305
 
306
306
  delete currentDsl[id];
307
307
 
308
- if (oldContent && !doNotPushHistory) {
309
- const uuid = historyService.pushCodeBlock(id, {
310
- oldContent,
311
- newContent: null,
308
+ if (oldValue && !doNotPushHistory) {
309
+ const step = createStackStep<CodeBlockContent, CodeBlockStepValue>(id, {
310
+ oldValue,
311
+ newValue: null,
312
312
  historyDescription,
313
313
  source: historySource,
314
- })?.uuid;
314
+ });
315
+ const uuid = step ? historyService.push('codeBlock', step, id)?.uuid : undefined;
315
316
  if (uuid) this.lastDeletedHistoryIds.push(uuid);
316
317
  }
317
318
 
@@ -385,7 +386,7 @@ class CodeBlock extends BaseService {
385
386
  * @returns 撤销的 step;栈不存在或已无可撤销时返回 null
386
387
  */
387
388
  public async undo(id: Id): Promise<CodeBlockStepValue | null> {
388
- const step = historyService.undoCodeBlock(id);
389
+ const step = historyService.undo('codeBlock', id);
389
390
  if (!step) return null;
390
391
  await this.applyHistoryStep(step, true);
391
392
  return step;
@@ -397,7 +398,7 @@ class CodeBlock extends BaseService {
397
398
  * @returns 重做的 step;栈不存在或已无可重做时返回 null
398
399
  */
399
400
  public async redo(id: Id): Promise<CodeBlockStepValue | null> {
400
- const step = historyService.redoCodeBlock(id);
401
+ const step = historyService.redo('codeBlock', id);
401
402
  if (!step) return null;
402
403
  await this.applyHistoryStep(step, false);
403
404
  return step;
@@ -405,12 +406,12 @@ class CodeBlock extends BaseService {
405
406
 
406
407
  /** 是否可对指定代码块撤销。 */
407
408
  public canUndo(id: Id): boolean {
408
- return historyService.canUndoCodeBlock(id);
409
+ return historyService.canUndo('codeBlock', id);
409
410
  }
410
411
 
411
412
  /** 是否可对指定代码块重做。 */
412
413
  public canRedo(id: Id): boolean {
413
- return historyService.canRedoCodeBlock(id);
414
+ return historyService.canRedo('codeBlock', id);
414
415
  }
415
416
 
416
417
  /**
@@ -421,7 +422,7 @@ class CodeBlock extends BaseService {
421
422
  * @returns 实际移动到的最终游标位置
422
423
  */
423
424
  public async goto(id: Id, targetCursor: number): Promise<number> {
424
- let cursor = historyService.getCodeBlockCursor(id);
425
+ let cursor = historyService.getCursor('codeBlock', id);
425
426
  const target = Math.max(0, targetCursor);
426
427
  while (cursor > target) {
427
428
  const step = await this.undo(id);
@@ -447,14 +448,14 @@ class CodeBlock extends BaseService {
447
448
  * @returns 反向后产生的新 step;目标不存在 / 未应用时返回 null
448
449
  */
449
450
  public async revert(id: Id, index: number): Promise<CodeBlockStepValue | null> {
450
- const list = historyService.getCodeBlockStepList(id);
451
+ const list = historyService.getStepList('codeBlock', id);
451
452
  const entry = list[index];
452
453
  if (!entry?.applied) return null;
453
454
  // 更新类步骤(前后 content 都存在)必须带 changeRecords 才支持回滚,否则只能整内容替换,会冲掉后续无关变更。
454
455
  const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
455
456
 
456
457
  if (oldSchema && newSchema && !changeRecords?.length) return null;
457
- const description = `回滚 #${index + 1}: ${describeRevertStep<CodeBlockContent>(entry.step.id, entry.step.diff?.[0], (s) => s.name)}`;
458
+ const description = `回滚 #${index + 1}: ${describeRevertStep<CodeBlockContent>(entry.step.data.id, entry.step.diff?.[0], (s) => s.name)}`;
458
459
  return await this.applyRevertStep(entry.step, description);
459
460
  }
460
461
 
@@ -468,7 +469,7 @@ class CodeBlock extends BaseService {
468
469
  public async revertById(uuids: string[]): Promise<(CodeBlockStepValue | null)[]> {
469
470
  const results: (CodeBlockStepValue | null)[] = [];
470
471
  for (const uuid of uuids) {
471
- const location = historyService.findCodeBlockStepLocationByUuid(uuid);
472
+ const location = historyService.findStepLocationByUuid('codeBlock', uuid);
472
473
  results.push(location ? await this.revert(location.id, location.index) : null);
473
474
  }
474
475
  return results;
@@ -564,19 +565,19 @@ class CodeBlock extends BaseService {
564
565
  step: CodeBlockStepValue,
565
566
  historyDescription: string,
566
567
  ): Promise<CodeBlockStepValue | null> {
567
- const { id } = step;
568
+ const { id } = step.data;
568
569
  const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
569
570
 
570
571
  // 原本是新增 → revert 即删除
571
572
  if (!oldSchema && newSchema) {
572
573
  await this.deleteCodeDslByIds([id], { historyDescription, historySource: 'rollback' });
573
- return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
574
+ return historyService.getStepList('codeBlock', id).slice(-1)[0]?.step ?? null;
574
575
  }
575
576
 
576
577
  // 原本是删除 → revert 即写回
577
578
  if (oldSchema && !newSchema) {
578
579
  this.setCodeDslByIdSync(id, cloneDeep(oldSchema), true, { historyDescription, historySource: 'rollback' });
579
- return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
580
+ return historyService.getStepList('codeBlock', id).slice(-1)[0]?.step ?? null;
580
581
  }
581
582
 
582
583
  if (!oldSchema || !newSchema) return null;
@@ -600,11 +601,11 @@ class CodeBlock extends BaseService {
600
601
  historyDescription,
601
602
  historySource: 'rollback',
602
603
  });
603
- return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
604
+ return historyService.getStepList('codeBlock', id).slice(-1)[0]?.step ?? null;
604
605
  }
605
606
 
606
607
  this.setCodeDslByIdSync(id, cloneDeep(oldSchema), true, { historyDescription, historySource: 'rollback' });
607
- return historyService.getCodeBlockStepList(id).slice(-1)[0]?.step ?? null;
608
+ return historyService.getStepList('codeBlock', id).slice(-1)[0]?.step ?? null;
608
609
  }
609
610
 
610
611
  /**
@@ -622,7 +623,7 @@ class CodeBlock extends BaseService {
622
623
  * @param reverse true=撤销,false=重做
623
624
  */
624
625
  private async applyHistoryStep(step: CodeBlockStepValue, reverse: boolean): Promise<void> {
625
- const { id } = step;
626
+ const { id } = step.data;
626
627
  const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
627
628
 
628
629
  // 新增 / 删除:直接 set 或 delete,不走 patch 逻辑
@@ -20,7 +20,7 @@ import type {
20
20
  } from '@editor/type';
21
21
  import { getFormConfig, getFormValue } from '@editor/utils/data-source';
22
22
  import { COPY_DS_STORAGE_KEY } from '@editor/utils/editor';
23
- import { describeRevertStep, getLastPushedHistoryIds } from '@editor/utils/history';
23
+ import { createStackStep, describeRevertStep, getLastPushedHistoryIds } from '@editor/utils/history';
24
24
 
25
25
  import BaseService from './BaseService';
26
26
 
@@ -137,13 +137,13 @@ class DataSource extends BaseService {
137
137
  this.get('dataSources').push(newConfig);
138
138
 
139
139
  if (!doNotPushHistory) {
140
- this.lastPushedHistoryId =
141
- historyService.pushDataSource(newConfig.id, {
142
- oldSchema: null,
143
- newSchema: newConfig,
144
- historyDescription,
145
- source: historySource,
146
- })?.uuid ?? null;
140
+ const step = createStackStep<DataSourceSchema, DataSourceStepValue>(newConfig.id, {
141
+ oldValue: null,
142
+ newValue: newConfig,
143
+ historyDescription,
144
+ source: historySource,
145
+ });
146
+ this.lastPushedHistoryId = (step ? historyService.push('dataSource', step, newConfig.id) : null)?.uuid ?? null;
147
147
  }
148
148
 
149
149
  this.emit('add', newConfig);
@@ -178,14 +178,14 @@ class DataSource extends BaseService {
178
178
  dataSources[index] = newConfig;
179
179
 
180
180
  if (!doNotPushHistory) {
181
- this.lastPushedHistoryId =
182
- historyService.pushDataSource(newConfig.id, {
183
- oldSchema: oldConfig ? cloneDeep(oldConfig) : null,
184
- newSchema: newConfig,
185
- changeRecords,
186
- historyDescription,
187
- source: historySource,
188
- })?.uuid ?? null;
181
+ const step = createStackStep<DataSourceSchema, DataSourceStepValue>(newConfig.id, {
182
+ oldValue: oldConfig ? cloneDeep(oldConfig) : null,
183
+ newValue: newConfig,
184
+ changeRecords,
185
+ historyDescription,
186
+ source: historySource,
187
+ });
188
+ this.lastPushedHistoryId = (step ? historyService.push('dataSource', step, newConfig.id) : null)?.uuid ?? null;
189
189
  }
190
190
 
191
191
  this.emit('update', newConfig, {
@@ -210,13 +210,13 @@ class DataSource extends BaseService {
210
210
  dataSources.splice(index, 1);
211
211
 
212
212
  if (oldConfig && !doNotPushHistory) {
213
- this.lastPushedHistoryId =
214
- historyService.pushDataSource(id, {
215
- oldSchema: cloneDeep(oldConfig),
216
- newSchema: null,
217
- historyDescription,
218
- source: historySource,
219
- })?.uuid ?? null;
213
+ const step = createStackStep<DataSourceSchema, DataSourceStepValue>(id, {
214
+ oldValue: cloneDeep(oldConfig),
215
+ newValue: null,
216
+ historyDescription,
217
+ source: historySource,
218
+ });
219
+ this.lastPushedHistoryId = (step ? historyService.push('dataSource', step, id) : null)?.uuid ?? null;
220
220
  }
221
221
 
222
222
  this.emit('remove', id);
@@ -270,7 +270,7 @@ class DataSource extends BaseService {
270
270
  * @returns 撤销的 step;栈不存在或已无可撤销时返回 null
271
271
  */
272
272
  public undo(id: Id) {
273
- const step = historyService.undoDataSource(id);
273
+ const step = historyService.undo('dataSource', id);
274
274
  if (!step) return null;
275
275
  this.applyHistoryStep(step, true);
276
276
  return step;
@@ -282,7 +282,7 @@ class DataSource extends BaseService {
282
282
  * @returns 重做的 step;栈不存在或已无可重做时返回 null
283
283
  */
284
284
  public redo(id: Id) {
285
- const step = historyService.redoDataSource(id);
285
+ const step = historyService.redo('dataSource', id);
286
286
  if (!step) return null;
287
287
  this.applyHistoryStep(step, false);
288
288
  return step;
@@ -290,12 +290,12 @@ class DataSource extends BaseService {
290
290
 
291
291
  /** 是否可对指定数据源撤销。 */
292
292
  public canUndo(id: Id): boolean {
293
- return historyService.canUndoDataSource(id);
293
+ return historyService.canUndo('dataSource', id);
294
294
  }
295
295
 
296
296
  /** 是否可对指定数据源重做。 */
297
297
  public canRedo(id: Id): boolean {
298
- return historyService.canRedoDataSource(id);
298
+ return historyService.canRedo('dataSource', id);
299
299
  }
300
300
 
301
301
  /**
@@ -306,7 +306,7 @@ class DataSource extends BaseService {
306
306
  * @returns 实际移动到的最终游标位置
307
307
  */
308
308
  public goto(id: Id, targetCursor: number): number {
309
- let cursor = historyService.getDataSourceCursor(id);
309
+ let cursor = historyService.getCursor('dataSource', id);
310
310
  const target = Math.max(0, targetCursor);
311
311
  while (cursor > target) {
312
312
  if (!this.undo(id)) break;
@@ -330,13 +330,13 @@ class DataSource extends BaseService {
330
330
  * @returns 反向后产生的新 step;目标不存在 / 未应用时返回 null
331
331
  */
332
332
  public revert(id: Id, index: number): DataSourceStepValue | null {
333
- const list = historyService.getDataSourceStepList(id);
333
+ const list = historyService.getStepList('dataSource', id);
334
334
  const entry = list[index];
335
335
  if (!entry?.applied) return null;
336
336
  // 更新类步骤(前后 schema 都存在)必须带 changeRecords 才支持回滚,否则只能整 schema 替换,会冲掉后续无关变更。
337
337
  const { oldSchema, newSchema, changeRecords } = entry.step.diff?.[0] ?? {};
338
338
  if (oldSchema && newSchema && !changeRecords?.length) return null;
339
- const description = `回滚 #${index + 1}: ${describeRevertStep<DataSourceSchema>(entry.step.id, entry.step.diff?.[0], (s) => s.title)}`;
339
+ const description = `回滚 #${index + 1}: ${describeRevertStep<DataSourceSchema>(entry.step.data.id, entry.step.diff?.[0], (s) => s.title)}`;
340
340
  return this.applyRevertStep(entry.step, description);
341
341
  }
342
342
 
@@ -349,7 +349,7 @@ class DataSource extends BaseService {
349
349
  */
350
350
  public revertById(uuids: string[]): (DataSourceStepValue | null)[] {
351
351
  return uuids.map((uuid) => {
352
- const location = historyService.findDataSourceStepLocationByUuid(uuid);
352
+ const location = historyService.findStepLocationByUuid('dataSource', uuid);
353
353
  if (!location) return null;
354
354
  return this.revert(location.id, location.index);
355
355
  });
@@ -435,19 +435,19 @@ class DataSource extends BaseService {
435
435
  * 同构,差异仅在于走对应的公共 add / update / remove 而不是带 doNotPushHistory 的版本。
436
436
  */
437
437
  private applyRevertStep(step: DataSourceStepValue, historyDescription: string): DataSourceStepValue | null {
438
- const { id } = step;
438
+ const { id } = step.data;
439
439
  const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
440
440
 
441
441
  // 原本是新增 → revert 即删除
442
442
  if (!oldSchema && newSchema) {
443
443
  this.remove(`${id}`, { historyDescription, historySource: 'rollback' });
444
- return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
444
+ return historyService.getStepList('dataSource', id).slice(-1)[0]?.step ?? null;
445
445
  }
446
446
 
447
447
  // 原本是删除 → revert 即重新加回
448
448
  if (oldSchema && !newSchema) {
449
449
  this.add(cloneDeep(oldSchema), { historyDescription, historySource: 'rollback' });
450
- return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
450
+ return historyService.getStepList('dataSource', id).slice(-1)[0]?.step ?? null;
451
451
  }
452
452
 
453
453
  if (!oldSchema || !newSchema) return null;
@@ -471,11 +471,11 @@ class DataSource extends BaseService {
471
471
  historyDescription,
472
472
  historySource: 'rollback',
473
473
  });
474
- return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
474
+ return historyService.getStepList('dataSource', id).slice(-1)[0]?.step ?? null;
475
475
  }
476
476
 
477
477
  this.update(cloneDeep(oldSchema), { historyDescription, historySource: 'rollback' });
478
- return historyService.getDataSourceStepList(id).slice(-1)[0]?.step ?? null;
478
+ return historyService.getStepList('dataSource', id).slice(-1)[0]?.step ?? null;
479
479
  }
480
480
 
481
481
  /**
@@ -493,7 +493,7 @@ class DataSource extends BaseService {
493
493
  * @param reverse true=撤销,false=重做
494
494
  */
495
495
  private applyHistoryStep(step: DataSourceStepValue, reverse: boolean): void {
496
- const { id } = step;
496
+ const { id } = step.data;
497
497
  const { oldSchema, newSchema, changeRecords } = step.diff?.[0] ?? {};
498
498
 
499
499
  // 新增 / 删除:直接 add 或 remove,不走 patch 逻辑
@@ -165,8 +165,8 @@ class Editor extends BaseService {
165
165
  // 重复创建,set root 不额外产生记录,由恢复出的历史栈作为当前状态来源。
166
166
  // 标记不进入撤销/重做栈,仅作为该页历史列表底部的初始基线展示。
167
167
  app.items?.forEach((pageNode) => {
168
- if (pageNode?.id !== undefined && !historyService.getPageMarker(pageNode.id)) {
169
- historyService.setPageMarker(pageNode.id, {
168
+ if (pageNode?.id !== undefined && !historyService.getMarker('page', pageNode.id)) {
169
+ historyService.setMarker('page', pageNode.id, {
170
170
  name: pageNode.name,
171
171
  source: options.historySource,
172
172
  });
@@ -297,12 +297,6 @@ class Editor extends BaseService {
297
297
  this.set('page', page);
298
298
  this.set('parent', parent);
299
299
 
300
- if (page) {
301
- historyService.changePage(toRaw(page));
302
- } else {
303
- historyService.resetState();
304
- }
305
-
306
300
  if (node?.id) {
307
301
  this.get('stage')
308
302
  ?.renderer?.runtime?.getApp?.()
@@ -586,8 +580,6 @@ class Editor extends BaseService {
586
580
  stage?.select(pages[0].id);
587
581
  } else {
588
582
  this.selectRoot();
589
-
590
- historyService.resetPage();
591
583
  }
592
584
  };
593
585
 
@@ -1320,7 +1312,9 @@ class Editor extends BaseService {
1320
1312
  * @returns 被撤销的操作
1321
1313
  */
1322
1314
  public async undo(): Promise<StepValue | null> {
1323
- const value = historyService.undo();
1315
+ const pageId = this.get('page')?.id;
1316
+ if (pageId === undefined) return null;
1317
+ const value = historyService.undo('page', pageId);
1324
1318
  if (value) {
1325
1319
  await this.applyHistoryOp(value, true);
1326
1320
  }
@@ -1332,7 +1326,9 @@ class Editor extends BaseService {
1332
1326
  * @returns 被恢复的操作
1333
1327
  */
1334
1328
  public async redo(): Promise<StepValue | null> {
1335
- const value = historyService.redo();
1329
+ const pageId = this.get('page')?.id;
1330
+ if (pageId === undefined) return null;
1331
+ const value = historyService.redo('page', pageId);
1336
1332
  if (value) {
1337
1333
  await this.applyHistoryOp(value, false);
1338
1334
  }
@@ -1356,7 +1352,7 @@ class Editor extends BaseService {
1356
1352
  * @returns 反向后产生的新 step;目标不存在 / 未应用 / 反向失败时返回 null
1357
1353
  */
1358
1354
  public async revertPageStep(index: number): Promise<StepValue | null> {
1359
- const list = historyService.getPageStepList();
1355
+ const list = historyService.getStepList('page', this.get('page')?.id);
1360
1356
  const entry = list[index];
1361
1357
  if (!entry?.applied) return null;
1362
1358
 
@@ -1374,7 +1370,8 @@ class Editor extends BaseService {
1374
1370
 
1375
1371
  // 反向应用产生的新 step 由内部 pushOpHistory 触发 history `change` 事件,监听一次以拿到引用。
1376
1372
  let revertedStep: StepValue | null = null;
1377
- const captureRevert = (s: StepValue) => {
1373
+ // page `change` 事件回调签名为 `(pageId, step)`,这里只关心被回滚产生的新 step。
1374
+ const captureRevert = (_pageId: Id, s: StepValue) => {
1378
1375
  revertedStep = s;
1379
1376
  };
1380
1377
  historyService.once('change', captureRevert);
@@ -1461,9 +1458,10 @@ class Editor extends BaseService {
1461
1458
  */
1462
1459
  public async revertPageStepById(uuids: string[]): Promise<(StepValue | null)[]> {
1463
1460
  const results: (StepValue | null)[] = [];
1461
+ const pageId = this.get('page')?.id;
1464
1462
  for (const uuid of uuids) {
1465
- const index = historyService.getPageStepIndexByUuid(uuid);
1466
- results.push(index < 0 ? null : await this.revertPageStep(index));
1463
+ const location = historyService.findStepLocationByUuid('page', uuid, pageId);
1464
+ results.push(!location ? null : await this.revertPageStep(location.index));
1467
1465
  }
1468
1466
  return results;
1469
1467
  }
@@ -1478,8 +1476,9 @@ class Editor extends BaseService {
1478
1476
  * @returns 实际移动到的最终游标位置
1479
1477
  */
1480
1478
  public async gotoPageStep(targetCursor: number): Promise<number> {
1481
- let cursor = historyService.getPageCursor();
1482
- const { length } = historyService.getPageStepList();
1479
+ const pageId = this.get('page')?.id;
1480
+ let cursor = historyService.getCursor('page', pageId);
1481
+ const { length } = historyService.getStepList('page', pageId);
1483
1482
  const target = Math.max(0, Math.min(targetCursor, length));
1484
1483
  while (cursor > target) {
1485
1484
  const step = await this.undo();
@@ -1628,9 +1627,11 @@ class Editor extends BaseService {
1628
1627
  uuid: guid(),
1629
1628
  data: { name: page.name || '', id: page.id },
1630
1629
  opType,
1631
- selectedBefore: [],
1632
- selectedAfter: [],
1633
- modifiedNodeIds: new Map(),
1630
+ extra: {
1631
+ selectedBefore: [],
1632
+ selectedAfter: [],
1633
+ modifiedNodeIds: new Map(),
1634
+ },
1634
1635
  diff: [diffItem],
1635
1636
  rootStep: true,
1636
1637
  };
@@ -1638,9 +1639,9 @@ class Editor extends BaseService {
1638
1639
 
1639
1640
  const top = historyService.getCurrentPageStep(page.id);
1640
1641
  if (top?.rootStep && top.source === source) {
1641
- historyService.replaceCurrentPageStep(step, page.id);
1642
+ historyService.replaceCurrentStep('page', step, page.id);
1642
1643
  } else {
1643
- historyService.push(step, page.id);
1644
+ historyService.push('page', step, page.id);
1644
1645
  }
1645
1646
  }
1646
1647
 
@@ -1662,16 +1663,18 @@ class Editor extends BaseService {
1662
1663
  uuid: guid(),
1663
1664
  data: pageData,
1664
1665
  opType,
1665
- selectedBefore: this.selectionBeforeOp ?? [],
1666
- selectedAfter: this.get('nodes').map((n) => n.id),
1667
- modifiedNodeIds: new Map(this.get('modifiedNodeIds')),
1666
+ extra: {
1667
+ selectedBefore: this.selectionBeforeOp ?? [],
1668
+ selectedAfter: this.get('nodes').map((n) => n.id),
1669
+ modifiedNodeIds: new Map(this.get('modifiedNodeIds')),
1670
+ },
1668
1671
  diff,
1669
1672
  };
1670
1673
  if (historyDescription) step.historyDescription = historyDescription;
1671
1674
  if (source) step.source = source;
1672
1675
  // 显式按 step.data.id 入栈:跨页操作(如 moveToContainer 从源页搬到目标页)
1673
1676
  // 必须落到正确的页面栈,否则会把记录错误地推到当前活动页 / 操作发起页。
1674
- const pushed = historyService.push(step, pageData.id);
1677
+ const pushed = historyService.push('page', step, pageData.id);
1675
1678
  // push 返回 null 表示当前没有可写入的页面栈(未真正入栈),此时不应返回 uuid。
1676
1679
  const historyId = pushed ? step.uuid : null;
1677
1680
  this.lastPushedHistoryId = historyId;
@@ -1799,11 +1802,11 @@ class Editor extends BaseService {
1799
1802
  }
1800
1803
  }
1801
1804
 
1802
- this.set('modifiedNodeIds', step.modifiedNodeIds);
1805
+ this.set('modifiedNodeIds', step.extra?.modifiedNodeIds ?? new Map());
1803
1806
 
1804
1807
  const page = toRaw(this.get('page'));
1805
1808
  if (page) {
1806
- const selectIds = reverse ? step.selectedBefore : step.selectedAfter;
1809
+ const selectIds = (reverse ? step.extra?.selectedBefore : step.extra?.selectedAfter) ?? [];
1807
1810
  setTimeout(() => {
1808
1811
  if (!selectIds.length) return;
1809
1812