@tmagic/editor 1.8.0-beta.0 → 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 (138) 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 +12 -3
  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/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +8 -2
  19. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +10 -2
  20. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +3 -1
  21. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +10 -2
  22. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +16 -3
  23. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +10 -2
  24. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +14 -2
  25. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +10 -2
  26. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +10 -2
  27. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +8 -5
  28. package/dist/es/hooks/use-code-block-edit.js +2 -2
  29. package/dist/es/hooks/use-stage.js +15 -7
  30. package/dist/es/index.js +3 -1
  31. package/dist/es/initService.js +6 -6
  32. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +4 -0
  33. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +12 -7
  34. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +8 -0
  35. package/dist/es/layouts/history-list/Bucket.js +5 -0
  36. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +84 -0
  37. package/dist/es/layouts/history-list/CodeBlockTab.js +5 -0
  38. package/dist/es/layouts/history-list/CodeBlockTab.vue_vue_type_script_setup_true_lang.js +62 -0
  39. package/dist/es/layouts/history-list/DataSourceTab.js +5 -0
  40. package/dist/es/layouts/history-list/DataSourceTab.vue_vue_type_script_setup_true_lang.js +62 -0
  41. package/dist/es/layouts/history-list/GroupRow.js +5 -0
  42. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +180 -0
  43. package/dist/es/layouts/history-list/HistoryDiffDialog.js +5 -0
  44. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +195 -0
  45. package/dist/es/layouts/history-list/HistoryListPanel.js +5 -0
  46. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +270 -0
  47. package/dist/es/layouts/history-list/InitialRow.js +5 -0
  48. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +48 -0
  49. package/dist/es/layouts/history-list/PageTab.js +5 -0
  50. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +99 -0
  51. package/dist/es/layouts/history-list/composables.js +154 -0
  52. package/dist/es/layouts/sidebar/layer/use-node-status.js +38 -4
  53. package/dist/es/services/BaseService.js +31 -68
  54. package/dist/es/services/codeBlock.js +205 -8
  55. package/dist/es/services/componentList.js +2 -2
  56. package/dist/es/services/dataSource.js +217 -8
  57. package/dist/es/services/dep.js +21 -9
  58. package/dist/es/services/editor.js +350 -160
  59. package/dist/es/services/events.js +2 -2
  60. package/dist/es/services/history.js +407 -12
  61. package/dist/es/services/keybinding.js +2 -2
  62. package/dist/es/services/props.js +8 -8
  63. package/dist/es/services/stageOverlay.js +2 -2
  64. package/dist/es/services/storage.js +2 -2
  65. package/dist/es/services/ui.js +2 -2
  66. package/dist/es/style.css +347 -1
  67. package/dist/es/utils/code-block.js +122 -0
  68. package/dist/es/utils/content-menu.js +4 -5
  69. package/dist/es/utils/dep/worker.js +1 -1
  70. package/dist/es/utils/editor.js +3 -3
  71. package/dist/es/utils/undo-redo.js +20 -0
  72. package/dist/style.css +347 -1
  73. package/dist/tmagic-editor.umd.cjs +3273 -766
  74. package/package.json +7 -7
  75. package/src/Editor.vue +7 -0
  76. package/src/components/CodeBlockEditor.vue +20 -109
  77. package/src/components/CodeParams.vue +6 -0
  78. package/src/components/CompareForm.vue +225 -0
  79. package/src/editorProps.ts +2 -0
  80. package/src/fields/Code.vue +19 -3
  81. package/src/fields/CodeSelect.vue +17 -1
  82. package/src/fields/CodeSelectCol.vue +30 -2
  83. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +7 -1
  84. package/src/fields/DataSourceFieldSelect/Index.vue +9 -2
  85. package/src/fields/DataSourceFields.vue +12 -3
  86. package/src/fields/DataSourceMethodSelect.vue +4 -1
  87. package/src/fields/DataSourceMethods.vue +14 -4
  88. package/src/fields/DataSourceMocks.vue +13 -3
  89. package/src/fields/DataSourceSelect.vue +4 -1
  90. package/src/fields/EventSelect.vue +57 -8
  91. package/src/fields/KeyValue.vue +17 -4
  92. package/src/fields/StyleSetter/Index.vue +6 -0
  93. package/src/fields/StyleSetter/components/Border.vue +15 -1
  94. package/src/fields/StyleSetter/components/Box.vue +2 -0
  95. package/src/fields/StyleSetter/pro/Background.vue +15 -1
  96. package/src/fields/StyleSetter/pro/Border.vue +24 -2
  97. package/src/fields/StyleSetter/pro/Font.vue +15 -1
  98. package/src/fields/StyleSetter/pro/Layout.vue +17 -1
  99. package/src/fields/StyleSetter/pro/Position.vue +15 -1
  100. package/src/fields/StyleSetter/pro/Transform.vue +15 -1
  101. package/src/fields/UISelect.vue +5 -2
  102. package/src/hooks/use-code-block-edit.ts +5 -2
  103. package/src/hooks/use-stage.ts +23 -7
  104. package/src/index.ts +1 -0
  105. package/src/layouts/CodeEditor.vue +15 -0
  106. package/src/layouts/Framework.vue +10 -5
  107. package/src/layouts/NavMenu.vue +9 -0
  108. package/src/layouts/history-list/Bucket.vue +99 -0
  109. package/src/layouts/history-list/CodeBlockTab.vue +61 -0
  110. package/src/layouts/history-list/DataSourceTab.vue +61 -0
  111. package/src/layouts/history-list/GroupRow.vue +207 -0
  112. package/src/layouts/history-list/HistoryDiffDialog.vue +212 -0
  113. package/src/layouts/history-list/HistoryListPanel.vue +271 -0
  114. package/src/layouts/history-list/InitialRow.vue +41 -0
  115. package/src/layouts/history-list/PageTab.vue +94 -0
  116. package/src/layouts/history-list/composables.ts +195 -0
  117. package/src/layouts/sidebar/layer/use-node-status.ts +64 -12
  118. package/src/services/BaseService.ts +35 -86
  119. package/src/services/codeBlock.ts +256 -5
  120. package/src/services/dataSource.ts +257 -6
  121. package/src/services/dep.ts +32 -9
  122. package/src/services/editor.ts +454 -171
  123. package/src/services/history.ts +452 -8
  124. package/src/services/props.ts +2 -2
  125. package/src/theme/history-list-panel.scss +423 -0
  126. package/src/theme/theme.scss +1 -0
  127. package/src/type.ts +179 -6
  128. package/src/utils/code-block.ts +150 -0
  129. package/src/utils/content-menu.ts +8 -5
  130. package/src/utils/dep/worker.ts +5 -3
  131. package/src/utils/editor.ts +4 -1
  132. package/src/utils/index.ts +1 -0
  133. package/src/utils/undo-redo.ts +23 -0
  134. package/types/index.d.ts +2107 -810
  135. package/dist/es/utils/compose.js +0 -44
  136. package/dist/es/utils/editor-history.js +0 -100
  137. package/src/utils/compose.ts +0 -52
  138. package/src/utils/editor-history.ts +0 -138
@@ -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,
@@ -27,10 +27,43 @@ var useNodeStatus = ({ editorService }) => {
27
27
  const nodeStatusMaps = ref(/* @__PURE__ */ new Map());
28
28
  /** 当前页面的节点状态 */
29
29
  const nodeStatusMap = computed(() => page.value ? nodeStatusMaps.value.get(page.value.id) : /* @__PURE__ */ new Map());
30
- watch(() => page.value?.id, (pageId) => {
31
- if (!pageId) return;
32
- nodeStatusMaps.value.set(pageId, createPageNodeStatus(page.value, nodeStatusMaps.value.get(pageId)));
30
+ watch(page, (newPage) => {
31
+ if (!newPage?.id) return;
32
+ nodeStatusMaps.value.set(newPage.id, createPageNodeStatus(newPage, nodeStatusMaps.value.get(newPage.id)));
33
33
  }, { immediate: true });
34
+ /**
35
+ * root 整体被替换时(外部 modelValue 变化、历史版本恢复、套件编辑模式进入/退出等):
36
+ * - 仅 watch page 引用还不够,因为 root-change 同步触发时 page 还是旧引用,
37
+ * 等 initService 的异步 IIFE 跑完 editorService.select(...) 后 page 才会
38
+ * 被替换为新 dsl 中的 page;此时上面的 page watch 才会触发重建。
39
+ * - 但若直接同步清空 nodeStatusMaps,会让 nodeStatusMap (computed) 立刻变
40
+ * undefined。上层 LayerPanel 用 `v-if="page && nodeStatusMap"` 渲染组件树,
41
+ * 会瞬间销毁整个面板;若紧接着的异步 select 链路(套件退出等场景)发生
42
+ * 竞态、page 引用未变 / 解析失败,watch(page) 不触发重建,组件树就再也回
43
+ * 不来。
44
+ * - 此外「污染」问题本质来自 createPageNodeStatus 用旧 status 作为新节点
45
+ * initial 值:只要新 root 的 page 是新引用,watch(page) 会触发重建,重建
46
+ * 时基于新 page.items 生成的 map 只会包含新节点 id;旧节点 id 即便残留在
47
+ * initialLayerNodeStatus 中也不会被写入新 map。真正的风险只有「同一 page
48
+ * id 下,新旧 dsl 都存在同一节点 id 但其实是不同节点」这种极端情况——这
49
+ * 在常规业务中不会发生(id 是 uuid)。
50
+ *
51
+ * 综合:root-change 时仅清理「在新 root 中已不存在的 page id」对应缓存,
52
+ * 保留仍然有效的 page status 不动;既避免 v-if 闪断,也不会保留无关 page 的
53
+ * 死缓存。同 page id 的重建交给下方 watch(page) 触发。
54
+ */
55
+ const rootChangeHandler = (value) => {
56
+ if (!value) {
57
+ nodeStatusMaps.value = /* @__PURE__ */ new Map();
58
+ return;
59
+ }
60
+ const validPageIds = /* @__PURE__ */ new Set();
61
+ (value.items || []).forEach((p) => {
62
+ if (p?.id !== void 0) validPageIds.add(p.id);
63
+ });
64
+ for (const cachedPageId of Array.from(nodeStatusMaps.value.keys())) if (!validPageIds.has(cachedPageId)) nodeStatusMaps.value.delete(cachedPageId);
65
+ };
66
+ editorService.on("root-change", rootChangeHandler);
34
67
  watch(nodes, (nodes) => {
35
68
  if (!nodeStatusMap.value) return;
36
69
  for (const [id, status] of nodeStatusMap.value.entries()) {
@@ -63,6 +96,7 @@ var useNodeStatus = ({ editorService }) => {
63
96
  };
64
97
  editorService.on("remove", removeHandler);
65
98
  onBeforeUnmount(() => {
99
+ editorService.off("root-change", rootChangeHandler);
66
100
  editorService.off("remove", removeHandler);
67
101
  editorService.off("add", addHandler);
68
102
  });
@@ -1,49 +1,39 @@
1
- import { compose } from "../utils/compose.js";
2
1
  import { EventEmitter } from "events";
3
2
  //#region packages/editor/src/services/BaseService.ts
4
3
  var methodName = (prefix, name) => `${prefix}${name[0].toUpperCase()}${name.substring(1)}`;
5
4
  var isError = (error) => Object.prototype.toString.call(error) === "[object Error]";
6
- var doAction = (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
7
- try {
8
- let beforeArgs = args;
9
- for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
10
- beforeArgs = beforeMethod(...beforeArgs) || [];
11
- if (isError(beforeArgs)) throw beforeArgs;
12
- if (!Array.isArray(beforeArgs)) beforeArgs = [beforeArgs];
13
- }
14
- let returnValue = fn(beforeArgs, sourceMethod.bind(scope));
15
- for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
16
- returnValue = afterMethod(returnValue, ...beforeArgs);
17
- if (isError(returnValue)) throw returnValue;
18
- }
19
- return returnValue;
20
- } catch (error) {
21
- throw error;
5
+ var doAction = (args, scope, sourceMethod, beforeMethodName, afterMethodName) => {
6
+ let beforeArgs = args;
7
+ for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
8
+ beforeArgs = beforeMethod(...beforeArgs) || [];
9
+ if (isError(beforeArgs)) throw beforeArgs;
10
+ if (!Array.isArray(beforeArgs)) beforeArgs = [beforeArgs];
11
+ }
12
+ let returnValue = sourceMethod.apply(scope, beforeArgs);
13
+ for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
14
+ returnValue = afterMethod(returnValue, ...beforeArgs);
15
+ if (isError(returnValue)) throw returnValue;
22
16
  }
17
+ return returnValue;
23
18
  };
24
- var doAsyncAction = async (args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) => {
25
- try {
26
- let beforeArgs = args;
27
- for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
28
- beforeArgs = await beforeMethod(...beforeArgs) || [];
29
- if (isError(beforeArgs)) throw beforeArgs;
30
- if (!Array.isArray(beforeArgs)) beforeArgs = [beforeArgs];
31
- }
32
- let returnValue = await fn(beforeArgs, sourceMethod.bind(scope));
33
- for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
34
- returnValue = await afterMethod(returnValue, ...beforeArgs);
35
- if (isError(returnValue)) throw returnValue;
36
- }
37
- return returnValue;
38
- } catch (error) {
39
- throw error;
19
+ var doAsyncAction = async (args, scope, sourceMethod, beforeMethodName, afterMethodName) => {
20
+ let beforeArgs = args;
21
+ for (const beforeMethod of scope.pluginOptionsList[beforeMethodName]) {
22
+ beforeArgs = await beforeMethod(...beforeArgs) || [];
23
+ if (isError(beforeArgs)) throw beforeArgs;
24
+ if (!Array.isArray(beforeArgs)) beforeArgs = [beforeArgs];
40
25
  }
26
+ let returnValue = await sourceMethod.apply(scope, beforeArgs);
27
+ for (const afterMethod of scope.pluginOptionsList[afterMethodName]) {
28
+ returnValue = await afterMethod(returnValue, ...beforeArgs);
29
+ if (isError(returnValue)) throw returnValue;
30
+ }
31
+ return returnValue;
41
32
  };
42
33
  /**
43
- * 提供两种方式对Class进行扩展
44
- * 方法1:
34
+ * Class进行扩展
45
35
  * 给Class中的每个方法都添加before after两个钩子
46
- * 给Class添加一个usePlugin方法,use方法可以传入一个包含before或者after方法的对象
36
+ * 给Class添加一个usePlugin方法,usePlugin方法可以传入一个包含before或者after方法的对象
47
37
  *
48
38
  * 例如:
49
39
  * Class EditorService extends BaseService {
@@ -68,27 +58,9 @@ var doAsyncAction = async (args, scope, sourceMethod, beforeMethodName, afterMet
68
58
  *
69
59
  * 调用时的参数会透传到before方法的参数中, 然后before的return 会作为原方法的参数和after的参数,after第一个参数则是原方法的return值;
70
60
  * 如需终止后续方法调用可以return new Error();
71
- *
72
- * 方法2:
73
- * 给Class中的每个方法都添加中间件
74
- * 给Class添加一个use方法,use方法可以传入一个包含源对象方法名作为key值的对象
75
- *
76
- * 例如:
77
- * Class EditorService extends BaseService {
78
- * constructor() {
79
- * super([ { name: 'add', isAsync: true },]);
80
- * }
81
- * add(value) { return result; }
82
- * };
83
- *
84
- * const editorService = new EditorService();
85
- * editorService.use({
86
- * add(value, next) { console.log(value); next() },
87
- * });
88
61
  */
89
- var BaseService_default = class extends EventEmitter {
62
+ var BaseService = class extends EventEmitter {
90
63
  pluginOptionsList = {};
91
- middleware = {};
92
64
  taskList = [];
93
65
  doingTask = false;
94
66
  constructor(methods = [], serialMethods = []) {
@@ -100,14 +72,12 @@ var BaseService_default = class extends EventEmitter {
100
72
  const afterMethodName = methodName("after", propertyName);
101
73
  this.pluginOptionsList[beforeMethodName] = [];
102
74
  this.pluginOptionsList[afterMethodName] = [];
103
- this.middleware[propertyName] = [];
104
- const fn = compose(this.middleware[propertyName], isAsync);
105
75
  Object.defineProperty(scope, propertyName, { value: isAsync ? async (...args) => {
106
- if (!serialMethods.includes(propertyName)) return doAsyncAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn);
76
+ if (!serialMethods.includes(propertyName)) return doAsyncAction(args, scope, sourceMethod, beforeMethodName, afterMethodName);
107
77
  const promise = new Promise((resolve, reject) => {
108
78
  this.taskList.push(async () => {
109
79
  try {
110
- resolve(await doAsyncAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn));
80
+ resolve(await doAsyncAction(args, scope, sourceMethod, beforeMethodName, afterMethodName));
111
81
  } catch (e) {
112
82
  reject(e);
113
83
  }
@@ -115,15 +85,9 @@ var BaseService_default = class extends EventEmitter {
115
85
  });
116
86
  if (!this.doingTask) this.doTask();
117
87
  return promise;
118
- } : (...args) => doAction(args, scope, sourceMethod, beforeMethodName, afterMethodName, fn) });
88
+ } : (...args) => doAction(args, scope, sourceMethod, beforeMethodName, afterMethodName) });
119
89
  });
120
90
  }
121
- /**
122
- * @deprecated 请使用usePlugin代替
123
- */
124
- use(options) {
125
- for (const [methodName, method] of Object.entries(options)) if (typeof method === "function") this.middleware[methodName].push(method);
126
- }
127
91
  usePlugin(options) {
128
92
  for (const [methodName, method] of Object.entries(options)) if (typeof method === "function" && !this.pluginOptionsList[methodName].includes(method)) this.pluginOptionsList[methodName].push(method);
129
93
  }
@@ -132,7 +96,6 @@ var BaseService_default = class extends EventEmitter {
132
96
  }
133
97
  removeAllPlugins() {
134
98
  for (const key of Object.keys(this.pluginOptionsList)) this.pluginOptionsList[key] = [];
135
- for (const key of Object.keys(this.middleware)) this.middleware[key] = [];
136
99
  }
137
100
  async doTask() {
138
101
  this.doingTask = true;
@@ -145,4 +108,4 @@ var BaseService_default = class extends EventEmitter {
145
108
  }
146
109
  };
147
110
  //#endregion
148
- export { BaseService_default as default };
111
+ export { BaseService as default };