@tmagic/editor 1.8.0-beta.4 → 1.8.0-beta.6

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 (76) hide show
  1. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +1 -1
  2. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +6 -6
  3. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +4 -1
  4. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +8 -2
  5. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +25 -8
  6. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +6 -4
  7. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +25 -12
  8. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +27 -5
  9. package/dist/es/index.js +6 -3
  10. package/dist/es/initService.js +1 -1
  11. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +19 -55
  13. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +22 -39
  14. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +149 -103
  15. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +32 -8
  16. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +335 -213
  17. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +36 -7
  18. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +47 -60
  19. package/dist/es/layouts/history-list/composables.js +73 -32
  20. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +4 -2
  21. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +5 -1
  22. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +16 -3
  23. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +23 -1
  24. package/dist/es/services/codeBlock.js +96 -37
  25. package/dist/es/services/dataSource.js +70 -26
  26. package/dist/es/services/editor.js +266 -104
  27. package/dist/es/services/history.js +271 -212
  28. package/dist/es/services/ui.js +3 -0
  29. package/dist/es/style.css +53 -7
  30. package/dist/es/utils/data-source/index.js +2 -0
  31. package/dist/es/utils/editor.js +103 -49
  32. package/dist/es/utils/history.js +232 -0
  33. package/dist/es/utils/indexed-db.js +86 -0
  34. package/dist/es/utils/undo-redo.js +60 -1
  35. package/dist/style.css +53 -7
  36. package/dist/tmagic-editor.umd.cjs +2064 -1000
  37. package/package.json +7 -7
  38. package/src/components/CompareForm.vue +3 -1
  39. package/src/components/ToolButton.vue +2 -2
  40. package/src/fields/CodeSelectCol.vue +7 -1
  41. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +16 -2
  42. package/src/fields/DataSourceFields.vue +37 -8
  43. package/src/fields/DataSourceMethodSelect.vue +9 -4
  44. package/src/fields/DataSourceMethods.vue +42 -21
  45. package/src/fields/StyleSetter/components/Border.vue +15 -6
  46. package/src/index.ts +1 -0
  47. package/src/initService.ts +1 -1
  48. package/src/layouts/Framework.vue +1 -1
  49. package/src/layouts/history-list/Bucket.vue +34 -71
  50. package/src/layouts/history-list/BucketTab.vue +46 -54
  51. package/src/layouts/history-list/GroupRow.vue +146 -111
  52. package/src/layouts/history-list/HistoryDiffDialog.vue +94 -68
  53. package/src/layouts/history-list/HistoryListPanel.vue +298 -115
  54. package/src/layouts/history-list/InitialRow.vue +28 -9
  55. package/src/layouts/history-list/PageTab.vue +57 -51
  56. package/src/layouts/history-list/composables.ts +157 -36
  57. package/src/layouts/page-bar/PageBar.vue +4 -2
  58. package/src/layouts/sidebar/Sidebar.vue +6 -1
  59. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +30 -2
  60. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +26 -1
  61. package/src/services/codeBlock.ts +113 -37
  62. package/src/services/dataSource.ts +94 -40
  63. package/src/services/editor.ts +376 -136
  64. package/src/services/history.ts +306 -209
  65. package/src/services/ui.ts +7 -0
  66. package/src/theme/history-list-panel.scss +72 -5
  67. package/src/theme/page-bar.scss +0 -4
  68. package/src/theme/style-setter/border.scss +4 -1
  69. package/src/type.ts +183 -64
  70. package/src/utils/data-source/index.ts +2 -0
  71. package/src/utils/editor.ts +166 -60
  72. package/src/utils/history.ts +308 -0
  73. package/src/utils/index.ts +2 -0
  74. package/src/utils/indexed-db.ts +122 -0
  75. package/src/utils/undo-redo.ts +88 -0
  76. package/types/index.d.ts +813 -302
@@ -45,6 +45,28 @@
45
45
  list-style: none;
46
46
  }
47
47
 
48
+ // 历史列表工具条:放置「清空」等列表级操作,右对齐。
49
+ .m-editor-history-list-toolbar {
50
+ display: flex;
51
+ align-items: center;
52
+ justify-content: flex-end;
53
+ padding: 0 4px 4px;
54
+ }
55
+
56
+ // 「清空」按钮:红色文字按钮,强调破坏性操作(点击后会二次确认)。
57
+ .m-editor-history-list-clear {
58
+ padding: 2px 8px;
59
+ border-radius: 4px;
60
+ font-size: 12px;
61
+ color: #f56c6c;
62
+ cursor: pointer;
63
+ user-select: none;
64
+
65
+ &:hover {
66
+ background-color: rgba(245, 108, 108, 0.12);
67
+ }
68
+ }
69
+
48
70
  .m-editor-history-list-item {
49
71
  display: flex;
50
72
  align-items: center;
@@ -215,6 +237,10 @@
215
237
 
216
238
  .m-editor-history-list-item-index {
217
239
  flex: 0 0 auto;
240
+ // 固定最小宽度并右对齐:序号位数不一(#6 / #16)时右边界仍统一,
241
+ // 使紧随其后的「类型」徽标在各行间对齐成整齐的一列。
242
+ min-width: 30px;
243
+ text-align: right;
218
244
  color: #909399;
219
245
  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
220
246
  font-size: 11px;
@@ -222,6 +248,15 @@
222
248
  white-space: nowrap;
223
249
  }
224
250
 
251
+ // 合并组头部展示的是步骤区间(如 #10-#20),宽度本就不定、也无需与单步行对齐,
252
+ // 恢复自然宽度与左对齐,避免被强制成固定列后显得突兀。
253
+ .m-editor-history-list-group.is-merged
254
+ > .m-editor-history-list-group-head
255
+ > .m-editor-history-list-item-index {
256
+ min-width: 0;
257
+ text-align: left;
258
+ }
259
+
225
260
  // 操作时间:弱化展示,紧贴在描述之后、各操作按钮之前。
226
261
  .m-editor-history-list-item-time {
227
262
  flex: 0 0 auto;
@@ -295,6 +330,20 @@
295
330
  font-weight: 400; // 防止被合并组头部的粗体继承
296
331
  }
297
332
 
333
+ // 「已保存」徽标:绿色实心胶囊,标记最近一次保存对应的历史记录(与 historyService.markSaved 对应)。
334
+ .m-editor-history-list-item-saved {
335
+ flex: 0 0 auto;
336
+ padding: 0 6px;
337
+ border-radius: 8px;
338
+ font-size: 10px;
339
+ line-height: 16px;
340
+ color: #fff;
341
+ background-color: #67c23a;
342
+ white-space: nowrap;
343
+ font-weight: 500;
344
+ letter-spacing: 0.2px;
345
+ }
346
+
298
347
  // 「合并 N 步」徽标:紫色实心胶囊,与合并组卡片色系一致,醒目区分单步条目。
299
348
  .m-editor-history-list-item-merge {
300
349
  flex: 0 0 auto;
@@ -308,6 +357,24 @@
308
357
  letter-spacing: 0.2px;
309
358
  }
310
359
 
360
+ // 操作区:把「回滚 / 回到 / 查看差异」收敛为一个统一容器,默认隐藏,
361
+ // 仅在指针悬停于所在行时显示。静止状态下每行最右侧固定为「时间」,
362
+ // 各行因此能对齐成整齐的右侧列,避免按钮数量不一导致的参差错乱。
363
+ .m-editor-history-list-item-actions {
364
+ display: none;
365
+ flex: 0 0 auto;
366
+ align-items: center;
367
+ gap: 6px;
368
+ }
369
+
370
+ .m-editor-history-list-group-head:hover > .m-editor-history-list-item-actions,
371
+ .m-editor-history-list-substeps
372
+ > li:hover
373
+ > .m-editor-history-list-item-actions,
374
+ .m-editor-history-list-initial:hover > .m-editor-history-list-item-actions {
375
+ display: flex;
376
+ }
377
+
311
378
  .m-editor-history-list-item-diff {
312
379
  flex: 0 0 auto;
313
380
  padding: 0 6px;
@@ -324,21 +391,21 @@
324
391
  }
325
392
  }
326
393
 
327
- // 「跳转」按钮:将历史游标移动到该 step,替代原先点击整行跳转的交互。
328
- // 使用与组卡片一致的紫色色系,与「查看差异」「回滚」区分开。
394
+ // 「回到」按钮:将历史游标移动到该 step。使用绿色色系,
395
+ // 与红色「回滚」、蓝色「查看差异」区分,也避免与紧邻的灰色「来源」徽标混淆。
329
396
  .m-editor-history-list-item-goto {
330
397
  flex: 0 0 auto;
331
398
  padding: 0 6px;
332
399
  border-radius: 2px;
333
400
  font-size: 10px;
334
401
  line-height: 16px;
335
- color: #606266;
336
- background-color: rgba(96, 98, 102, 0.1);
402
+ color: #529b2e;
403
+ background-color: rgba(103, 194, 58, 0.12);
337
404
  cursor: pointer;
338
405
  user-select: none;
339
406
 
340
407
  &:hover {
341
- background-color: rgba(96, 98, 102, 0.18);
408
+ background-color: rgba(103, 194, 58, 0.24);
342
409
  }
343
410
  }
344
411
 
@@ -102,10 +102,6 @@
102
102
  transition: all 0.2s ease 0s;
103
103
  padding: 5px 14px;
104
104
 
105
- .tmagic-design-button {
106
- color: $font-color;
107
- }
108
-
109
105
  &:hover {
110
106
  background-color: $hover-color;
111
107
  }
@@ -24,9 +24,12 @@
24
24
  & + .border-icon {
25
25
  margin-left: 8px;
26
26
  }
27
+ &.configured {
28
+ border-color: var(--el-color-success, var(--td-success-color, #2ba471));
29
+ }
27
30
  &.active {
28
31
  border-width: 1px;
29
- border-color: var(--el-color-primary);
32
+ border-color: var(--el-color-primary, var(--td-brand-color, #0052d9));
30
33
  }
31
34
  &.border-icon-top {
32
35
  border-top-width: 2px;
package/src/type.ts CHANGED
@@ -56,7 +56,7 @@ import type { PropsService } from './services/props';
56
56
  import type { StageOverlayService } from './services/stageOverlay';
57
57
  import type { StorageService } from './services/storage';
58
58
  import type { UiService } from './services/ui';
59
- import type { UndoRedo } from './utils/undo-redo';
59
+ import type { SerializedUndoRedo, UndoRedo } from './utils/undo-redo';
60
60
 
61
61
  export type EditorSlots = FrameworkSlots &
62
62
  WorkspaceSlots &
@@ -312,6 +312,8 @@ export interface UiState {
312
312
  hideSlideBar: boolean;
313
313
  /** 侧边栏面板配置 */
314
314
  sideBarItems: SideComponent[];
315
+ /** 当前激活的侧边栏面板 */
316
+ sideBarActiveTabName: string;
315
317
 
316
318
  // navMenu 的宽高
317
319
  navMenuRect: {
@@ -333,6 +335,7 @@ export interface EditorNodeInfo {
333
335
  node: MNode | null;
334
336
  parent: MContainer | null;
335
337
  page: MPage | MPageFragment | null;
338
+ path: MNode[];
336
339
  }
337
340
  // #endregion EditorNodeInfo
338
341
 
@@ -390,6 +393,9 @@ export interface MenuButton {
390
393
  items?: MenuButton[];
391
394
  /** 唯一标识,用于高亮 */
392
395
  id?: string | number;
396
+ buttonProps?: {
397
+ type?: string;
398
+ };
393
399
  }
394
400
  // #endregion MenuButton
395
401
 
@@ -680,7 +686,13 @@ export interface CodeParamStatement {
680
686
  }
681
687
 
682
688
  // #region HistoryOpType
683
- export type HistoryOpType = 'add' | 'remove' | 'update';
689
+ /**
690
+ * 历史记录操作类型:
691
+ * - `add` / `remove` / `update`:普通可撤销/重做的节点变更;
692
+ * - `initial`:页面「未修改的初始状态」基线(设置 root 时生成),作为页面栈 index 0 的固定底线 step。
693
+ * 该 step 不可被撤销/回滚(cursor 不会低于它),仅用于历史面板底部的初始行展示。
694
+ */
695
+ export type HistoryOpType = 'add' | 'remove' | 'update' | 'initial';
684
696
  // #endregion HistoryOpType
685
697
 
686
698
  // #region HistoryOpSource
@@ -705,11 +717,13 @@ export type HistoryOpType = 'add' | 'remove' | 'update';
705
717
  * 通过 `(string & {})` 允许业务侧扩展自定义途径字符串,同时保留内置值的自动补全。
706
718
  */
707
719
  export type HistoryOpSource =
720
+ | 'initial'
708
721
  | 'stage'
709
722
  | 'tree'
710
723
  | 'component-panel'
711
724
  | 'props'
712
725
  | 'code'
726
+ | 'root-code'
713
727
  | 'stage-contextmenu'
714
728
  | 'tree-contextmenu'
715
729
  | 'toolbar'
@@ -717,35 +731,69 @@ export type HistoryOpSource =
717
731
  | 'rollback'
718
732
  | 'api'
719
733
  | 'ai'
734
+ // 同步
735
+ | 'sync'
720
736
  | 'unknown'
721
737
  | (string & {});
722
738
  // #endregion HistoryOpSource
723
739
 
724
- // #region StepValue
725
- export interface StepValue {
726
- /** 页面信息 */
727
- data: { name: string; id: Id };
728
- opType: HistoryOpType;
729
- /** 操作前选中的节点 ID,用于撤销后恢复选择状态 */
730
- selectedBefore: Id[];
731
- /** 操作后选中的节点 ID,用于重做后恢复选择状态 */
732
- selectedAfter: Id[];
733
- modifiedNodeIds: Map<Id, Id>;
734
- /** opType 'add': 新增的节点 */
735
- nodes?: MNode[];
736
- /** opType 'add': 父节点 ID */
740
+ // #region DslOpWithHistoryIdsResult
741
+ /** *AndGetHistoryId 系列方法返回值:原操作结果 + 本次写入历史记录的 uuid 列表(未入栈时为 `[]`)。 */
742
+ export type DslOpWithHistoryIdsResult<T> = {
743
+ result: T;
744
+ historyIds: string[];
745
+ };
746
+ // #endregion DslOpWithHistoryIdsResult
747
+
748
+ // #region StepDiffItem
749
+ /**
750
+ * 单条变更的 diff 描述,统一表达「页面节点 / 代码块 / 数据源」的变化内容,
751
+ * 被 {@link StepValue} / {@link CodeBlockStepValue} / {@link DataSourceStepValue} 的 `diff` 复用。
752
+ *
753
+ * 按 `opType` 区分携带的字段:
754
+ * - `add`:仅 `newSchema`(页面节点还带 `parentId` / `index`);
755
+ * - `remove`:仅 `oldSchema`(页面节点还带 `parentId` / `index`);
756
+ * - `update`:`oldSchema` + `newSchema`,并可带 `changeRecords` 做局部更新。
757
+ *
758
+ * 泛型 `T` 为变化内容的快照类型:页面节点为 `MNode`,代码块为 `CodeBlockContent`,数据源为 `DataSourceSchema`。
759
+ */
760
+ export interface StepDiffItem<T = unknown> {
761
+ /** 变更后的内容快照。`opType` 为 `add` / `update` 时有,`remove` 时无。 */
762
+ newSchema?: T;
763
+ /** 变更前的内容快照。`opType` 为 `remove` / `update` 时有,`add` 时无。 */
764
+ oldSchema?: T;
765
+ /** 父节点 id。仅页面节点有(数据源 / 代码块没有父节点)。 */
737
766
  parentId?: Id;
738
- /** opType 'add': 每个新增节点在父节点 items 中的索引 */
739
- indexMap?: Record<string, number>;
740
- /** opType 'remove': 被删除的节点及其位置信息 */
741
- removedItems?: { node: MNode; parentId: Id; index: number }[];
767
+ /** 在父节点 items 数组中的索引。仅页面节点有(数据源 / 代码块无需排序)。 */
768
+ index?: number;
742
769
  /**
743
- * opType 'update': 变更前后的节点快照
744
- *
745
- * `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
746
- * 缺省(未传 / 空数组)才退化为整节点替换。
770
+ * form 端 propPath/value 变更列表,仅 `opType` 为 `update` 时有;
771
+ * 撤销/重做时若有则按 propPath 局部更新,缺省才退化为整内容替换。
747
772
  */
748
- updatedItems?: { oldNode: MNode; newNode: MNode; changeRecords?: ChangeRecord[] }[];
773
+ changeRecords?: ChangeRecord[];
774
+ }
775
+ // #endregion StepDiffItem
776
+
777
+ // #region BaseStepValue
778
+ /**
779
+ * 历史记录条目公共字段,被 {@link StepValue} / {@link CodeBlockStepValue} / {@link DataSourceStepValue} 复用。
780
+ *
781
+ * 泛型 `T` 为 `diff` 中变化内容的快照类型(页面节点 `MNode` / 代码块 `CodeBlockContent` / 数据源 `DataSourceSchema`)。
782
+ */
783
+ export interface BaseStepValue<T = unknown> {
784
+ /**
785
+ * 历史记录唯一标识(uuid)。入栈时自动写入(若调用方未指定),
786
+ * 用于精确定位 / 引用某一条历史记录(如 revert、埋点、跨端同步等)。
787
+ * 注意与各自的 `id`(关联的页面 / 代码块 / 数据源 id)区分。
788
+ */
789
+ uuid: string;
790
+ /** 操作类型:新增 / 删除 / 更新(三类历史记录统一携带)。 */
791
+ opType: HistoryOpType;
792
+ /**
793
+ * 本次变更的内容(统一 diff 表达),每项见 {@link StepDiffItem}。
794
+ * 页面节点(add/remove 多节点、update 多节点)会有多项,代码块 / 数据源通常只有一项。
795
+ */
796
+ diff: StepDiffItem<T>[];
749
797
  /**
750
798
  * 调用方可选传入的人类可读描述(如「调整按钮颜色」),用于历史面板展示。
751
799
  * 不影响 undo/redo 行为;缺省时面板会根据节点 / propPath 自动生成描述。
@@ -758,65 +806,60 @@ export interface StepValue {
758
806
  */
759
807
  source?: HistoryOpSource;
760
808
  /**
761
- * 入栈时间戳(毫秒)。在 historyService.push 时自动写入(若调用方未指定),仅用于历史面板展示。
809
+ * 入栈时间戳(毫秒)。入栈时自动写入(若调用方未指定),仅用于历史面板展示。
762
810
  */
763
811
  timestamp?: number;
812
+ /**
813
+ * 是否为「已保存」记录:DSL 落库(如保存到后端 / 本地)时由 historyService.markSaved 标记。
814
+ * 同一栈内任意时刻最多只有一条记录为 true;从 IndexedDB 恢复时游标会被定位到最近一条已保存记录之后。
815
+ */
816
+ saved?: boolean;
817
+ /**
818
+ * 是否为「整体设置 root」(set root)产生的记录(由 {@link Editor.pushRootDiffHistory} 写入)。
819
+ * 用于「连续 set root 合并」:当某页栈最新一条已是 root 记录时,下一条 set root 会替换它而非新增,
820
+ * 避免源码反复保存 / 外部重设 DSL 时堆积多条 root 记录。
821
+ */
822
+ rootStep?: boolean;
823
+ }
824
+ // #endregion BaseStepValue
825
+
826
+ // #region StepValue
827
+ export interface StepValue extends BaseStepValue<MNode> {
828
+ /** 页面信息 */
829
+ data: { name: string; id: Id };
830
+ /** 操作前选中的节点 ID,用于撤销后恢复选择状态 */
831
+ selectedBefore: Id[];
832
+ /** 操作后选中的节点 ID,用于重做后恢复选择状态 */
833
+ selectedAfter: Id[];
834
+ modifiedNodeIds: Map<Id, Id>;
764
835
  }
765
836
  // #endregion StepValue
766
837
 
767
838
  // #region CodeBlockStepValue
768
839
  /**
769
840
  * 代码块历史记录条目。按 codeBlock.id 分组保存到 historyState.codeBlockState。
770
- * - 新增:oldContent = null,newContent = 新内容
771
- * - 更新:oldContent / newContent 都为对应内容
772
- * - 删除:newContent = null,oldContent = 删除前内容
841
+ * 变更内容统一由 `diff`(单项)表达,每项见 {@link StepDiffItem}:
842
+ * - 新增(opType 'add'):仅 `newSchema`(新内容);
843
+ * - 更新(opType 'update'):`oldSchema` + `newSchema`,并可带 `changeRecords` 做局部更新;
844
+ * - 删除(opType 'remove'):仅 `oldSchema`(删除前内容)。
773
845
  */
774
- export interface CodeBlockStepValue {
846
+ export interface CodeBlockStepValue extends BaseStepValue<CodeBlockContent> {
775
847
  /** 关联的代码块 id */
776
848
  id: Id;
777
- /** 变更前的代码块内容,新增时为 null */
778
- oldContent: CodeBlockContent | null;
779
- /** 变更后的代码块内容,删除时为 null */
780
- newContent: CodeBlockContent | null;
781
- /**
782
- * form 端 propPath/value 列表。撤销/重做时若有则按 propPath 局部更新;
783
- * 缺省才退化为整内容替换。新增/删除场景通常无 changeRecords。
784
- */
785
- changeRecords?: ChangeRecord[];
786
- /** 调用方可选传入的人类可读描述,用于历史面板展示;不影响 undo/redo 行为。 */
787
- historyDescription?: string;
788
- /** 操作途径:标记本次变更由哪条交互入口触发,取值见 {@link HistoryOpSource};仅用于历史面板展示与埋点,不影响 undo/redo 行为。 */
789
- source?: HistoryOpSource;
790
- /** 入栈时间戳(毫秒),入栈时自动写入,仅用于历史面板展示。 */
791
- timestamp?: number;
792
849
  }
793
850
  // #endregion CodeBlockStepValue
794
851
 
795
852
  // #region DataSourceStepValue
796
853
  /**
797
854
  * 数据源历史记录条目。按 dataSource.id 分组保存到 historyState.dataSourceState。
798
- * - 新增:oldSchema = null,newSchema = 新 schema
799
- * - 更新:oldSchema / newSchema 都为对应 schema
800
- * - 删除:newSchema = null,oldSchema = 删除前 schema
855
+ * 变更内容统一由 `diff`(单项)表达,每项见 {@link StepDiffItem}:
856
+ * - 新增(opType 'add'):仅 `newSchema`(新 schema);
857
+ * - 更新(opType 'update'):`oldSchema` + `newSchema`,并可带 `changeRecords` 做局部更新;
858
+ * - 删除(opType 'remove'):仅 `oldSchema`(删除前 schema)。
801
859
  */
802
- export interface DataSourceStepValue {
860
+ export interface DataSourceStepValue extends BaseStepValue<DataSourceSchema> {
803
861
  /** 关联的数据源 id */
804
862
  id: Id;
805
- /** 变更前的数据源 schema,新增时为 null */
806
- oldSchema: DataSourceSchema | null;
807
- /** 变更后的数据源 schema,删除时为 null */
808
- newSchema: DataSourceSchema | null;
809
- /**
810
- * form 端 propPath/value 列表。撤销/重做时若有则按 propPath 局部更新;
811
- * 缺省才退化为整 schema 替换。新增/删除场景通常无 changeRecords。
812
- */
813
- changeRecords?: ChangeRecord[];
814
- /** 调用方可选传入的人类可读描述,用于历史面板展示;不影响 undo/redo 行为。 */
815
- historyDescription?: string;
816
- /** 操作途径:标记本次变更由哪条交互入口触发,取值见 {@link HistoryOpSource};仅用于历史面板展示与埋点,不影响 undo/redo 行为。 */
817
- source?: HistoryOpSource;
818
- /** 入栈时间戳(毫秒),入栈时自动写入,仅用于历史面板展示。 */
819
- timestamp?: number;
820
863
  }
821
864
  // #endregion DataSourceStepValue
822
865
 
@@ -837,6 +880,45 @@ export interface HistoryState {
837
880
  dataSourceState: Record<Id, UndoRedo<DataSourceStepValue>>;
838
881
  }
839
882
 
883
+ // #region PersistedHistoryState
884
+ /**
885
+ * 历史记录的可持久化快照。由 historyService.saveToIndexedDB 写入 IndexedDB,
886
+ * 再由 historyService.restoreFromIndexedDB 读出并重建各 UndoRedo 栈。
887
+ */
888
+ export interface PersistedHistoryState {
889
+ /** 快照结构版本号,便于后续兼容升级。 */
890
+ version: number;
891
+ /** 保存时的活动页 id。 */
892
+ pageId?: Id;
893
+ /** 各页面历史栈的序列化快照,按 pageId 分组。 */
894
+ pageSteps: Record<Id, SerializedUndoRedo<StepValue>>;
895
+ /** 各代码块历史栈的序列化快照,按 codeBlockId 分组。 */
896
+ codeBlockState: Record<Id, SerializedUndoRedo<CodeBlockStepValue>>;
897
+ /** 各数据源历史栈的序列化快照,按 dataSourceId 分组。 */
898
+ dataSourceState: Record<Id, SerializedUndoRedo<DataSourceStepValue>>;
899
+ /** 保存时间戳(毫秒)。 */
900
+ savedAt: number;
901
+ }
902
+ // #endregion PersistedHistoryState
903
+
904
+ // #region HistoryPersistOptions
905
+ /** historyService 持久化相关 API 的可选配置。 */
906
+ export interface HistoryPersistOptions {
907
+ /** IndexedDB 数据库名,默认 `tmagic-editor`(最终库名会拼上当前 DSL app id)。 */
908
+ dbName?: string;
909
+ /** objectStore 名,默认 `history`。 */
910
+ storeName?: string;
911
+ /** 记录 key,用于区分不同活动页 / 项目,默认 `default`。 */
912
+ key?: IDBValidKey;
913
+ /**
914
+ * 显式指定用于库名隔离的 DSL app id。
915
+ * 缺省时回退到当前 editorService 的 `root.id`;在「先恢复历史再 set root」场景下 root 尚未设置,
916
+ * 需由调用方(如从待加载 DSL 取 id)显式传入,否则会读 / 写到未按 app 隔离的默认库。
917
+ */
918
+ appId?: Id;
919
+ }
920
+ // #endregion HistoryPersistOptions
921
+
840
922
  // #region HistoryListEntry
841
923
  /**
842
924
  * 历史面板用:当前页面的一条历史步骤(包含位置和是否已应用)。
@@ -1074,6 +1156,8 @@ export type SyncHookPlugin<
1074
1156
 
1075
1157
  export interface EventBusEvent {
1076
1158
  'edit-data-source': [id: string];
1159
+ 'edit-data-source-method': [id: string, methodName: string];
1160
+ 'edit-data-source-field': [id: string, fieldPath: string[]];
1077
1161
  'remove-data-source': [id: string];
1078
1162
  'edit-code': [id: string];
1079
1163
  }
@@ -1107,7 +1191,11 @@ export type CustomContentMenuFunction = (
1107
1191
  ) => (MenuButton | MenuComponent)[];
1108
1192
 
1109
1193
  export interface EditorEvents {
1110
- 'root-change': [value: StoreState['root'], preValue?: StoreState['root']];
1194
+ 'root-change': [
1195
+ value: StoreState['root'],
1196
+ preValue?: StoreState['root'],
1197
+ options?: { historySource?: HistoryOpSource },
1198
+ ];
1111
1199
  select: [node: MNode | null];
1112
1200
  add: [nodes: MNode[]];
1113
1201
  remove: [nodes: MNode[]];
@@ -1202,3 +1290,34 @@ export interface DiffDialogPayload {
1202
1290
  /** 用于标题展示的目标 id */
1203
1291
  id?: string | number;
1204
1292
  }
1293
+
1294
+ /**
1295
+ * 一组「描述 + 可操作性」的判定函数集合。页面 / 数据源 / 代码块及业务自定义历史
1296
+ * 各自实现一份,作为整体注入,避免把 describe* / isStep* 拆成多个独立 props 反复透传。
1297
+ */
1298
+ export interface HistoryRowDescriptor<T extends BaseStepValue = BaseStepValue> {
1299
+ /** 组级描述文案生成器,接收一个 group,返回展示文本。 */
1300
+ describeGroup: (_group: any) => string;
1301
+ /** 单步描述文案生成器,接收一个 step,返回展示文本(合并组展开后的子步列表用)。 */
1302
+ describeStep: (_step: T) => string;
1303
+ /** 判断某个 step 是否可查看差异(前后值都存在)。不传则一律不展示差异入口。 */
1304
+ isStepDiffable?: (_step: T) => boolean;
1305
+ /** 判断某个 step 是否支持回滚(如更新需带 changeRecords)。不传则已应用即可回滚。 */
1306
+ isStepRevertable?: (_step: T) => boolean;
1307
+ }
1308
+
1309
+ /**
1310
+ * 通用 bucket(数据源 / 代码块 / 业务自定义历史)的整体渲染配置。
1311
+ * 把原先散落在 Bucket / BucketTab 上的 title / prefix / describe* / isStep* / showInitial / gotoEnabled
1312
+ * 收敛成一个对象作为单一 prop 传递,调用方一次配齐、组件内部按需读取。
1313
+ */
1314
+ export interface HistoryBucketConfig<T extends BaseStepValue = BaseStepValue> extends HistoryRowDescriptor<T> {
1315
+ /** bucket 头部标题,例如 "数据源" / "代码块"。 */
1316
+ title: string;
1317
+ /** 子项 key 的命名空间前缀(`ds` 数据源 / `cb` 代码块 / 业务自定义如 `mod`)。 */
1318
+ prefix: string;
1319
+ /** 是否展示底部「回到初始状态」入口,默认 true。无 undo cursor 语义的自定义历史可传 false。 */
1320
+ showInitial?: boolean;
1321
+ /** 是否支持「跳转到该记录」(goto),默认 true。 */
1322
+ gotoEnabled?: boolean;
1323
+ }
@@ -10,6 +10,7 @@ const dataSourceFormConfig: TabConfig = {
10
10
  items: [
11
11
  {
12
12
  title: '数据定义',
13
+ status: 'fields',
13
14
  items: [
14
15
  {
15
16
  name: 'fields',
@@ -20,6 +21,7 @@ const dataSourceFormConfig: TabConfig = {
20
21
  },
21
22
  {
22
23
  title: '方法定义',
24
+ status: 'methods',
23
25
  items: [
24
26
  {
25
27
  name: 'methods',