@tmagic/editor 1.8.0-manmanyu.8 → 1.8.0

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 (281) hide show
  1. package/dist/es/Editor.vue_vue_type_script_setup_true_lang.js +22 -13
  2. package/dist/es/components/CodeBlockEditor.vue_vue_type_script_setup_true_lang.js +10 -5
  3. package/dist/es/components/CodeParams.vue_vue_type_script_setup_true_lang.js +7 -6
  4. package/dist/es/components/CompareForm.vue_vue_type_script_setup_true_lang.js +21 -119
  5. package/dist/es/components/ContentMenu.vue_vue_type_script_setup_true_lang.js +21 -5
  6. package/dist/es/components/FloatingBox.vue_vue_type_script_setup_true_lang.js +62 -16
  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 +3 -2
  9. package/dist/es/components/ScrollBar.js +0 -1
  10. package/dist/es/components/ScrollBar.vue_vue_type_script_setup_true_lang.js +5 -4
  11. package/dist/es/components/ScrollViewer.vue_vue_type_script_setup_true_lang.js +1 -1
  12. package/dist/es/components/SearchInput.vue_vue_type_script_setup_true_lang.js +1 -1
  13. package/dist/es/components/SplitView.vue_vue_type_script_setup_true_lang.js +1 -1
  14. package/dist/es/components/ToolButton.vue_vue_type_script_setup_true_lang.js +1 -1
  15. package/dist/es/components/Tree.vue_vue_type_script_setup_true_lang.js +1 -1
  16. package/dist/es/components/TreeNode.vue_vue_type_script_setup_true_lang.js +1 -1
  17. package/dist/es/components/ViewForm.js +5 -0
  18. package/dist/es/components/ViewForm.vue_vue_type_script_setup_true_lang.js +56 -0
  19. package/dist/es/editorProps.js +7 -1
  20. package/dist/es/fields/Code.vue_vue_type_script_setup_true_lang.js +1 -1
  21. package/dist/es/fields/CodeLink.vue_vue_type_script_setup_true_lang.js +1 -1
  22. package/dist/es/fields/CodeSelect.vue_vue_type_script_setup_true_lang.js +30 -113
  23. package/dist/es/fields/CodeSelectCol.vue_vue_type_script_setup_true_lang.js +2 -2
  24. package/dist/es/fields/CondOpSelect.vue_vue_type_script_setup_true_lang.js +7 -19
  25. package/dist/es/fields/DataSourceFieldSelect/FieldSelect.vue_vue_type_script_setup_true_lang.js +7 -6
  26. package/dist/es/fields/DataSourceFieldSelect/Index.vue_vue_type_script_setup_true_lang.js +4 -2
  27. package/dist/es/fields/DataSourceFields.vue_vue_type_script_setup_true_lang.js +12 -4
  28. package/dist/es/fields/DataSourceInput.vue_vue_type_script_setup_true_lang.js +9 -20
  29. package/dist/es/fields/DataSourceMethodSelect.vue_vue_type_script_setup_true_name_true_lang.js +2 -2
  30. package/dist/es/fields/DataSourceMethods.vue_vue_type_script_setup_true_lang.js +5 -3
  31. package/dist/es/fields/DataSourceMocks.vue_vue_type_script_setup_true_lang.js +9 -4
  32. package/dist/es/fields/DataSourceSelect.vue_vue_type_script_setup_true_lang.js +2 -2
  33. package/dist/es/fields/DisplayConds.vue_vue_type_script_setup_true_lang.js +10 -110
  34. package/dist/es/fields/EventSelect.vue_vue_type_script_setup_true_lang.js +52 -363
  35. package/dist/es/fields/KeyValue.vue_vue_type_script_setup_true_lang.js +1 -1
  36. package/dist/es/fields/PageFragmentSelect.vue_vue_type_script_setup_true_lang.js +1 -1
  37. package/dist/es/fields/StyleSetter/Index.vue_vue_type_script_setup_true_lang.js +3 -6
  38. package/dist/es/fields/StyleSetter/components/BackgroundPosition.vue_vue_type_script_setup_true_lang.js +1 -1
  39. package/dist/es/fields/StyleSetter/components/Border.vue_vue_type_script_setup_true_lang.js +4 -35
  40. package/dist/es/fields/StyleSetter/components/Box.vue_vue_type_script_setup_true_lang.js +3 -3
  41. package/dist/es/fields/StyleSetter/configs.js +663 -0
  42. package/dist/es/fields/StyleSetter/icons/align-items/Center.js +1 -1
  43. package/dist/es/fields/StyleSetter/icons/align-items/FlexEnd.js +1 -1
  44. package/dist/es/fields/StyleSetter/icons/align-items/FlexStart.js +1 -1
  45. package/dist/es/fields/StyleSetter/icons/align-items/SpaceAround.js +1 -1
  46. package/dist/es/fields/StyleSetter/icons/align-items/SpaceBetween.js +1 -1
  47. package/dist/es/fields/StyleSetter/icons/background-repeat/NoRepeat.js +1 -1
  48. package/dist/es/fields/StyleSetter/icons/background-repeat/Repeat.js +1 -1
  49. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatX.js +1 -1
  50. package/dist/es/fields/StyleSetter/icons/background-repeat/RepeatY.js +1 -1
  51. package/dist/es/fields/StyleSetter/icons/display/Block.js +1 -1
  52. package/dist/es/fields/StyleSetter/icons/display/Flex.js +1 -1
  53. package/dist/es/fields/StyleSetter/icons/display/Inline.js +1 -1
  54. package/dist/es/fields/StyleSetter/icons/display/InlineBlock.js +1 -1
  55. package/dist/es/fields/StyleSetter/icons/display/None.js +1 -1
  56. package/dist/es/fields/StyleSetter/icons/flex-direction/Column.js +1 -1
  57. package/dist/es/fields/StyleSetter/icons/flex-direction/ColumnReverse.js +1 -1
  58. package/dist/es/fields/StyleSetter/icons/flex-direction/Row.js +1 -1
  59. package/dist/es/fields/StyleSetter/icons/flex-direction/RowReverse.js +1 -1
  60. package/dist/es/fields/StyleSetter/icons/justify-content/Center.js +1 -1
  61. package/dist/es/fields/StyleSetter/icons/justify-content/FlexEnd.js +1 -1
  62. package/dist/es/fields/StyleSetter/icons/justify-content/FlexStart.js +1 -1
  63. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceAround.js +1 -1
  64. package/dist/es/fields/StyleSetter/icons/justify-content/SpaceBetween.js +1 -1
  65. package/dist/es/fields/StyleSetter/icons/text-align/Center.js +1 -1
  66. package/dist/es/fields/StyleSetter/icons/text-align/Left.js +1 -1
  67. package/dist/es/fields/StyleSetter/icons/text-align/Right.js +1 -1
  68. package/dist/es/fields/StyleSetter/pro/Background.vue_vue_type_script_setup_true_lang.js +29 -106
  69. package/dist/es/fields/StyleSetter/pro/Border.vue_vue_type_script_setup_true_lang.js +8 -11
  70. package/dist/es/fields/StyleSetter/pro/Font.vue_vue_type_script_setup_true_lang.js +30 -89
  71. package/dist/es/fields/StyleSetter/pro/Layout.vue_vue_type_script_setup_true_lang.js +29 -297
  72. package/dist/es/fields/StyleSetter/pro/Position.vue_vue_type_script_setup_true_lang.js +29 -82
  73. package/dist/es/fields/StyleSetter/pro/Transform.vue_vue_type_script_setup_true_lang.js +8 -24
  74. package/dist/es/fields/UISelect.js +0 -1
  75. package/dist/es/fields/UISelect.vue_vue_type_script_setup_true_lang.js +1 -1
  76. package/dist/es/fields/configs/codeSelect.js +98 -0
  77. package/dist/es/fields/configs/displayConds.js +158 -0
  78. package/dist/es/fields/configs/eventSelect.js +225 -0
  79. package/dist/es/fields/configs/stickyAddButton.js +21 -0
  80. package/dist/es/fields/headless-validation.js +139 -0
  81. package/dist/es/headless.js +2 -0
  82. package/dist/es/hooks/use-code-block-edit.js +4 -2
  83. package/dist/es/hooks/use-compare-form.js +101 -0
  84. package/dist/es/hooks/use-form-context.js +29 -0
  85. package/dist/es/hooks/use-stage.js +1 -0
  86. package/dist/es/icons/CenterIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  87. package/dist/es/icons/CodeIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  88. package/dist/es/icons/DatasourceIcon.js +1 -1
  89. package/dist/es/icons/FolderMinusIcon.vue_vue_type_script_setup_true_lang.js +1 -1
  90. package/dist/es/index.js +17 -9
  91. package/dist/es/initService.js +52 -16
  92. package/dist/es/layouts/AddPageBox.vue_vue_type_script_setup_true_lang.js +1 -1
  93. package/dist/es/layouts/CodeEditor.vue_vue_type_script_setup_true_lang.js +26 -10
  94. package/dist/es/layouts/Framework.vue_vue_type_script_setup_true_lang.js +11 -8
  95. package/dist/es/layouts/NavMenu.vue_vue_type_script_setup_true_lang.js +3 -3
  96. package/dist/es/layouts/NavMenuColumn.vue_vue_type_script_setup_true_lang.js +1 -1
  97. package/dist/es/layouts/history-list/Bucket.vue_vue_type_script_setup_true_lang.js +1 -1
  98. package/dist/es/layouts/history-list/BucketTab.vue_vue_type_script_setup_true_lang.js +8 -5
  99. package/dist/es/layouts/history-list/GroupRow.vue_vue_type_script_setup_true_lang.js +1 -1
  100. package/dist/es/layouts/history-list/HistoryDiffDialog.vue_vue_type_script_setup_true_lang.js +26 -10
  101. package/dist/es/layouts/history-list/HistoryListPanel.vue_vue_type_script_setup_true_lang.js +36 -28
  102. package/dist/es/layouts/history-list/InitialRow.vue_vue_type_script_setup_true_lang.js +1 -1
  103. package/dist/es/layouts/history-list/PageTab.vue_vue_type_script_setup_true_lang.js +1 -1
  104. package/dist/es/layouts/history-list/composables.js +1 -1
  105. package/dist/es/layouts/history-list/useHistoryList.js +4 -4
  106. package/dist/es/layouts/history-list/useHistoryRevert.js +38 -33
  107. package/dist/es/layouts/page-bar/AddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  108. package/dist/es/layouts/page-bar/PageBar.vue_vue_type_script_setup_true_lang.js +1 -1
  109. package/dist/es/layouts/page-bar/PageBarScrollContainer.vue_vue_type_script_setup_true_lang.js +1 -1
  110. package/dist/es/layouts/page-bar/PageList.vue_vue_type_script_setup_true_lang.js +1 -1
  111. package/dist/es/layouts/page-bar/Search.vue_vue_type_script_setup_true_lang.js +2 -2
  112. package/dist/es/layouts/props-panel/FormPanel.vue_vue_type_script_setup_true_lang.js +50 -20
  113. package/dist/es/layouts/props-panel/PropsPanel.vue_vue_type_script_setup_true_lang.js +53 -35
  114. package/dist/es/layouts/props-panel/use-style-panel.js +3 -2
  115. package/dist/es/layouts/sidebar/ComponentListPanel.vue_vue_type_script_setup_true_lang.js +3 -3
  116. package/dist/es/layouts/sidebar/Sidebar.vue_vue_type_script_setup_true_lang.js +21 -36
  117. package/dist/es/layouts/sidebar/code-block/CodeBlockList.vue_vue_type_script_setup_true_lang.js +1 -1
  118. package/dist/es/layouts/sidebar/code-block/CodeBlockListPanel.vue_vue_type_script_setup_true_lang.js +4 -4
  119. package/dist/es/layouts/sidebar/code-block/useContentMenu.js +19 -4
  120. package/dist/es/layouts/sidebar/data-source/DataSourceAddButton.vue_vue_type_script_setup_true_lang.js +1 -1
  121. package/dist/es/layouts/sidebar/data-source/DataSourceConfigPanel.vue_vue_type_script_setup_true_lang.js +9 -4
  122. package/dist/es/layouts/sidebar/data-source/DataSourceList.vue_vue_type_script_setup_true_lang.js +1 -1
  123. package/dist/es/layouts/sidebar/data-source/DataSourceListPanel.vue_vue_type_script_setup_true_lang.js +4 -4
  124. package/dist/es/layouts/sidebar/data-source/useContentMenu.js +19 -4
  125. package/dist/es/layouts/sidebar/layer/LayerMenu.vue_vue_type_script_setup_true_lang.js +3 -3
  126. package/dist/es/layouts/sidebar/layer/LayerNodeContent.js +5 -0
  127. package/dist/es/layouts/sidebar/layer/LayerNodeContent.vue_vue_type_script_setup_true_lang.js +46 -0
  128. package/dist/es/layouts/sidebar/layer/LayerNodeTool.vue_vue_type_script_setup_true_lang.js +1 -1
  129. package/dist/es/layouts/sidebar/layer/LayerPanel.vue_vue_type_script_setup_true_lang.js +6 -4
  130. package/dist/es/layouts/sidebar/layer/use-click.js +3 -3
  131. package/dist/es/layouts/sidebar/layer/use-drag.js +11 -10
  132. package/dist/es/layouts/sidebar/layer/use-node-status.js +2 -2
  133. package/dist/es/layouts/workspace/Breadcrumb.vue_vue_type_script_setup_true_lang.js +1 -1
  134. package/dist/es/layouts/workspace/Workspace.vue_vue_type_script_setup_true_lang.js +1 -1
  135. package/dist/es/layouts/workspace/viewer/NodeListMenu.vue_vue_type_script_setup_true_lang.js +9 -3
  136. package/dist/es/layouts/workspace/viewer/Stage.vue_vue_type_script_setup_true_lang.js +55 -9
  137. package/dist/es/layouts/workspace/viewer/StageOverlay.vue_vue_type_script_setup_true_lang.js +1 -1
  138. package/dist/es/layouts/workspace/viewer/ViewerMenu.vue_vue_type_script_setup_true_lang.js +8 -8
  139. package/dist/es/plugin.js +35 -23
  140. package/dist/es/services/codeBlock.js +40 -31
  141. package/dist/es/services/dataSource.js +46 -34
  142. package/dist/es/services/dep.js +162 -41
  143. package/dist/es/services/editor.js +373 -137
  144. package/dist/es/services/events.js +4 -4
  145. package/dist/es/services/history.js +275 -392
  146. package/dist/es/services/keybinding.js +3 -3
  147. package/dist/es/services/props.js +2 -1
  148. package/dist/es/services/storage.js +2 -2
  149. package/dist/es/services/ui.js +7 -2
  150. package/dist/es/style.css +277 -114
  151. package/dist/es/utils/code-block.js +14 -7
  152. package/dist/es/utils/config.js +4 -1
  153. package/dist/es/utils/const.js +2 -2
  154. package/dist/es/utils/content-menu.js +2 -2
  155. package/dist/es/utils/data-source/index.js +34 -12
  156. package/dist/es/utils/dep/collect-worker-client.js +154 -0
  157. package/dist/es/utils/dep/idle-task.js +70 -22
  158. package/dist/es/utils/dep/worker.js +1 -1
  159. package/dist/es/utils/editor.js +4 -2
  160. package/dist/es/utils/event.js +135 -0
  161. package/dist/es/utils/history.js +35 -47
  162. package/dist/es/utils/monaco-editor.js +22 -6
  163. package/dist/es/utils/operator.js +2 -2
  164. package/dist/es/utils/props.js +50 -106
  165. package/dist/es/utils/type-match-rules.js +356 -0
  166. package/dist/style.css +277 -114
  167. package/dist/themes/magic-admin.css +978 -175
  168. package/dist/tmagic-editor-headless.umd.cjs +10880 -0
  169. package/dist/tmagic-editor.umd.cjs +13047 -11363
  170. package/package.json +18 -13
  171. package/src/Editor.vue +23 -6
  172. package/src/components/CodeBlockEditor.vue +3 -0
  173. package/src/components/CodeParams.vue +10 -4
  174. package/src/components/CompareForm.vue +20 -190
  175. package/src/components/ContentMenu.vue +34 -6
  176. package/src/components/FloatingBox.vue +71 -7
  177. package/src/components/ScrollBar.vue +0 -67
  178. package/src/components/ViewForm.vue +54 -0
  179. package/src/editorProps.ts +23 -10
  180. package/src/fields/Code.vue +1 -1
  181. package/src/fields/CodeSelect.vue +20 -113
  182. package/src/fields/CodeSelectCol.vue +1 -1
  183. package/src/fields/CondOpSelect.vue +5 -26
  184. package/src/fields/DataSourceFieldSelect/FieldSelect.vue +2 -2
  185. package/src/fields/DataSourceFieldSelect/Index.vue +4 -1
  186. package/src/fields/DataSourceFields.vue +5 -1
  187. package/src/fields/DataSourceInput.vue +10 -28
  188. package/src/fields/DataSourceMethodSelect.vue +1 -1
  189. package/src/fields/DataSourceMethods.vue +6 -2
  190. package/src/fields/DataSourceMocks.vue +3 -0
  191. package/src/fields/DataSourceSelect.vue +1 -1
  192. package/src/fields/DisplayConds.vue +17 -139
  193. package/src/fields/EventSelect.vue +29 -416
  194. package/src/fields/KeyValue.vue +1 -1
  195. package/src/fields/StyleSetter/Index.vue +2 -8
  196. package/src/fields/StyleSetter/components/Border.vue +4 -38
  197. package/src/fields/StyleSetter/components/Box.vue +1 -1
  198. package/src/fields/StyleSetter/components/Position.vue +1 -1
  199. package/src/fields/StyleSetter/configs.ts +690 -0
  200. package/src/fields/StyleSetter/pro/Background.vue +9 -64
  201. package/src/fields/StyleSetter/pro/Border.vue +5 -16
  202. package/src/fields/StyleSetter/pro/Font.vue +11 -71
  203. package/src/fields/StyleSetter/pro/Layout.vue +10 -230
  204. package/src/fields/StyleSetter/pro/Position.vue +11 -82
  205. package/src/fields/StyleSetter/pro/Transform.vue +6 -34
  206. package/src/fields/UISelect.vue +0 -12
  207. package/src/fields/configs/codeSelect.ts +127 -0
  208. package/src/fields/configs/displayConds.ts +184 -0
  209. package/src/fields/configs/eventSelect.ts +309 -0
  210. package/src/fields/configs/stickyAddButton.ts +32 -0
  211. package/src/fields/headless-validation.ts +177 -0
  212. package/src/headless.ts +33 -0
  213. package/src/hooks/index.ts +1 -0
  214. package/src/hooks/use-compare-form.ts +166 -0
  215. package/src/hooks/use-form-context.ts +56 -0
  216. package/src/hooks/use-stage.ts +1 -0
  217. package/src/index.ts +9 -0
  218. package/src/initService.ts +73 -18
  219. package/src/layouts/CodeEditor.vue +32 -6
  220. package/src/layouts/Framework.vue +7 -6
  221. package/src/layouts/history-list/BucketTab.vue +2 -2
  222. package/src/layouts/history-list/HistoryDiffDialog.vue +33 -15
  223. package/src/layouts/history-list/HistoryListPanel.vue +16 -18
  224. package/src/layouts/history-list/useHistoryRevert.ts +57 -41
  225. package/src/layouts/props-panel/FormPanel.vue +75 -20
  226. package/src/layouts/props-panel/PropsPanel.vue +81 -30
  227. package/src/layouts/props-panel/use-style-panel.ts +4 -3
  228. package/src/layouts/sidebar/ComponentListPanel.vue +6 -2
  229. package/src/layouts/sidebar/Sidebar.vue +5 -3
  230. package/src/layouts/sidebar/code-block/CodeBlockListPanel.vue +5 -2
  231. package/src/layouts/sidebar/code-block/useContentMenu.ts +14 -2
  232. package/src/layouts/sidebar/data-source/DataSourceConfigPanel.vue +3 -0
  233. package/src/layouts/sidebar/data-source/DataSourceListPanel.vue +3 -3
  234. package/src/layouts/sidebar/data-source/useContentMenu.ts +14 -2
  235. package/src/layouts/sidebar/layer/LayerMenu.vue +2 -2
  236. package/src/layouts/sidebar/layer/LayerNodeContent.vue +57 -0
  237. package/src/layouts/sidebar/layer/LayerPanel.vue +5 -2
  238. package/src/layouts/sidebar/layer/use-click.ts +3 -3
  239. package/src/layouts/sidebar/layer/use-node-status.ts +2 -2
  240. package/src/layouts/workspace/viewer/NodeListMenu.vue +4 -1
  241. package/src/layouts/workspace/viewer/Stage.vue +80 -10
  242. package/src/layouts/workspace/viewer/ViewerMenu.vue +7 -7
  243. package/src/plugin.ts +35 -25
  244. package/src/services/codeBlock.ts +4 -0
  245. package/src/services/dataSource.ts +3 -0
  246. package/src/services/dep.ts +292 -47
  247. package/src/services/editor.ts +383 -120
  248. package/src/services/events.ts +8 -7
  249. package/src/services/history.ts +22 -1
  250. package/src/services/keybinding.ts +3 -3
  251. package/src/services/ui.ts +26 -5
  252. package/src/theme/code-block.scss +0 -37
  253. package/src/theme/display-conds.scss +4 -0
  254. package/src/theme/event.scss +25 -33
  255. package/src/theme/floating-box.scss +9 -1
  256. package/src/theme/framework.scss +4 -1
  257. package/src/theme/layer-node-content.scss +14 -0
  258. package/src/theme/nav-menu.scss +5 -1
  259. package/src/theme/props-panel.scss +17 -3
  260. package/src/theme/scroll-bar.scss +64 -0
  261. package/src/theme/theme.scss +3 -0
  262. package/src/theme/themes/magic-admin/index.scss +109 -9
  263. package/src/theme/ui-select.scss +9 -0
  264. package/src/type.ts +216 -6
  265. package/src/utils/code-block.ts +1 -0
  266. package/src/utils/config.ts +9 -1
  267. package/src/utils/const.ts +3 -3
  268. package/src/utils/content-menu.ts +2 -2
  269. package/src/utils/data-source/index.ts +31 -12
  270. package/src/utils/dep/collect-worker-client.ts +215 -0
  271. package/src/utils/dep/idle-task.ts +104 -35
  272. package/src/utils/dep/worker.ts +113 -9
  273. package/src/utils/event.ts +237 -0
  274. package/src/utils/index.ts +2 -0
  275. package/src/utils/monaco-editor.ts +27 -0
  276. package/src/utils/operator.ts +2 -2
  277. package/src/utils/props.ts +61 -122
  278. package/src/utils/type-match-rules.ts +697 -0
  279. package/types/headless.d.ts +17 -0
  280. package/types/index.d.ts +1494 -978
  281. package/src/fields/StyleSetter/icons/align-items/index copy.ts +0 -5
@@ -7,11 +7,13 @@ import storage_default, { Protocol } from "./storage.js";
7
7
  import { COPY_STORAGE_KEY, calcAlignCenterStyle, calcLayerTargetIndex, calcMoveStyle, classifyDragSources, collectRelatedNodes, describeStepForRevert, editorNodeMergeCustomizer, fixNodePosition, getInitPositionStyle, getNodeIndex, getPageFragmentList, getPageList, moveItemsInContainer, resolveSelectedNode, setChildrenLayout, setLayout, toggleFixedPosition } from "../utils/editor.js";
8
8
  import { beforePaste, getAddParent } from "../utils/operator.js";
9
9
  import { isFixed } from "@tmagic/stage";
10
- import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, setValueByKeyPath } from "@tmagic/utils";
10
+ import { getNodeInfo, getNodePath, getValueByKeyPath, guid, isPage, isPageFragment, isPageOrFragment, setValueByKeyPath, traverseNode } from "@tmagic/utils";
11
11
  import { NodeType } from "@tmagic/core";
12
- import { reactive, toRaw } from "vue";
12
+ import { nextTick, reactive, toRaw } from "vue";
13
13
  import { cloneDeep as cloneDeep$1, isEmpty, isEqual, isObject, mergeWith, uniq } from "lodash-es";
14
14
  //#region packages/editor/src/services/editor.ts
15
+ /** 历史插回时把记录的下标收敛到 [0, length],越界(含未记录)一律追加到末尾 */
16
+ var clampIndex = (index, length) => typeof index === "number" && index >= 0 && index <= length ? index : length;
15
17
  /**
16
18
  * 把「变更前后节点快照」列表归一成 update 类型的 {@link StepDiffItem} 列表,供 {@link StepValue.diff} 使用。
17
19
  * `changeRecords` 来自 form 端的 propPath/value 列表,撤销/重做时只对这些 propPath 做局部更新;
@@ -33,6 +35,7 @@ var Editor = class extends BaseService {
33
35
  stageLoading: true,
34
36
  highlightNode: null,
35
37
  modifiedNodeIds: /* @__PURE__ */ new Map(),
38
+ invalidNodeIds: /* @__PURE__ */ new Map(),
36
39
  pageLength: 0,
37
40
  pageFragmentLength: 0,
38
41
  disabledMultiSelect: false,
@@ -40,11 +43,21 @@ var Editor = class extends BaseService {
40
43
  });
41
44
  selectionBeforeOp = null;
42
45
  /**
46
+ * 操作前的节点校验错误快照,与 selectionBeforeOp 同时在 captureSelectionBeforeOp 中捕获,
47
+ * 供 pushOpHistory 写入 step.extra.invalidNodeIdsBefore,用于撤销时还原到「操作前」的错误状态。
48
+ */
49
+ invalidNodeIdsBeforeOp = null;
50
+ /**
43
51
  * 最近一次 pushOpHistory 写入的历史记录 uuid。
44
52
  * 供 *AndGetHistoryId 系列方法在调用普通操作后取回本次产生的历史记录 id;
45
53
  * 普通操作不会读取它,调用前由 *AndGetHistoryId 重置为 null。
46
54
  */
47
55
  lastPushedHistoryId = null;
56
+ /**
57
+ * 上一次 doAdd 的插入记忆:nodeId 为插入的节点,selectedId 为当时的选中节点。
58
+ * 仅在选中节点未变化时复用(连续 add / doNotSelect / 批量粘贴),选中变化后自动失效,无需手动清理。
59
+ */
60
+ lastAdded = null;
48
61
  constructor() {
49
62
  super(canUsePluginMethods.async.map((methodName) => ({
50
63
  name: methodName,
@@ -57,7 +70,7 @@ var Editor = class extends BaseService {
57
70
  }
58
71
  /**
59
72
  * 设置当前指点节点配置
60
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength
73
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'invalidNodeIds' | 'pageLength' | 'pageFragmentLength
61
74
  * @param value MNode
62
75
  * @param options.historySource 设置 root 时,本次变更写入历史记录的「操作来源」(仅 name === 'root' 时生效)
63
76
  */
@@ -74,7 +87,7 @@ var Editor = class extends BaseService {
74
87
  this.state.stageLoading = this.state.pageLength !== 0;
75
88
  if (preValue && !isEmpty(preValue)) this.pushRootDiffHistory(preValue, app, options.historySource);
76
89
  else app.items?.forEach((pageNode) => {
77
- if (pageNode?.id !== void 0 && !history_default.getPageMarker(pageNode.id)) history_default.setPageMarker(pageNode.id, {
90
+ if (pageNode?.id !== void 0 && !history_default.getMarker("page", pageNode.id)) history_default.setMarker("page", pageNode.id, {
78
91
  name: pageNode.name,
79
92
  source: options.historySource
80
93
  });
@@ -85,11 +98,12 @@ var Editor = class extends BaseService {
85
98
  this.state.stageLoading = false;
86
99
  }
87
100
  this.emit("root-change", value, preValue, options);
101
+ this.pruneInvalidNodeIds();
88
102
  }
89
103
  }
90
104
  /**
91
105
  * 获取当前指点节点配置
92
- * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'pageLength' | 'pageFragmentLength'
106
+ * @param name 'root' | 'page' | 'parent' | 'node' | 'highlightNode' | 'nodes' | 'stage' | 'modifiedNodeIds' | 'invalidNodeIds' | 'pageLength' | 'pageFragmentLength'
93
107
  * @returns MNode
94
108
  */
95
109
  get(name) {
@@ -152,7 +166,7 @@ var Editor = class extends BaseService {
152
166
  isOnDifferentPage(node) {
153
167
  const currentPageId = this.get("page")?.id;
154
168
  if (currentPageId === void 0 || currentPageId === null) return false;
155
- if (isPage(node) || isPageFragment(node)) return `${node.id}` !== `${currentPageId}`;
169
+ if (isPageOrFragment(node)) return `${node.id}` !== `${currentPageId}`;
156
170
  const nodePage = this.getNodeInfo(node.id, false).page;
157
171
  if (!nodePage) return false;
158
172
  return `${nodePage.id}` !== `${currentPageId}`;
@@ -176,8 +190,6 @@ var Editor = class extends BaseService {
176
190
  this.set("nodes", node ? [node] : []);
177
191
  this.set("page", page);
178
192
  this.set("parent", parent);
179
- if (page) history_default.changePage(toRaw(page));
180
- else history_default.resetState();
181
193
  if (node?.id) this.get("stage")?.renderer?.runtime?.getApp?.()?.page?.emit("editor:select", {
182
194
  node,
183
195
  page,
@@ -241,24 +253,29 @@ var Editor = class extends BaseService {
241
253
  this.set("stage", null);
242
254
  this.set("highlightNode", null);
243
255
  }
244
- async doAdd(node, parent) {
256
+ async doAdd(node, parent, _options = {}) {
245
257
  const root = this.get("root");
246
258
  if (!root) throw new Error("root为空");
247
259
  const curNode = this.get("node");
248
260
  const stage = this.get("stage");
249
261
  if (!curNode) throw new Error("当前选中节点为空");
250
- if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !(isPage(node) || isPageFragment(node))) throw new Error("app下不能添加组件");
251
- if (parent.id !== curNode.id && !(isPage(node) || isPageFragment(node))) {
252
- const index = parent.items.indexOf(curNode);
253
- parent.items?.splice(index + 1, 0, node);
254
- } else parent.items?.push(node);
262
+ if ((parent.type === NodeType.ROOT || curNode?.type === NodeType.ROOT) && !isPageOrFragment(node)) throw new Error("app下不能添加组件");
263
+ const anchorId = this.lastAdded?.selectedId === curNode.id ? this.lastAdded.nodeId : curNode.id;
264
+ const anchorIndex = isPageOrFragment(node) ? -1 : Math.max(getNodeIndex(anchorId, parent), getNodeIndex(curNode.id, parent));
265
+ const insertIndex = anchorIndex < 0 ? parent.items.length : anchorIndex + 1;
266
+ parent.items.splice(insertIndex, 0, node);
267
+ this.lastAdded = {
268
+ selectedId: curNode.id,
269
+ nodeId: node.id
270
+ };
255
271
  const layout = await this.getLayout(toRaw(parent), node);
256
272
  node.style = getInitPositionStyle(node.style, layout);
257
273
  await stage?.add({
258
274
  config: cloneDeep$1(node),
259
275
  parent: cloneDeep$1(parent),
260
276
  parentId: parent.id,
261
- root: cloneDeep$1(root)
277
+ root: cloneDeep$1(root),
278
+ index: insertIndex
262
279
  });
263
280
  const newStyle = fixNodePosition(node, parent, stage);
264
281
  if (newStyle && (newStyle.top !== node.style.top || newStyle.left !== node.style.left)) {
@@ -276,14 +293,12 @@ var Editor = class extends BaseService {
276
293
  * 向指点容器添加组件节点
277
294
  * @param addConfig 将要添加的组件节点配置
278
295
  * @param parent 要添加到的容器组件节点配置,如果不设置,默认为当前选中的组件的父节点
279
- * @param options 可选配置
280
- * @param options.doNotSelect 添加后是否不更新当前选中节点(默认 false,添加后会选中新增的节点)
281
- * @param options.doNotSwitchPage 添加后是否不切换当前页面(默认 false;新增页面 / 跨页新增时为 true 会跳过会引发页面切换的选中操作)
282
- * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
296
+ * @param options 可选配置,见 {@link DslOpOptions}
283
297
  * @returns 添加后的节点
284
298
  */
285
- async add(addNode, parent, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
299
+ async add(addNode, parent, options = {}) {
286
300
  this.captureSelectionBeforeOp();
301
+ const { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = options;
287
302
  const stage = this.get("stage");
288
303
  const addNodes = [];
289
304
  if (!Array.isArray(addNode)) {
@@ -291,13 +306,13 @@ var Editor = class extends BaseService {
291
306
  if (!type) throw new Error("组件类型不能为空");
292
307
  addNodes.push({ ...toRaw(await props_default.getPropsValue(type, config)) });
293
308
  } else addNodes.push(...addNode);
294
- const newNodes = await Promise.all(addNodes.map((node) => {
309
+ const newNodes = [];
310
+ for (const node of addNodes) {
295
311
  const root = this.get("root");
296
- if ((isPage(node) || isPageFragment(node)) && root) return this.doAdd(node, root);
297
- const parentNode = parent ?? getAddParent(node);
312
+ const parentNode = isPageOrFragment(node) && root ? root : parent ?? getAddParent(node);
298
313
  if (!parentNode) throw new Error("未找到父元素");
299
- return this.doAdd(node, parentNode);
300
- }));
314
+ newNodes.push(await this.doAdd(node, parentNode, options));
315
+ }
301
316
  if (newNodes.length > 1) {
302
317
  const wouldSwitchPage = newNodes.some((n) => this.isOnDifferentPage(n));
303
318
  if (!doNotSelect && !(doNotSwitchPage && wouldSwitchPage)) {
@@ -313,7 +328,7 @@ var Editor = class extends BaseService {
313
328
  else if (isPageFragment(newNodes[0])) this.state.pageFragmentLength += 1;
314
329
  else if (!skipSelect) stage?.select(newNodes[0].id);
315
330
  }
316
- if (!(isPage(newNodes[0]) || isPageFragment(newNodes[0]))) {
331
+ if (!isPageOrFragment(newNodes[0])) {
317
332
  const pageForOp = this.getNodeInfo(newNodes[0].id, false).page;
318
333
  if (!doNotPushHistory) {
319
334
  const parentId = (this.getParentById(newNodes[0].id, false) ?? this.get("root")).id;
@@ -337,6 +352,20 @@ var Editor = class extends BaseService {
337
352
  } else this.selectionBeforeOp = null;
338
353
  }
339
354
  this.emit("add", newNodes);
355
+ this.emit("change", {
356
+ type: "add",
357
+ data: newNodes.map((node) => ({
358
+ node,
359
+ page: this.getPageOfNode(node.id)
360
+ })),
361
+ historySource,
362
+ doNotPushHistory
363
+ });
364
+ const addedPages = newNodes.filter((node) => isPageOrFragment(node));
365
+ if (addedPages.length) history_default.notifyPageStructureChange({
366
+ add: addedPages,
367
+ remove: []
368
+ });
340
369
  return Array.isArray(addNode) ? newNodes : newNodes[0];
341
370
  }
342
371
  async doRemove(node, { doNotSelect = false, doNotSwitchPage = false } = {}) {
@@ -346,13 +375,10 @@ var Editor = class extends BaseService {
346
375
  if (!parent || !curNode) throw new Error("找不要删除的节点");
347
376
  const index = getNodeIndex(curNode.id, parent);
348
377
  if (typeof index !== "number" || index === -1) throw new Error("找不要删除的节点");
349
- parent.items?.splice(index, 1);
350
378
  const stage = this.get("stage");
351
- stage?.remove({
352
- id: node.id,
353
- parentId: parent.id,
354
- root: cloneDeep$1(root)
355
- });
379
+ const currentPage = this.get("page");
380
+ const isDeletingCurrentPage = isPageOrFragment(node) && !!currentPage && `${currentPage.id}` === `${node.id}`;
381
+ parent.items?.splice(index, 1);
356
382
  const selectedNodes = this.get("nodes");
357
383
  const removedSelectedIndex = selectedNodes.findIndex((n) => `${n.id}` === `${node.id}`);
358
384
  if (removedSelectedIndex !== -1) {
@@ -360,27 +386,29 @@ var Editor = class extends BaseService {
360
386
  nextSelected.splice(removedSelectedIndex, 1);
361
387
  this.set("nodes", nextSelected);
362
388
  }
363
- if (isPage(node) || isPageFragment(node)) {
364
- const currentPage = this.get("page");
365
- if (currentPage && `${currentPage.id}` === `${node.id}`) this.set("page", null);
366
- }
367
- const selectDefault = async (pages) => {
368
- if (pages[0]) {
369
- await this.select(pages[0]);
370
- stage?.select(pages[0].id);
371
- } else {
372
- this.selectRoot();
373
- history_default.resetPage();
374
- }
389
+ const removeData = {
390
+ id: node.id,
391
+ parentId: parent.id,
392
+ root: cloneDeep$1(root)
375
393
  };
376
394
  const rootItems = root.items || [];
377
- if (isPage(node)) {
378
- this.state.pageLength -= 1;
379
- if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
380
- } else if (isPageFragment(node)) {
381
- this.state.pageFragmentLength -= 1;
382
- if (!doNotSelect && !doNotSwitchPage) await selectDefault(rootItems);
395
+ const shouldReselect = isDeletingCurrentPage && !doNotSelect && !doNotSwitchPage;
396
+ const shouldSwitchPage = shouldReselect && rootItems.length > 0;
397
+ if (isPageOrFragment(node)) {
398
+ if (isPage(node)) this.state.pageLength -= 1;
399
+ else this.state.pageFragmentLength -= 1;
400
+ if (shouldSwitchPage) {
401
+ await this.select(rootItems[0]);
402
+ stage?.select(rootItems[0].id);
403
+ await nextTick();
404
+ stage?.remove(removeData);
405
+ } else {
406
+ stage?.remove(removeData);
407
+ if (shouldReselect) this.selectRoot();
408
+ else if (isDeletingCurrentPage) this.set("page", null);
409
+ }
383
410
  } else {
411
+ stage?.remove(removeData);
384
412
  if (!doNotSelect) {
385
413
  await this.select(parent);
386
414
  stage?.select(parent.id);
@@ -400,12 +428,17 @@ var Editor = class extends BaseService {
400
428
  * @param options.doNotSwitchPage 删除后是否不切换当前页面(默认 false;删除页面 / 页面片段时为 true 会跳过自动切换到首个剩余页面)
401
429
  * @param options.doNotPushHistory 是否不写入历史记录(默认 false)
402
430
  */
403
- async remove(nodeOrNodeList, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
431
+ async remove(nodeOrNodeList, options = {}) {
432
+ const { doNotPushHistory = false, historyDescription, historySource } = options;
404
433
  this.captureSelectionBeforeOp();
405
434
  const nodes = Array.isArray(nodeOrNodeList) ? nodeOrNodeList : [nodeOrNodeList];
435
+ const changeItems = nodes.map((node) => ({
436
+ node,
437
+ page: this.getPageOfNode(node.id)
438
+ }));
406
439
  const removedItems = [];
407
440
  let pageForOp = null;
408
- if (!(isPage(nodes[0]) || isPageFragment(nodes[0]))) for (const n of nodes) {
441
+ if (!isPageOrFragment(nodes[0])) for (const n of nodes) {
409
442
  const { parent, node: curNode, page } = this.getNodeInfo(n.id, false);
410
443
  if (parent && curNode) {
411
444
  if (!pageForOp && page) pageForOp = {
@@ -420,27 +453,39 @@ var Editor = class extends BaseService {
420
453
  });
421
454
  }
422
455
  }
423
- await Promise.all(nodes.map((node) => this.doRemove(node, {
424
- doNotSelect,
425
- doNotSwitchPage
426
- })));
427
- if (removedItems.length > 0 && pageForOp) if (!doNotPushHistory) this.pushOpHistory("remove", {
428
- diff: removedItems,
429
- pageData: pageForOp,
430
- historyDescription,
431
- source: historySource
432
- });
433
- else this.selectionBeforeOp = null;
456
+ await Promise.all(nodes.map((node) => this.doRemove(node, options)));
457
+ this.removeInvalidNodesBySubtree(nodes);
458
+ if (removedItems.length > 0 && pageForOp) {
459
+ if (!doNotPushHistory) this.pushOpHistory("remove", {
460
+ diff: removedItems,
461
+ pageData: pageForOp,
462
+ historyDescription,
463
+ source: historySource
464
+ });
465
+ else this.selectionBeforeOp = null;
466
+ }
434
467
  this.emit("remove", nodes);
468
+ this.emit("change", {
469
+ type: "remove",
470
+ data: changeItems,
471
+ historySource,
472
+ doNotPushHistory
473
+ });
474
+ const removedPages = nodes.filter((node) => isPageOrFragment(node));
475
+ if (removedPages.length) history_default.notifyPageStructureChange({
476
+ add: [],
477
+ remove: removedPages
478
+ });
435
479
  }
436
- async doUpdate(config, { changeRecords = [], historySource } = {}) {
480
+ async doUpdate(config, data = {}) {
481
+ const { changeRecords = [], historySource, replace = false } = data;
437
482
  if (!this.get("root")) throw new Error("root为空");
438
483
  if (!config?.id) throw new Error("没有配置或者配置缺少id值");
439
484
  const info = this.getNodeInfo(config.id, false);
440
485
  if (!info.node) throw new Error(`获取不到id为${config.id}的节点`);
441
486
  const node = toRaw(info.node);
442
- let newConfig = await toggleFixedPosition(toRaw(config), node, info.path, this.getLayout);
443
- newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
487
+ let newConfig = replace ? cloneDeep$1(toRaw(config)) : await toggleFixedPosition(toRaw(config), node, info.path, this.getLayout);
488
+ if (!replace) newConfig = mergeWith(cloneDeep$1(node), newConfig, editorNodeMergeCustomizer);
444
489
  if (!newConfig.type) throw new Error("配置缺少type值");
445
490
  if (newConfig.type === NodeType.ROOT) {
446
491
  this.set("root", newConfig, { historySource });
@@ -455,9 +500,11 @@ var Editor = class extends BaseService {
455
500
  const parentNodeItems = parent.items;
456
501
  const index = getNodeIndex(newConfig.id, parent);
457
502
  if (!parentNodeItems || typeof index === "undefined" || index === -1) throw new Error("更新的节点未找到");
458
- const newLayout = await this.getLayout(newConfig);
459
- const layout = await this.getLayout(node);
460
- if (Array.isArray(newConfig.items) && newLayout !== layout) newConfig = setChildrenLayout(newConfig, newLayout);
503
+ if (!replace) {
504
+ const newLayout = await this.getLayout(newConfig);
505
+ const layout = await this.getLayout(node);
506
+ if (Array.isArray(newConfig.items) && newLayout !== layout) newConfig = setChildrenLayout(newConfig, newLayout);
507
+ }
461
508
  parentNodeItems[index] = newConfig;
462
509
  const selectedNodes = this.get("nodes");
463
510
  const targetIndex = selectedNodes.findIndex((nodeItem) => `${nodeItem.id}` === `${newConfig.id}`);
@@ -465,7 +512,7 @@ var Editor = class extends BaseService {
465
512
  selectedNodes.splice(targetIndex, 1, newConfig);
466
513
  this.set("nodes", [...selectedNodes]);
467
514
  }
468
- if (isPage(newConfig) || isPageFragment(newConfig)) {
515
+ if (isPageOrFragment(newConfig)) {
469
516
  const currentPage = this.get("page");
470
517
  if (currentPage && `${currentPage.id}` === `${newConfig.id}`) this.set("page", newConfig);
471
518
  }
@@ -480,43 +527,51 @@ var Editor = class extends BaseService {
480
527
  * 更新节点
481
528
  * update后会触发依赖收集,收集完后会掉stage.update方法
482
529
  * @param config 新的节点配置,配置中需要有id信息
483
- * @param data 额外数据
484
- * @param data.changeRecords 单节点 form 端变更记录(多节点场景下被忽略,使用 changeRecordList)
485
- * @param data.changeRecordList 多节点 form 端变更记录列表,按 config 数组同序对应每个节点;优先级高于 changeRecords
486
- * @param data.doNotPushHistory 是否不写入历史记录(默认 false)
487
- * @param data.historyDescription 入栈时附带的人类可读描述,用于历史面板展示(不影响 undo/redo 行为)
530
+ * @param data 额外数据,见 {@link UpdateOptions}
488
531
  * @returns 更新后的节点配置
489
532
  */
490
533
  async update(config, data = {}) {
491
534
  this.captureSelectionBeforeOp();
492
- const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource } = data;
535
+ const { doNotPushHistory = false, changeRecordList, changeRecords, historyDescription, historySource, invalidInfo } = data;
493
536
  const nodes = Array.isArray(config) ? config : [config];
494
537
  const updateData = await Promise.all(nodes.map((node, index) => {
495
538
  const recordsForNode = changeRecordList ? changeRecordList[index] ?? [] : changeRecords ?? [];
496
539
  return this.doUpdate(node, {
497
- changeRecords: recordsForNode,
498
- historySource
540
+ ...data,
541
+ changeRecords: recordsForNode
499
542
  });
500
543
  }));
544
+ this.applyInvalidInfo(config, invalidInfo);
501
545
  if (updateData[0].oldNode?.type !== NodeType.ROOT) {
502
- if (this.get("nodes").length) if (!doNotPushHistory) {
503
- const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
504
- this.pushOpHistory("update", {
505
- diff: buildUpdateDiff(updateData.map((d) => ({
506
- oldNode: cloneDeep$1(d.oldNode),
507
- newNode: cloneDeep$1(d.newNode),
508
- changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
509
- }))),
510
- pageData: {
511
- name: pageForOp?.name || "",
512
- id: pageForOp.id
513
- },
514
- historyDescription,
515
- source: historySource
516
- });
517
- } else this.selectionBeforeOp = null;
546
+ if (this.get("nodes").length) {
547
+ if (!doNotPushHistory) {
548
+ const pageForOp = this.getNodeInfo(nodes[0].id, false).page;
549
+ this.pushOpHistory("update", {
550
+ diff: buildUpdateDiff(updateData.map((d) => ({
551
+ oldNode: cloneDeep$1(d.oldNode),
552
+ newNode: cloneDeep$1(d.newNode),
553
+ changeRecords: d.changeRecords?.length ? cloneDeep$1(d.changeRecords) : void 0
554
+ }))),
555
+ pageData: {
556
+ name: pageForOp?.name || "",
557
+ id: pageForOp.id
558
+ },
559
+ historyDescription,
560
+ source: historySource
561
+ });
562
+ } else this.selectionBeforeOp = null;
563
+ }
518
564
  }
519
565
  this.emit("update", updateData);
566
+ this.emit("change", {
567
+ type: "update",
568
+ data: updateData.map((node) => ({
569
+ node,
570
+ page: this.getPageOfNode(node.newNode.id)
571
+ })),
572
+ historySource,
573
+ doNotPushHistory
574
+ });
520
575
  return Array.isArray(config) ? updateData.map((item) => item.newNode) : updateData[0].newNode;
521
576
  }
522
577
  /**
@@ -633,8 +688,10 @@ var Editor = class extends BaseService {
633
688
  historyDescription,
634
689
  historySource
635
690
  });
636
- if (!doNotSelect) if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
637
- else await stage?.select(newNodes[0].id);
691
+ if (!doNotSelect) {
692
+ if (newNodes.length > 1) await stage?.multiSelect(newNodes.map((node) => node.id));
693
+ else await stage?.select(newNodes[0].id);
694
+ }
638
695
  return newNode;
639
696
  }
640
697
  /**
@@ -682,6 +739,16 @@ var Editor = class extends BaseService {
682
739
  });
683
740
  } else this.selectionBeforeOp = null;
684
741
  this.emit("move-layer", offset);
742
+ this.emit("change", {
743
+ type: "move-layer",
744
+ data: [{
745
+ node,
746
+ page: this.getPageOfNode(node.id)
747
+ }],
748
+ offset,
749
+ historySource,
750
+ doNotPushHistory
751
+ });
685
752
  }
686
753
  /**
687
754
  * 移动一个或多个节点到指定容器中。
@@ -699,7 +766,7 @@ var Editor = class extends BaseService {
699
766
  */
700
767
  async moveToContainer(config, targetId, { doNotSelect = false, doNotSwitchPage = false, doNotPushHistory = false, historyDescription, historySource } = {}) {
701
768
  const isBatch = Array.isArray(config);
702
- const configs = (isBatch ? config : [config]).filter((item) => !(isPage(item) || isPageFragment(item)));
769
+ const configs = (isBatch ? config : [config]).filter((item) => !isPageOrFragment(item));
703
770
  if (configs.length === 0) throw new Error("没有可移动的节点");
704
771
  this.captureSelectionBeforeOp();
705
772
  const target = this.getNodeById(targetId, false);
@@ -811,6 +878,17 @@ var Editor = class extends BaseService {
811
878
  configs,
812
879
  targetParent
813
880
  });
881
+ this.emit("change", {
882
+ type: "drag-to",
883
+ data: configs.map((node) => ({
884
+ node,
885
+ page: this.getPageOfNode(node.id)
886
+ })),
887
+ targetIndex,
888
+ targetParent,
889
+ historySource,
890
+ doNotPushHistory
891
+ });
814
892
  }
815
893
  /**
816
894
  * 下列 *AndGetHistoryId 方法与对应的普通操作(add / remove / update ...)行为完全一致,
@@ -875,7 +953,9 @@ var Editor = class extends BaseService {
875
953
  * @returns 被撤销的操作
876
954
  */
877
955
  async undo() {
878
- const value = history_default.undo();
956
+ const pageId = this.get("page")?.id;
957
+ if (pageId === void 0) return null;
958
+ const value = history_default.undo("page", pageId);
879
959
  if (value) await this.applyHistoryOp(value, true);
880
960
  return value;
881
961
  }
@@ -884,7 +964,9 @@ var Editor = class extends BaseService {
884
964
  * @returns 被恢复的操作
885
965
  */
886
966
  async redo() {
887
- const value = history_default.redo();
967
+ const pageId = this.get("page")?.id;
968
+ if (pageId === void 0) return null;
969
+ const value = history_default.redo("page", pageId);
888
970
  if (value) await this.applyHistoryOp(value, false);
889
971
  return value;
890
972
  }
@@ -905,7 +987,7 @@ var Editor = class extends BaseService {
905
987
  * @returns 反向后产生的新 step;目标不存在 / 未应用 / 反向失败时返回 null
906
988
  */
907
989
  async revertPageStep(index) {
908
- const entry = history_default.getPageStepList()[index];
990
+ const entry = history_default.getStepList("page", this.get("page")?.id)[index];
909
991
  if (!entry?.applied) return null;
910
992
  const { step } = entry;
911
993
  if (step.opType === "initial") return null;
@@ -915,7 +997,7 @@ var Editor = class extends BaseService {
915
997
  if (!items.length || !items.every((item) => item.changeRecords?.length)) return null;
916
998
  }
917
999
  let revertedStep = null;
918
- const captureRevert = (s) => {
1000
+ const captureRevert = (_pageId, s) => {
919
1001
  revertedStep = s;
920
1002
  };
921
1003
  history_default.once("change", captureRevert);
@@ -985,9 +1067,10 @@ var Editor = class extends BaseService {
985
1067
  */
986
1068
  async revertPageStepById(uuids) {
987
1069
  const results = [];
1070
+ const pageId = this.get("page")?.id;
988
1071
  for (const uuid of uuids) {
989
- const index = history_default.getPageStepIndexByUuid(uuid);
990
- results.push(index < 0 ? null : await this.revertPageStep(index));
1072
+ const location = history_default.findStepLocationByUuid("page", uuid, pageId);
1073
+ results.push(!location ? null : await this.revertPageStep(location.index));
991
1074
  }
992
1075
  return results;
993
1076
  }
@@ -1001,8 +1084,9 @@ var Editor = class extends BaseService {
1001
1084
  * @returns 实际移动到的最终游标位置
1002
1085
  */
1003
1086
  async gotoPageStep(targetCursor) {
1004
- let cursor = history_default.getPageCursor();
1005
- const { length } = history_default.getPageStepList();
1087
+ const pageId = this.get("page")?.id;
1088
+ let cursor = history_default.getCursor("page", pageId);
1089
+ const { length } = history_default.getStepList("page", pageId);
1006
1090
  const target = Math.max(0, Math.min(targetCursor, length));
1007
1091
  while (cursor > target) {
1008
1092
  if (!await this.undo()) break;
@@ -1038,6 +1122,7 @@ var Editor = class extends BaseService {
1038
1122
  this.set("stage", null);
1039
1123
  this.set("highlightNode", null);
1040
1124
  this.set("modifiedNodeIds", /* @__PURE__ */ new Map());
1125
+ this.set("invalidNodeIds", /* @__PURE__ */ new Map());
1041
1126
  this.set("pageLength", 0);
1042
1127
  }
1043
1128
  destroy() {
@@ -1048,6 +1133,51 @@ var Editor = class extends BaseService {
1048
1133
  resetModifiedNodeId() {
1049
1134
  this.get("modifiedNodeIds").clear();
1050
1135
  }
1136
+ /**
1137
+ * 记录(或覆盖)某个节点在指定来源(属性表单 / 样式表单)上的校验错误信息。
1138
+ * @param id 节点 id
1139
+ * @param source 错误来源:'props'(属性表单)| 'style'(样式表单)
1140
+ * @param message 错误文案(可能是包含 <br> 的 HTML)
1141
+ */
1142
+ setInvalidNode(id, source, message) {
1143
+ const map = this.get("invalidNodeIds");
1144
+ const info = { ...map.get(id) || {} };
1145
+ info[source] = message;
1146
+ map.set(id, info);
1147
+ this.emit("invalid-node-change", map);
1148
+ }
1149
+ /**
1150
+ * 删除节点的校验错误记录。
1151
+ * @param id 节点 id
1152
+ * @param source 指定来源则仅删除该来源;不传则删除该节点全部来源的错误
1153
+ */
1154
+ deleteInvalidNode(id, source) {
1155
+ const map = this.get("invalidNodeIds");
1156
+ if (!map.has(id)) return;
1157
+ if (!source) map.delete(id);
1158
+ else {
1159
+ const info = { ...map.get(id) || {} };
1160
+ delete info[source];
1161
+ if (info.props || info.style) map.set(id, info);
1162
+ else map.delete(id);
1163
+ }
1164
+ this.emit("invalid-node-change", map);
1165
+ }
1166
+ /** 获取当前存在校验错误的节点错误 Map(key 为节点 id) */
1167
+ getInvalidNodeIds() {
1168
+ return this.get("invalidNodeIds");
1169
+ }
1170
+ /** 获取指定节点的校验错误信息 */
1171
+ getInvalidNodeInfo(id) {
1172
+ return this.get("invalidNodeIds").get(id);
1173
+ }
1174
+ /** 清空全部校验错误记录 */
1175
+ resetInvalidNodeId() {
1176
+ const map = this.get("invalidNodeIds");
1177
+ if (map.size === 0) return;
1178
+ map.clear();
1179
+ this.emit("invalid-node-change", map);
1180
+ }
1051
1181
  usePlugin(options) {
1052
1182
  super.usePlugin(options);
1053
1183
  }
@@ -1060,12 +1190,63 @@ var Editor = class extends BaseService {
1060
1190
  emit(eventName, ...args) {
1061
1191
  return super.emit(eventName, ...args);
1062
1192
  }
1193
+ /**
1194
+ * 应用一次属性面板提交携带的校验错误信息(在写入历史记录之前调用,使历史快照与本次变更对齐)。
1195
+ * - invalidInfo 为空(调用方未携带):认为本次更新对应的节点已无校验错误,清除其全部来源的错误;
1196
+ * - error 非空则记录错误,为空则清除对应来源的错误。
1197
+ */
1198
+ applyInvalidInfo(config, invalidInfo) {
1199
+ if (!invalidInfo) {
1200
+ (Array.isArray(config) ? config : [config]).map((node) => node.id).filter((id) => id !== void 0).forEach((id) => this.deleteInvalidNode(id));
1201
+ return;
1202
+ }
1203
+ const { id, source, error } = invalidInfo;
1204
+ if (error) this.setInvalidNode(id, source, error);
1205
+ else this.deleteInvalidNode(id, source);
1206
+ }
1207
+ /** 删除被移除节点(含其子树)的校验错误记录,避免残留误报 */
1208
+ removeInvalidNodesBySubtree(nodes) {
1209
+ const map = this.get("invalidNodeIds");
1210
+ if (map.size === 0) return;
1211
+ let changed = false;
1212
+ nodes.forEach((node) => {
1213
+ traverseNode(node, (n) => {
1214
+ if (n.id !== void 0 && map.delete(n.id)) changed = true;
1215
+ });
1216
+ });
1217
+ if (changed) this.emit("invalid-node-change", map);
1218
+ }
1219
+ /** 清理不在当前 DSL 中的失效节点错误记录(用于整体替换 root 后) */
1220
+ pruneInvalidNodeIds() {
1221
+ const map = this.get("invalidNodeIds");
1222
+ if (map.size === 0) return;
1223
+ let changed = false;
1224
+ for (const id of [...map.keys()]) if (!this.getNodeById(id, false)) {
1225
+ map.delete(id);
1226
+ changed = true;
1227
+ }
1228
+ if (changed) this.emit("invalid-node-change", map);
1229
+ }
1063
1230
  addModifiedNodeId(id) {
1064
1231
  this.get("modifiedNodeIds").set(id, id);
1065
1232
  }
1233
+ /**
1234
+ * 获取指定节点所属的页面 / 页面片:
1235
+ * - 普通节点返回其所在的 page;
1236
+ * - 节点本身就是 page / pageFragment 时返回它自己;
1237
+ * - 找不到时返回 null。
1238
+ * 供 `change` 事件携带「变更节点对应的 page」(而非编辑器当前选中页)。
1239
+ */
1240
+ getPageOfNode(id) {
1241
+ const { node, page } = this.getNodeInfo(id, false);
1242
+ if (page) return page;
1243
+ if (node && isPageOrFragment(node)) return node;
1244
+ return null;
1245
+ }
1066
1246
  captureSelectionBeforeOp() {
1067
1247
  if (this.selectionBeforeOp) return;
1068
1248
  this.selectionBeforeOp = this.get("nodes").map((n) => n.id);
1249
+ this.invalidNodeIdsBeforeOp = new Map(this.get("invalidNodeIds"));
1069
1250
  }
1070
1251
  /**
1071
1252
  * 比较「上一次 root」与「新 root」的页面 / 页面片,按页面粒度把整体替换拆成历史记录:
@@ -1082,24 +1263,35 @@ var Editor = class extends BaseService {
1082
1263
  const prevMap = new Map(prevPages.map((p) => [`${p.id}`, p]));
1083
1264
  const nextMap = new Map(nextPages.map((p) => [`${p.id}`, p]));
1084
1265
  const indexInItems = (root, id) => (root.items ?? []).findIndex((item) => `${item.id}` === `${id}`);
1266
+ const addedPages = [];
1267
+ const removedPages = [];
1085
1268
  nextPages.forEach((nextPage) => {
1086
1269
  const prevPage = prevMap.get(`${nextPage.id}`);
1087
- if (!prevPage) this.pushPageDiffStep("add", nextPage, {
1088
- newSchema: cloneDeep$1(toRaw(nextPage)),
1089
- parentId: nextRoot.id,
1090
- index: indexInItems(nextRoot, nextPage.id)
1091
- }, source);
1092
- else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
1270
+ if (!prevPage) {
1271
+ this.pushPageDiffStep("add", nextPage, {
1272
+ newSchema: cloneDeep$1(toRaw(nextPage)),
1273
+ parentId: nextRoot.id,
1274
+ index: indexInItems(nextRoot, nextPage.id)
1275
+ }, source);
1276
+ addedPages.push(nextPage);
1277
+ } else if (!isEqual(toRaw(prevPage), toRaw(nextPage))) this.pushPageDiffStep("update", nextPage, {
1093
1278
  oldSchema: cloneDeep$1(toRaw(prevPage)),
1094
1279
  newSchema: cloneDeep$1(toRaw(nextPage))
1095
1280
  }, source);
1096
1281
  });
1097
1282
  prevPages.forEach((prevPage) => {
1098
- if (!nextMap.has(`${prevPage.id}`)) this.pushPageDiffStep("remove", prevPage, {
1099
- oldSchema: cloneDeep$1(toRaw(prevPage)),
1100
- parentId: preRoot.id,
1101
- index: indexInItems(preRoot, prevPage.id)
1102
- }, source);
1283
+ if (!nextMap.has(`${prevPage.id}`)) {
1284
+ this.pushPageDiffStep("remove", prevPage, {
1285
+ oldSchema: cloneDeep$1(toRaw(prevPage)),
1286
+ parentId: preRoot.id,
1287
+ index: indexInItems(preRoot, prevPage.id)
1288
+ }, source);
1289
+ removedPages.push(prevPage);
1290
+ }
1291
+ });
1292
+ if (addedPages.length || removedPages.length) history_default.notifyPageStructureChange({
1293
+ add: addedPages,
1294
+ remove: removedPages
1103
1295
  });
1104
1296
  }
1105
1297
  /**
@@ -1117,41 +1309,50 @@ var Editor = class extends BaseService {
1117
1309
  id: page.id
1118
1310
  },
1119
1311
  opType,
1120
- selectedBefore: [],
1121
- selectedAfter: [],
1122
- modifiedNodeIds: /* @__PURE__ */ new Map(),
1312
+ extra: {
1313
+ selectedBefore: [],
1314
+ selectedAfter: [],
1315
+ modifiedNodeIds: /* @__PURE__ */ new Map()
1316
+ },
1123
1317
  diff: [diffItem],
1124
1318
  rootStep: true
1125
1319
  };
1126
1320
  if (source) step.source = source;
1127
1321
  const top = history_default.getCurrentPageStep(page.id);
1128
- if (top?.rootStep && top.source === source) history_default.replaceCurrentPageStep(step, page.id);
1129
- else history_default.push(step, page.id);
1322
+ if (top?.rootStep && top.source === source) history_default.replaceCurrentStep("page", step, page.id);
1323
+ else history_default.push("page", step, page.id);
1130
1324
  }
1131
1325
  pushOpHistory(opType, { diff, pageData, historyDescription, source }) {
1132
1326
  const step = {
1133
1327
  uuid: guid(),
1134
1328
  data: pageData,
1135
1329
  opType,
1136
- selectedBefore: this.selectionBeforeOp ?? [],
1137
- selectedAfter: this.get("nodes").map((n) => n.id),
1138
- modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
1330
+ extra: {
1331
+ selectedBefore: this.selectionBeforeOp ?? [],
1332
+ selectedAfter: this.get("nodes").map((n) => n.id),
1333
+ modifiedNodeIds: new Map(this.get("modifiedNodeIds")),
1334
+ invalidNodeIdsBefore: new Map(this.invalidNodeIdsBeforeOp ?? this.get("invalidNodeIds")),
1335
+ invalidNodeIdsAfter: new Map(this.get("invalidNodeIds"))
1336
+ },
1139
1337
  diff
1140
1338
  };
1141
1339
  if (historyDescription) step.historyDescription = historyDescription;
1142
1340
  if (source) step.source = source;
1143
- const historyId = history_default.push(step, pageData.id) ? step.uuid : null;
1341
+ const historyId = history_default.push("page", step, pageData.id) ? step.uuid : null;
1144
1342
  this.lastPushedHistoryId = historyId;
1145
1343
  this.selectionBeforeOp = null;
1344
+ this.invalidNodeIdsBeforeOp = null;
1146
1345
  return historyId;
1147
1346
  }
1148
1347
  /**
1149
1348
  * 应用历史操作(撤销 / 重做)
1150
1349
  *
1151
- * 所有 DSL 修改都走 `editor.add / remove / update`,并通过 `doNotPushHistory` 阻止再次入栈、
1152
- * `doNotSelect / doNotSwitchPage` 让选区由方法末尾的统一逻辑兜底。
1350
+ * 删除 / 更新类修改走 `editor.remove / update`,并通过 `doNotPushHistory` 阻止再次入栈、
1351
+ * `doNotSelect / doNotSwitchPage` 让选区由方法末尾的统一逻辑兜底;
1352
+ * 「重新插回节点」(撤销 remove / 重做 add)需按 step 记录的 parentId / index 精确还原,
1353
+ * 不走 this.add,由手工 splice + stage.add 完成后补发等价的 add / change 事件(见 emitHistoryInsertEvents)。
1153
1354
  *
1154
- * 注意:这些公开方法会发出 add / remove / update 事件,业务侧若需要区分"用户操作"与"撤销重做触发",
1355
+ * 注意:这些路径都会发出 add / remove / update / change 事件,业务侧若需要区分"用户操作"与"撤销重做触发",
1155
1356
  * 请监听 `history-change` 事件配合判断。
1156
1357
  *
1157
1358
  * @param step 操作记录
@@ -1165,7 +1366,8 @@ var Editor = class extends BaseService {
1165
1366
  const commonOpts = {
1166
1367
  doNotSelect: true,
1167
1368
  doNotSwitchPage: true,
1168
- doNotPushHistory: true
1369
+ doNotPushHistory: true,
1370
+ historySource: step.source
1169
1371
  };
1170
1372
  switch (step.opType) {
1171
1373
  case "add": {
@@ -1177,20 +1379,25 @@ var Editor = class extends BaseService {
1177
1379
  }
1178
1380
  else {
1179
1381
  const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1382
+ const addedNodes = [];
1180
1383
  for (const { newSchema, parentId, index } of sorted) {
1181
1384
  if (!newSchema || parentId === void 0) continue;
1182
1385
  const parent = this.getNodeById(parentId, false);
1183
1386
  if (parent?.items) {
1184
- if (typeof index === "number" && index >= 0 && index < parent.items.length) parent.items.splice(index, 0, cloneDeep$1(newSchema));
1185
- else parent.items.push(cloneDeep$1(newSchema));
1387
+ const addedNode = cloneDeep$1(newSchema);
1388
+ const insertIndex = clampIndex(index, parent.items.length);
1389
+ parent.items.splice(insertIndex, 0, addedNode);
1390
+ addedNodes.push(addedNode);
1186
1391
  await stage?.add({
1187
1392
  config: cloneDeep$1(newSchema),
1188
1393
  parent: cloneDeep$1(parent),
1189
1394
  parentId: parent.id,
1190
- root: cloneDeep$1(root)
1395
+ root: cloneDeep$1(root),
1396
+ index: insertIndex
1191
1397
  });
1192
1398
  }
1193
1399
  }
1400
+ this.emitHistoryInsertEvents(addedNodes, step.source);
1194
1401
  }
1195
1402
  break;
1196
1403
  }
@@ -1198,19 +1405,25 @@ var Editor = class extends BaseService {
1198
1405
  const items = step.diff ?? [];
1199
1406
  if (reverse) {
1200
1407
  const sorted = [...items].sort((a, b) => (a.index ?? 0) - (b.index ?? 0));
1408
+ const addedNodes = [];
1201
1409
  for (const { oldSchema, parentId, index } of sorted) {
1202
1410
  if (!oldSchema || parentId === void 0) continue;
1203
1411
  const parent = this.getNodeById(parentId, false);
1204
1412
  if (parent?.items) {
1205
- parent.items.splice(index ?? parent.items.length, 0, cloneDeep$1(oldSchema));
1413
+ const addedNode = cloneDeep$1(oldSchema);
1414
+ const insertIndex = clampIndex(index, parent.items.length);
1415
+ parent.items.splice(insertIndex, 0, addedNode);
1416
+ addedNodes.push(addedNode);
1206
1417
  await stage?.add({
1207
1418
  config: cloneDeep$1(oldSchema),
1208
1419
  parent: cloneDeep$1(parent),
1209
1420
  parentId,
1210
- root: cloneDeep$1(root)
1421
+ root: cloneDeep$1(root),
1422
+ index: insertIndex
1211
1423
  });
1212
1424
  }
1213
1425
  }
1426
+ this.emitHistoryInsertEvents(addedNodes, step.source);
1214
1427
  } else for (const { oldSchema } of items) {
1215
1428
  if (!oldSchema) continue;
1216
1429
  const existing = this.getNodeById(oldSchema.id, false);
@@ -1237,14 +1450,20 @@ var Editor = class extends BaseService {
1237
1450
  }
1238
1451
  return cloneDeep$1(reverse ? oldNode : newNode);
1239
1452
  });
1240
- if (configs.length) await this.update(configs, { doNotPushHistory: true });
1453
+ if (configs.length) await this.update(configs, {
1454
+ doNotPushHistory: true,
1455
+ historySource: step.source
1456
+ });
1241
1457
  break;
1242
1458
  }
1243
1459
  }
1244
- this.set("modifiedNodeIds", step.modifiedNodeIds);
1460
+ this.set("modifiedNodeIds", step.extra?.modifiedNodeIds ?? /* @__PURE__ */ new Map());
1461
+ const invalidToRestore = (reverse ? step.extra?.invalidNodeIdsBefore : step.extra?.invalidNodeIdsAfter) ?? /* @__PURE__ */ new Map();
1462
+ this.set("invalidNodeIds", new Map(invalidToRestore));
1463
+ this.emit("invalid-node-change", this.get("invalidNodeIds"));
1245
1464
  const page = toRaw(this.get("page"));
1246
1465
  if (page) {
1247
- const selectIds = reverse ? step.selectedBefore : step.selectedAfter;
1466
+ const selectIds = (reverse ? step.extra?.selectedBefore : step.extra?.selectedAfter) ?? [];
1248
1467
  setTimeout(() => {
1249
1468
  if (!selectIds.length) return;
1250
1469
  if (selectIds.length > 1) {
@@ -1258,6 +1477,23 @@ var Editor = class extends BaseService {
1258
1477
  selectedConfigExceptionHandler(config) {
1259
1478
  return resolveSelectedNode(config, (id) => this.getNodeInfo(id), this.state.root?.id);
1260
1479
  }
1480
+ /**
1481
+ * 撤销 remove / 重做 add 通过手工 splice 按 step 记录的 parentId / index 精确还原节点(不走 this.add,原因见调用处注释),
1482
+ * 这里补齐与 this.add 等价的 add / change 事件,保证撤销/重做路径的事件行为与正向操作一致。
1483
+ */
1484
+ emitHistoryInsertEvents(nodes, source) {
1485
+ if (!nodes.length) return;
1486
+ this.emit("add", nodes);
1487
+ this.emit("change", {
1488
+ type: "add",
1489
+ data: nodes.map((node) => ({
1490
+ node,
1491
+ page: this.getPageOfNode(node.id)
1492
+ })),
1493
+ historySource: source,
1494
+ doNotPushHistory: true
1495
+ });
1496
+ }
1261
1497
  };
1262
1498
  var editor_default = new Editor();
1263
1499
  //#endregion