@tmagic/editor 1.8.0-beta.1 → 1.8.0-beta.2

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 (119) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +11 -2
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +18 -110
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +6 -0
  4. package/dist/es/components/CompareForm.js +5 -0
  5. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +167 -0
  6. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +19 -3
  7. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +18 -2
  8. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +37 -6
  9. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +3 -1
  10. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +2 -2
  11. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +14 -6
  12. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +3 -1
  13. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +15 -7
  14. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +14 -6
  15. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +3 -1
  16. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +58 -11
  17. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +11 -6
  18. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +8 -5
  19. package/dist/es/hooks/use-code-block-edit.js +2 -2
  20. package/dist/es/hooks/use-stage.js +15 -7
  21. package/dist/es/index.js +3 -1
  22. package/dist/es/initService.js +6 -6
  23. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +4 -0
  24. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +12 -7
  25. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +8 -0
  26. package/dist/es/layouts/history-list/Bucket.js +5 -0
  27. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +84 -0
  28. package/dist/es/layouts/history-list/CodeBlockTab.js +5 -0
  29. package/dist/es/layouts/history-list/CodeBlockTab.vue_vue_type_script_setup_true_lang.js +62 -0
  30. package/dist/es/layouts/history-list/DataSourceTab.js +5 -0
  31. package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +62 -0
  32. package/dist/es/layouts/history-list/GroupRow.js +5 -0
  33. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +180 -0
  34. package/dist/es/layouts/history-list/HistoryDiffDialog.js +5 -0
  35. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +195 -0
  36. package/dist/es/layouts/history-list/HistoryListPanel.js +5 -0
  37. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +270 -0
  38. package/dist/es/layouts/history-list/InitialRow.js +5 -0
  39. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +48 -0
  40. package/dist/es/layouts/history-list/PageTab.js +5 -0
  41. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +99 -0
  42. package/dist/es/layouts/history-list/composables.js +154 -0
  43. package/dist/es/layouts/sidebar/layer/use-node-status.js +1 -1
  44. package/dist/es/services/BaseService.js +31 -68
  45. package/dist/es/services/codeBlock.js +205 -8
  46. package/dist/es/services/componentList.js +2 -2
  47. package/dist/es/services/dataSource.js +217 -8
  48. package/dist/es/services/dep.js +21 -9
  49. package/dist/es/services/editor.js +350 -160
  50. package/dist/es/services/events.js +2 -2
  51. package/dist/es/services/history.js +407 -12
  52. package/dist/es/services/keybinding.js +2 -2
  53. package/dist/es/services/props.js +8 -8
  54. package/dist/es/services/stageOverlay.js +2 -2
  55. package/dist/es/services/storage.js +2 -2
  56. package/dist/es/services/ui.js +2 -2
  57. package/dist/es/style.css +347 -1
  58. package/dist/es/utils/code-block.js +122 -0
  59. package/dist/es/utils/content-menu.js +4 -5
  60. package/dist/es/utils/dep/worker.js +1 -1
  61. package/dist/es/utils/editor.js +2 -2
  62. package/dist/es/utils/undo-redo.js +20 -0
  63. package/dist/style.css +347 -1
  64. package/dist/tmagic-editor.umd.cjs +3165 -774
  65. package/package.json +7 -7
  66. package/src/Editor.vue +7 -0
  67. package/src/components/CodeBlockEditor.vue +20 -109
  68. package/src/components/CodeParams.vue +6 -0
  69. package/src/components/CompareForm.vue +225 -0
  70. package/src/editorProps.ts +2 -0
  71. package/src/fields/Code.vue +19 -3
  72. package/src/fields/CodeSelect.vue +17 -1
  73. package/src/fields/CodeSelectCol.vue +30 -2
  74. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +7 -1
  75. package/src/fields/DataSourceFieldSelect/Index.vue +2 -2
  76. package/src/fields/DataSourceFields.vue +12 -3
  77. package/src/fields/DataSourceMethodSelect.vue +4 -1
  78. package/src/fields/DataSourceMethods.vue +14 -4
  79. package/src/fields/DataSourceMocks.vue +13 -3
  80. package/src/fields/DataSourceSelect.vue +4 -1
  81. package/src/fields/EventSelect.vue +57 -8
  82. package/src/fields/KeyValue.vue +17 -4
  83. package/src/fields/UISelect.vue +5 -2
  84. package/src/hooks/use-code-block-edit.ts +5 -2
  85. package/src/hooks/use-stage.ts +23 -7
  86. package/src/index.ts +1 -0
  87. package/src/layouts/CodeEditor.vue +15 -0
  88. package/src/layouts/Framework.vue +10 -5
  89. package/src/layouts/NavMenu.vue +9 -0
  90. package/src/layouts/history-list/Bucket.vue +99 -0
  91. package/src/layouts/history-list/CodeBlockTab.vue +61 -0
  92. package/src/layouts/history-list/DataSourceTab.vue +61 -0
  93. package/src/layouts/history-list/GroupRow.vue +207 -0
  94. package/src/layouts/history-list/HistoryDiffDialog.vue +212 -0
  95. package/src/layouts/history-list/HistoryListPanel.vue +271 -0
  96. package/src/layouts/history-list/InitialRow.vue +41 -0
  97. package/src/layouts/history-list/PageTab.vue +94 -0
  98. package/src/layouts/history-list/composables.ts +195 -0
  99. package/src/layouts/sidebar/layer/use-node-status.ts +9 -6
  100. package/src/services/BaseService.ts +35 -86
  101. package/src/services/codeBlock.ts +256 -5
  102. package/src/services/dataSource.ts +257 -6
  103. package/src/services/dep.ts +32 -9
  104. package/src/services/editor.ts +454 -171
  105. package/src/services/history.ts +452 -8
  106. package/src/services/props.ts +2 -2
  107. package/src/theme/history-list-panel.scss +423 -0
  108. package/src/theme/theme.scss +1 -0
  109. package/src/type.ts +179 -6
  110. package/src/utils/code-block.ts +150 -0
  111. package/src/utils/content-menu.ts +8 -5
  112. package/src/utils/dep/worker.ts +5 -3
  113. package/src/utils/index.ts +1 -0
  114. package/src/utils/undo-redo.ts +23 -0
  115. package/types/index.d.ts +2105 -810
  116. package/dist/es/utils/compose.js +0 -44
  117. package/dist/es/utils/editor-history.js +0 -100
  118. package/src/utils/compose.ts +0 -52
  119. package/src/utils/editor-history.ts +0 -138
@@ -0,0 +1,270 @@
1
+ import { useServices } from "../../hooks/use-services.js";
2
+ import Icon_default from "../../components/Icon.js";
3
+ import { useHistoryList } from "./composables.js";
4
+ import CodeBlockTab_default from "./CodeBlockTab.js";
5
+ import DataSourceTab_default from "./DataSourceTab.js";
6
+ import HistoryDiffDialog_default from "./HistoryDiffDialog.js";
7
+ import PageTab_default from "./PageTab.js";
8
+ import { TMagicButton, TMagicPopover, TMagicTabs, TMagicTooltip, getDesignConfig } from "@tmagic/design";
9
+ import { Fragment, createBlock, createElementBlock, createElementVNode, createVNode, defineComponent, guardReactiveProps, inject, markRaw, normalizeProps, openBlock, ref, resolveDynamicComponent, unref, useTemplateRef, withCtx } from "vue";
10
+ import { Clock, Close } from "@element-plus/icons-vue";
11
+ //#region packages/editor/src/layouts/history-list/HistoryListPanel.vue?vue&type=script&setup=true&lang.ts
12
+ var _hoisted_1 = { class: "m-editor-history-list" };
13
+ var HistoryListPanel_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
14
+ name: "MEditorHistoryListPanel",
15
+ __name: "HistoryListPanel",
16
+ setup(__props) {
17
+ /**
18
+ * 历史记录面板:在顶部 NavMenu 上点击图标打开 popover,分三个 tab:
19
+ * - 页面:当前活动页面的历史栈,连续修改同一节点的多步会被合并成一组
20
+ * - 数据源:以 dataSource.id 分组,每组内部相邻的连续 update 自动合并
21
+ * - 代码块:同上,按 codeBlock.id 分组并合并相邻 update
22
+ *
23
+ * 数据通过 historyService 暴露的聚合 API 读取,UI 仅用于只读展示,
24
+ * 同时支持点击任意一条记录跳转至该状态:
25
+ * - 页面 tab:调用 editorService.gotoPageStep(targetCursor)
26
+ * - 数据源 tab:调用 dataSourceService.goto(id, targetCursor)
27
+ * - 代码块 tab:调用 codeBlockService.goto(id, targetCursor)
28
+ *
29
+ * 这里的 targetCursor = 用户点击的 step.index + 1,即"应用至此步完成的状态"。
30
+ *
31
+ * 此外每条 step 上提供"查看差异"入口(仅在前后值都存在的 update 步骤显示),
32
+ * 点击后弹出 HistoryDiffDialog,使用 CompareForm 组件以表单形式展示新旧值差异。
33
+ *
34
+ * 各 tab 的内容拆分为独立的 SFC(PageTab / DataSourceTab / CodeBlockTab),
35
+ * 共享的描述生成与折叠状态在 composables.ts 中维护。
36
+ */
37
+ const ClockIcon = markRaw(Clock);
38
+ const CloseIcon = markRaw(Close);
39
+ const activeTab = ref("page");
40
+ /** 面板显隐受控:reference 图标点击切换,右上角关闭按钮置为 false。 */
41
+ const visible = ref(false);
42
+ const tabPaneComponent = getDesignConfig("components")?.tabPane;
43
+ const { editorService, dataSourceService, codeBlockService, historyService } = useServices();
44
+ /**
45
+ * 通过 inject 拿到 Editor 顶层注入的 `extendFormState`,转交给 HistoryDiffDialog
46
+ * 内部的 CompareForm,使差异对比表单的 filterFunction 能拿到完整的业务上下文。
47
+ * 未提供时为 undefined,CompareForm/MForm 会跳过 extendState 处理。
48
+ */
49
+ const extendFormState = inject("extendFormState", void 0);
50
+ const { expanded, toggleGroup, pageGroups, dataSourceGroups, codeBlockGroups, pageGroupsDisplay, dataSourceGroupsByTarget, codeBlockGroupsByTarget } = useHistoryList();
51
+ /** 把"目标 step 索引"翻译成"目标 cursor"(已应用步骤数量)。 */
52
+ const indexToCursor = (index) => index + 1;
53
+ const onPageGoto = (index) => {
54
+ editorService.gotoPageStep(indexToCursor(index));
55
+ };
56
+ const onDataSourceGoto = (id, index) => {
57
+ dataSourceService.goto(id, indexToCursor(index));
58
+ };
59
+ const onCodeBlockGoto = (id, index) => {
60
+ codeBlockService.goto(id, indexToCursor(index));
61
+ };
62
+ /**
63
+ * "回到初始状态" = 把对应栈 cursor 移到 0(全部已撤销)。
64
+ * 复用 service.goto*(0) 即可,所有真实 step 的反向应用由 service 层的 undo 链路完成。
65
+ */
66
+ const onPageGotoInitial = () => {
67
+ editorService.gotoPageStep(0);
68
+ };
69
+ const onDataSourceGotoInitial = (id) => {
70
+ dataSourceService.goto(id, 0);
71
+ };
72
+ const onCodeBlockGotoInitial = (id) => {
73
+ codeBlockService.goto(id, 0);
74
+ };
75
+ /**
76
+ * 「回滚」入口:把目标历史步骤的修改作为一次新操作反向应用(类 git revert),
77
+ * 不破坏原有栈结构。各 service 内部完成反向 + 入栈,并自带描述用于面板展示。
78
+ */
79
+ const onPageRevert = (index) => {
80
+ editorService.revertPageStep(index);
81
+ };
82
+ const onDataSourceRevert = (id, index) => {
83
+ dataSourceService.revert(id, index);
84
+ };
85
+ const onCodeBlockRevert = (id, index) => {
86
+ codeBlockService.revert(id, index);
87
+ };
88
+ const diffDialogRef = useTemplateRef("diffDialog");
89
+ /**
90
+ * 页面 step 差异:仅 update 单节点修改可对比,传入旧/新节点。
91
+ * 节点类型 `type` 优先取 newNode.type,再回退 oldNode.type。
92
+ * `currentValue` 取自 editorService 中该节点当前实际值,用于支持「与当前对比」。
93
+ */
94
+ const onPageDiff = (index) => {
95
+ const groups = historyService.getPageHistoryGroups();
96
+ for (const group of groups) {
97
+ const entry = group.steps.find((s) => s.index === index);
98
+ if (!entry) continue;
99
+ const item = entry.step.updatedItems?.[0];
100
+ if (!item?.oldNode || !item?.newNode) return;
101
+ const type = item.newNode.type || item.oldNode.type || "";
102
+ const nodeId = item.newNode.id ?? item.oldNode.id;
103
+ const currentNode = nodeId !== void 0 ? editorService.getNodeById(nodeId) : null;
104
+ diffDialogRef.value?.open({
105
+ category: "node",
106
+ type,
107
+ lastValue: item.oldNode,
108
+ value: item.newNode,
109
+ currentValue: currentNode || null,
110
+ targetLabel: item.newNode.name || item.oldNode.name || type,
111
+ id: nodeId
112
+ });
113
+ return;
114
+ }
115
+ };
116
+ const onDataSourceDiff = (id, index) => {
117
+ const groups = historyService.getDataSourceHistoryGroups();
118
+ for (const group of groups) {
119
+ if (group.id !== id) continue;
120
+ const entry = group.steps.find((s) => s.index === index);
121
+ if (!entry) continue;
122
+ const { oldSchema, newSchema } = entry.step;
123
+ if (!oldSchema || !newSchema) return;
124
+ const currentSchema = dataSourceService.getDataSourceById(`${id}`);
125
+ diffDialogRef.value?.open({
126
+ category: "data-source",
127
+ type: newSchema.type || oldSchema.type || "base",
128
+ lastValue: oldSchema,
129
+ value: newSchema,
130
+ currentValue: currentSchema || null,
131
+ targetLabel: newSchema.title || oldSchema.title || `${id}`,
132
+ id
133
+ });
134
+ return;
135
+ }
136
+ };
137
+ const onCodeBlockDiff = (id, index) => {
138
+ const groups = historyService.getCodeBlockHistoryGroups();
139
+ for (const group of groups) {
140
+ if (group.id !== id) continue;
141
+ const entry = group.steps.find((s) => s.index === index);
142
+ if (!entry) continue;
143
+ const { oldContent, newContent } = entry.step;
144
+ if (!oldContent || !newContent) return;
145
+ const currentContent = codeBlockService.getCodeContentById(id);
146
+ diffDialogRef.value?.open({
147
+ category: "code-block",
148
+ lastValue: oldContent,
149
+ value: newContent,
150
+ currentValue: currentContent || null,
151
+ targetLabel: newContent.name || oldContent.name || `${id}`,
152
+ id
153
+ });
154
+ return;
155
+ }
156
+ };
157
+ return (_ctx, _cache) => {
158
+ return openBlock(), createElementBlock(Fragment, null, [createVNode(unref(TMagicPopover), {
159
+ "popper-class": "m-editor-history-list-popover",
160
+ placement: "bottom",
161
+ trigger: "click",
162
+ visible: visible.value,
163
+ width: 660
164
+ }, {
165
+ reference: withCtx(() => [createVNode(unref(TMagicTooltip), {
166
+ effect: "dark",
167
+ placement: "bottom",
168
+ content: "历史记录"
169
+ }, {
170
+ default: withCtx(() => [createVNode(unref(TMagicButton), {
171
+ size: "small",
172
+ link: "",
173
+ onClick: _cache[2] || (_cache[2] = ($event) => visible.value = !visible.value)
174
+ }, {
175
+ icon: withCtx(() => [createVNode(Icon_default, { icon: unref(ClockIcon) }, null, 8, ["icon"])]),
176
+ _: 1
177
+ })]),
178
+ _: 1
179
+ })]),
180
+ default: withCtx(() => [createElementVNode("div", _hoisted_1, [createVNode(unref(TMagicTooltip), {
181
+ effect: "dark",
182
+ placement: "top",
183
+ content: "关闭"
184
+ }, {
185
+ default: withCtx(() => [createVNode(unref(TMagicButton), {
186
+ class: "m-editor-history-list-close",
187
+ size: "small",
188
+ link: "",
189
+ onClick: _cache[0] || (_cache[0] = ($event) => visible.value = false)
190
+ }, {
191
+ icon: withCtx(() => [createVNode(Icon_default, { icon: unref(CloseIcon) }, null, 8, ["icon"])]),
192
+ _: 1
193
+ })]),
194
+ _: 1
195
+ }), createVNode(unref(TMagicTabs), {
196
+ modelValue: activeTab.value,
197
+ "onUpdate:modelValue": _cache[1] || (_cache[1] = ($event) => activeTab.value = $event),
198
+ class: "m-editor-history-list-tabs"
199
+ }, {
200
+ default: withCtx(() => [
201
+ (openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), normalizeProps(guardReactiveProps(unref(tabPaneComponent)?.props({
202
+ name: "page",
203
+ label: `页面 (${unref(pageGroups).length})`
204
+ }) || {})), {
205
+ default: withCtx(() => [createVNode(PageTab_default, {
206
+ list: unref(pageGroupsDisplay),
207
+ expanded: unref(expanded),
208
+ onToggle: unref(toggleGroup),
209
+ onGoto: onPageGoto,
210
+ onGotoInitial: onPageGotoInitial,
211
+ onDiffStep: onPageDiff,
212
+ onRevertStep: onPageRevert
213
+ }, null, 8, [
214
+ "list",
215
+ "expanded",
216
+ "onToggle"
217
+ ])]),
218
+ _: 1
219
+ }, 16)),
220
+ (openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), normalizeProps(guardReactiveProps(unref(tabPaneComponent)?.props({
221
+ name: "data-source",
222
+ label: `数据源 (${unref(dataSourceGroups).length})`
223
+ }) || {})), {
224
+ default: withCtx(() => [createVNode(DataSourceTab_default, {
225
+ buckets: unref(dataSourceGroupsByTarget),
226
+ expanded: unref(expanded),
227
+ onToggle: unref(toggleGroup),
228
+ onGoto: onDataSourceGoto,
229
+ onGotoInitial: onDataSourceGotoInitial,
230
+ onDiffStep: onDataSourceDiff,
231
+ onRevertStep: onDataSourceRevert
232
+ }, null, 8, [
233
+ "buckets",
234
+ "expanded",
235
+ "onToggle"
236
+ ])]),
237
+ _: 1
238
+ }, 16)),
239
+ (openBlock(), createBlock(resolveDynamicComponent(unref(tabPaneComponent)?.component || "el-tab-pane"), normalizeProps(guardReactiveProps(unref(tabPaneComponent)?.props({
240
+ name: "code-block",
241
+ label: `代码块 (${unref(codeBlockGroups).length})`
242
+ }) || {})), {
243
+ default: withCtx(() => [createVNode(CodeBlockTab_default, {
244
+ buckets: unref(codeBlockGroupsByTarget),
245
+ expanded: unref(expanded),
246
+ onToggle: unref(toggleGroup),
247
+ onGoto: onCodeBlockGoto,
248
+ onGotoInitial: onCodeBlockGotoInitial,
249
+ onDiffStep: onCodeBlockDiff,
250
+ onRevertStep: onCodeBlockRevert
251
+ }, null, 8, [
252
+ "buckets",
253
+ "expanded",
254
+ "onToggle"
255
+ ])]),
256
+ _: 1
257
+ }, 16))
258
+ ]),
259
+ _: 1
260
+ }, 8, ["modelValue"])])]),
261
+ _: 1
262
+ }, 8, ["visible"]), createVNode(HistoryDiffDialog_default, {
263
+ ref: "diffDialog",
264
+ "extend-state": unref(extendFormState)
265
+ }, null, 8, ["extend-state"])], 64);
266
+ };
267
+ }
268
+ });
269
+ //#endregion
270
+ export { HistoryListPanel_vue_vue_type_script_setup_true_lang_default as default };
@@ -0,0 +1,5 @@
1
+ import InitialRow_vue_vue_type_script_setup_true_lang_default from "./InitialRow.vue_vue_type_script_setup_true_lang.js";
2
+ //#region packages/editor/src/layouts/history-list/InitialRow.vue
3
+ var InitialRow_default = InitialRow_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { InitialRow_default as default };
@@ -0,0 +1,48 @@
1
+ import { createCommentVNode, createElementBlock, createElementVNode, defineComponent, normalizeClass, openBlock } from "vue";
2
+ //#region packages/editor/src/layouts/history-list/InitialRow.vue?vue&type=script&setup=true&lang.ts
3
+ var _hoisted_1 = ["title"];
4
+ var _hoisted_2 = {
5
+ key: 0,
6
+ class: "m-editor-history-list-item-current"
7
+ };
8
+ var InitialRow_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
9
+ name: "MEditorHistoryListInitialRow",
10
+ __name: "InitialRow",
11
+ props: { isCurrent: { type: Boolean } },
12
+ emits: ["goto-initial"],
13
+ setup(__props, { emit: __emit }) {
14
+ /**
15
+ * 「初始状态」记录行:渲染于历史列表底部,作为整个栈的"零点"。
16
+ * - 点击该行会把对应栈撤销到 cursor === 0(即没有任何已应用步骤),等同于回到所有修改之前。
17
+ * - 当对应栈本身已处于 cursor === 0 时(isCurrent=true),用户已在初始状态,点击不再触发动作。
18
+ *
19
+ * 该行不是真实 step,仅作为 UI 入口;上层负责把"点击"翻译为 `service.goto*(0)`。
20
+ */
21
+ const props = __props;
22
+ const emit = __emit;
23
+ const onClick = () => {
24
+ if (props.isCurrent) return;
25
+ emit("goto-initial");
26
+ };
27
+ return (_ctx, _cache) => {
28
+ return openBlock(), createElementBlock("li", {
29
+ class: normalizeClass(["m-editor-history-list-item m-editor-history-list-initial", {
30
+ "is-current": __props.isCurrent,
31
+ "is-clickable": !__props.isCurrent
32
+ }]),
33
+ title: __props.isCurrent ? "当前已回到未修改的初始状态" : "点击回到未修改的初始状态",
34
+ onClick
35
+ }, [
36
+ _cache[0] || (_cache[0] = createElementVNode("span", {
37
+ class: "m-editor-history-list-item-index",
38
+ title: "历史步骤编号 #0(未修改的初始状态)"
39
+ }, "#0", -1)),
40
+ _cache[1] || (_cache[1] = createElementVNode("span", { class: "m-editor-history-list-item-op op-initial" }, "初始", -1)),
41
+ _cache[2] || (_cache[2] = createElementVNode("span", { class: "m-editor-history-list-item-desc" }, "未修改的初始状态", -1)),
42
+ __props.isCurrent ? (openBlock(), createElementBlock("span", _hoisted_2, "当前")) : createCommentVNode("v-if", true)
43
+ ], 10, _hoisted_1);
44
+ };
45
+ }
46
+ });
47
+ //#endregion
48
+ export { InitialRow_vue_vue_type_script_setup_true_lang_default as default };
@@ -0,0 +1,5 @@
1
+ import PageTab_vue_vue_type_script_setup_true_lang_default from "./PageTab.vue_vue_type_script_setup_true_lang.js";
2
+ //#region packages/editor/src/layouts/history-list/PageTab.vue
3
+ var PageTab_default = PageTab_vue_vue_type_script_setup_true_lang_default;
4
+ //#endregion
5
+ export { PageTab_default as default };
@@ -0,0 +1,99 @@
1
+ import { describePageGroup, describePageStep } from "./composables.js";
2
+ import GroupRow_default from "./GroupRow.js";
3
+ import InitialRow_default from "./InitialRow.js";
4
+ import { TMagicScrollbar } from "@tmagic/design";
5
+ import { Fragment, computed, createBlock, createCommentVNode, createElementBlock, createElementVNode, createVNode, defineComponent, openBlock, renderList, unref, withCtx } from "vue";
6
+ //#region packages/editor/src/layouts/history-list/PageTab.vue?vue&type=script&setup=true&lang.ts
7
+ var _hoisted_1 = {
8
+ key: 0,
9
+ class: "m-editor-history-list-empty"
10
+ };
11
+ var _hoisted_2 = { class: "m-editor-history-list-ul" };
12
+ var PageTab_vue_vue_type_script_setup_true_lang_default = /* @__PURE__ */ defineComponent({
13
+ name: "MEditorHistoryListPageTab",
14
+ __name: "PageTab",
15
+ props: {
16
+ list: {},
17
+ expanded: {}
18
+ },
19
+ emits: [
20
+ "toggle",
21
+ "goto",
22
+ "goto-initial",
23
+ "diff-step",
24
+ "revert-step"
25
+ ],
26
+ setup(__props) {
27
+ const props = __props;
28
+ /**
29
+ * 当前 step 是否可查看差异:
30
+ * - 仅 update 操作;
31
+ * - 单节点更新(updatedItems.length === 1),且 oldNode / newNode 都存在。
32
+ * 多节点更新难以选定单一对比目标,统一不展示差异入口。
33
+ */
34
+ const isPageStepDiffable = (step) => {
35
+ if (step.opType !== "update") return false;
36
+ const items = step.updatedItems ?? [];
37
+ if (items.length !== 1) return false;
38
+ return Boolean(items[0]?.oldNode && items[0]?.newNode);
39
+ };
40
+ /**
41
+ * 是否处于"初始状态"——即对应页面历史栈 cursor===0:
42
+ * 当 list 中所有 group 的 applied 都为 false 时即为该状态。
43
+ * 没有任何 group 的情况由外层"暂无操作记录"分支兜底,本计算可以不考虑。
44
+ */
45
+ const isInitial = computed(() => props.list.length > 0 && props.list.every((g) => !g.applied));
46
+ return (_ctx, _cache) => {
47
+ return !__props.list.length ? (openBlock(), createElementBlock("div", _hoisted_1, "暂无操作记录")) : (openBlock(), createBlock(unref(TMagicScrollbar), {
48
+ key: 1,
49
+ "max-height": "360px"
50
+ }, {
51
+ default: withCtx(() => [createElementVNode("ul", _hoisted_2, [
52
+ (openBlock(true), createElementBlock(Fragment, null, renderList(__props.list, (group, gIdx) => {
53
+ return openBlock(), createBlock(GroupRow_default, {
54
+ key: `pg-${gIdx}`,
55
+ "group-key": `pg-${gIdx}`,
56
+ applied: group.applied,
57
+ merged: group.steps.length > 1,
58
+ "op-type": group.opType,
59
+ desc: unref(describePageGroup)(group),
60
+ "step-count": group.steps.length,
61
+ "sub-steps": group.steps.map((s) => ({
62
+ index: s.index,
63
+ applied: s.applied,
64
+ isCurrent: s.isCurrent,
65
+ desc: unref(describePageStep)(s.step),
66
+ diffable: isPageStepDiffable(s.step),
67
+ revertable: s.applied
68
+ })),
69
+ "is-current": group.isCurrent,
70
+ expanded: !!__props.expanded[`pg-${gIdx}`],
71
+ onToggle: _cache[0] || (_cache[0] = (key) => _ctx.$emit("toggle", key)),
72
+ onGoto: _cache[1] || (_cache[1] = (index) => _ctx.$emit("goto", index)),
73
+ onDiffStep: _cache[2] || (_cache[2] = (index) => _ctx.$emit("diff-step", index)),
74
+ onRevertStep: _cache[3] || (_cache[3] = (index) => _ctx.$emit("revert-step", index))
75
+ }, null, 8, [
76
+ "group-key",
77
+ "applied",
78
+ "merged",
79
+ "op-type",
80
+ "desc",
81
+ "step-count",
82
+ "sub-steps",
83
+ "is-current",
84
+ "expanded"
85
+ ]);
86
+ }), 128)),
87
+ createCommentVNode("\n 初始状态项:永远位于列表底部(页面 tab 倒序展示,最底部=最早),\n 作为\"未修改\"零点。当所有 group 都未 applied 时它即为当前位置。\n "),
88
+ createVNode(InitialRow_default, {
89
+ "is-current": isInitial.value,
90
+ onGotoInitial: _cache[4] || (_cache[4] = ($event) => _ctx.$emit("goto-initial"))
91
+ }, null, 8, ["is-current"])
92
+ ])]),
93
+ _: 1
94
+ }));
95
+ };
96
+ }
97
+ });
98
+ //#endregion
99
+ export { PageTab_vue_vue_type_script_setup_true_lang_default as default };
@@ -0,0 +1,154 @@
1
+ import { useServices } from "../../hooks/use-services.js";
2
+ import { computed, reactive } from "vue";
3
+ //#region packages/editor/src/layouts/history-list/composables.ts
4
+ /**
5
+ * 历史记录面板共享逻辑:
6
+ * - 暴露三类历史的聚合数据(页面 / 数据源 / 代码块);
7
+ * - 提供折叠状态管理;
8
+ * - 提供操作描述文案生成器。
9
+ *
10
+ * 所有数据基于 historyService 的 reactive state 派生,自动跟随历史变化刷新。
11
+ */
12
+ var useHistoryList = () => {
13
+ const { historyService } = useServices();
14
+ /** 折叠状态:key 形如 `pg-${groupIdx}` / `ds-${id}-${groupIdx}` / `cb-${id}-${groupIdx}`。 */
15
+ const expanded = reactive({});
16
+ const toggleGroup = (key) => {
17
+ expanded[key] = !expanded[key];
18
+ };
19
+ const pageGroups = computed(() => historyService.getPageHistoryGroups());
20
+ const dataSourceGroups = computed(() => historyService.getDataSourceHistoryGroups());
21
+ const codeBlockGroups = computed(() => historyService.getCodeBlockHistoryGroups());
22
+ /** 页面 tab 倒序展示(最新一组在最上面)。 */
23
+ const pageGroupsDisplay = computed(() => pageGroups.value.slice().reverse());
24
+ /**
25
+ * 把按时间正序的 group 列表,再按 id 聚拢成 bucket(同 id 的所有分组放一起)。
26
+ * 每个 bucket 内部仍然按时间倒序展示(最近的操作最先看到)。
27
+ */
28
+ const groupByTarget = (groups) => {
29
+ const map = /* @__PURE__ */ new Map();
30
+ groups.forEach((g) => {
31
+ const list = map.get(g.id) ?? [];
32
+ list.push(g);
33
+ map.set(g.id, list);
34
+ });
35
+ return Array.from(map.entries()).map(([id, gs]) => ({
36
+ id,
37
+ groups: gs.slice().reverse()
38
+ }));
39
+ };
40
+ return {
41
+ expanded,
42
+ toggleGroup,
43
+ pageGroups,
44
+ dataSourceGroups,
45
+ codeBlockGroups,
46
+ pageGroupsDisplay,
47
+ dataSourceGroupsByTarget: computed(() => groupByTarget(dataSourceGroups.value)),
48
+ codeBlockGroupsByTarget: computed(() => groupByTarget(codeBlockGroups.value))
49
+ };
50
+ };
51
+ var opLabel = (op) => {
52
+ switch (op) {
53
+ case "add": return "新增";
54
+ case "remove": return "删除";
55
+ default: return "修改";
56
+ }
57
+ };
58
+ var nameOf = (node) => node?.name || node?.type || `${node?.id ?? ""}`;
59
+ /**
60
+ * 默认描述里展示「名称 (id: xxx)」,便于区分同名实体。
61
+ * - 当未传入 id,或 label 本身就是 id 字符串(即没有 name/type/title 可用)时,仅展示 label,避免出现「123 (id: 123)」。
62
+ */
63
+ var labelWithId = (label, id) => {
64
+ const labelStr = label === void 0 || label === null ? "" : `${label}`;
65
+ if (id === void 0 || id === null || id === "") return labelStr;
66
+ if (labelStr === "" || labelStr === `${id}`) return `${id}`;
67
+ return `${labelStr} (id: ${id})`;
68
+ };
69
+ /** 从一组可选 historyDescription 中取最后一条非空值;都为空时返回 undefined。 */
70
+ var pickLastDescription = (descs) => {
71
+ for (let i = descs.length - 1; i >= 0; i--) if (descs[i]) return descs[i];
72
+ };
73
+ var describePageStep = (step) => {
74
+ if (step.historyDescription) return step.historyDescription;
75
+ const { opType } = step;
76
+ if (opType === "add") {
77
+ const count = step.nodes?.length ?? 0;
78
+ const node = step.nodes?.[0];
79
+ return `新增 ${count} 个节点${count === 1 && node ? `(${labelWithId(nameOf(node), node.id)})` : ""}`;
80
+ }
81
+ if (opType === "remove") {
82
+ const count = step.removedItems?.length ?? 0;
83
+ const node = step.removedItems?.[0]?.node;
84
+ return `删除 ${count} 个节点${count === 1 && node ? `(${labelWithId(nameOf(node), node.id)})` : ""}`;
85
+ }
86
+ const updated = step.updatedItems ?? [];
87
+ if (!updated.length) return "修改节点";
88
+ if (updated.length === 1) {
89
+ const { newNode, changeRecords } = updated[0];
90
+ const propPath = changeRecords?.[0]?.propPath;
91
+ return `修改 ${labelWithId(nameOf(newNode), newNode?.id)}${propPath ? ` · ${propPath}` : ""}`;
92
+ }
93
+ return `修改 ${updated.length} 个节点`;
94
+ };
95
+ /**
96
+ * 合并组的展示文案:
97
+ * - 若组内任一步显式提供了 historyDescription:取最后一条非空 historyDescription(最近一次的描述更准确);
98
+ * - 单步组:复用 describePageStep;
99
+ * - 多步组(连续修改同一节点):展示节点名 + 涉及的前几个 propPath。
100
+ */
101
+ var describePageGroup = (group) => {
102
+ const lastDesc = pickLastDescription(group.steps.map((s) => s.step.historyDescription));
103
+ if (lastDesc) return lastDesc;
104
+ if (group.steps.length === 1) return describePageStep(group.steps[0].step);
105
+ const paths = /* @__PURE__ */ new Set();
106
+ group.steps.forEach((s) => {
107
+ s.step.updatedItems?.[0]?.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
108
+ });
109
+ const pathList = Array.from(paths).slice(0, 3).join(", ");
110
+ const target = labelWithId(group.targetName ?? (group.targetId !== void 0 ? `${group.targetId}` : "节点"), group.targetId);
111
+ return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
112
+ };
113
+ var describeDataSourceStep = (step) => {
114
+ if (step.historyDescription) return step.historyDescription;
115
+ if (step.oldSchema === null && step.newSchema) return `创建 ${labelWithId(step.newSchema.title, step.newSchema.id ?? step.id)}`;
116
+ if (step.newSchema === null && step.oldSchema) return `删除 ${labelWithId(step.oldSchema.title, step.oldSchema.id ?? step.id)}`;
117
+ const propPath = step.changeRecords?.[0]?.propPath;
118
+ const title = labelWithId(step.newSchema?.title || step.oldSchema?.title, step.id);
119
+ return propPath ? `修改 ${title} · ${propPath}` : `修改 ${title}`;
120
+ };
121
+ var describeDataSourceGroup = (group) => {
122
+ const lastDesc = pickLastDescription(group.steps.map((s) => s.step.historyDescription));
123
+ if (lastDesc) return lastDesc;
124
+ if (group.steps.length === 1) return describeDataSourceStep(group.steps[0].step);
125
+ const paths = /* @__PURE__ */ new Set();
126
+ group.steps.forEach((s) => {
127
+ s.step.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
128
+ });
129
+ const pathList = Array.from(paths).slice(0, 3).join(", ");
130
+ const target = labelWithId(group.steps[group.steps.length - 1].step.newSchema?.title || group.steps[0].step.oldSchema?.title, group.id);
131
+ return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
132
+ };
133
+ var describeCodeBlockStep = (step) => {
134
+ if (step.historyDescription) return step.historyDescription;
135
+ if (step.oldContent === null && step.newContent) return `创建 ${labelWithId(step.newContent.name, step.newContent.id ?? step.id)}`;
136
+ if (step.newContent === null && step.oldContent) return `删除 ${labelWithId(step.oldContent.name, step.oldContent.id ?? step.id)}`;
137
+ const propPath = step.changeRecords?.[0]?.propPath;
138
+ const title = labelWithId(step.newContent?.name || step.oldContent?.name, step.id);
139
+ return propPath ? `修改 ${title} · ${propPath}` : `修改 ${title}`;
140
+ };
141
+ var describeCodeBlockGroup = (group) => {
142
+ const lastDesc = pickLastDescription(group.steps.map((s) => s.step.historyDescription));
143
+ if (lastDesc) return lastDesc;
144
+ if (group.steps.length === 1) return describeCodeBlockStep(group.steps[0].step);
145
+ const paths = /* @__PURE__ */ new Set();
146
+ group.steps.forEach((s) => {
147
+ s.step.changeRecords?.forEach((r) => r.propPath && paths.add(r.propPath));
148
+ });
149
+ const pathList = Array.from(paths).slice(0, 3).join(", ");
150
+ const target = labelWithId(group.steps[group.steps.length - 1].step.newContent?.name || group.steps[0].step.oldContent?.name, group.id);
151
+ return pathList ? `修改 ${target} · ${pathList}${paths.size > 3 ? "…" : ""}` : `修改 ${target}`;
152
+ };
153
+ //#endregion
154
+ export { describeCodeBlockGroup, describeCodeBlockStep, describeDataSourceGroup, describeDataSourceStep, describePageGroup, describePageStep, opLabel, useHistoryList };
@@ -4,7 +4,7 @@ import { computed, onBeforeUnmount, ref, watch } from "vue";
4
4
  //#region packages/editor/src/layouts/sidebar/layer/use-node-status.ts
5
5
  var createPageNodeStatus = (page, initialLayerNodeStatus) => {
6
6
  const map = /* @__PURE__ */ new Map();
7
- map.set(page.id, {
7
+ map.set(page.id, initialLayerNodeStatus?.get(page.id) || {
8
8
  visible: true,
9
9
  expand: true,
10
10
  selected: true,