@formicoidea/labre-core 0.43.1 → 0.45.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 (263) hide show
  1. package/dist/_pkgs/affine-block-attachment/utils.js +4 -0
  2. package/dist/_pkgs/affine-block-bookmark/configs/toolbar.js +17 -4
  3. package/dist/_pkgs/affine-block-callout/commands/turn-into-callout.d.ts +29 -0
  4. package/dist/_pkgs/affine-block-callout/commands/turn-into-callout.js +81 -0
  5. package/dist/_pkgs/affine-block-callout/configs/slash-menu.js +6 -4
  6. package/dist/_pkgs/affine-block-callout/configs/turn-into.d.ts +8 -0
  7. package/dist/_pkgs/affine-block-callout/configs/turn-into.js +19 -0
  8. package/dist/_pkgs/affine-block-callout/translations.d.ts +4 -2
  9. package/dist/_pkgs/affine-block-callout/translations.js +4 -2
  10. package/dist/_pkgs/affine-block-callout/view.js +3 -0
  11. package/dist/_pkgs/affine-block-code/code-block-service.d.ts +13 -0
  12. package/dist/_pkgs/affine-block-code/code-block-service.js +26 -0
  13. package/dist/_pkgs/affine-block-code/code-block.d.ts +6 -0
  14. package/dist/_pkgs/affine-block-code/code-block.js +11 -2
  15. package/dist/_pkgs/affine-block-code/code-toolbar/components/lang-button.js +31 -8
  16. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/config.d.ts +11 -0
  17. package/dist/_pkgs/affine-block-edgeless-text/svg-painter.d.ts +20 -0
  18. package/dist/_pkgs/affine-block-edgeless-text/svg-painter.js +76 -0
  19. package/dist/_pkgs/affine-block-edgeless-text/view.js +2 -0
  20. package/dist/_pkgs/affine-block-embed/configs/toolbar.js +11 -2
  21. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +11 -2
  22. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-edgeless-linked-doc-block.js +3 -1
  23. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +7 -2
  24. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-edgeless-synced-doc-block.js +3 -1
  25. package/dist/_pkgs/affine-block-frame/frame-manager.d.ts +5 -0
  26. package/dist/_pkgs/affine-block-frame/frame-manager.js +48 -17
  27. package/dist/_pkgs/affine-block-frame/frame-tool.js +3 -1
  28. package/dist/_pkgs/affine-block-image/utils.js +4 -1
  29. package/dist/_pkgs/affine-block-latex/adapters/markdown/preprocessor.d.ts +8 -0
  30. package/dist/_pkgs/affine-block-latex/adapters/markdown/preprocessor.js +9 -5
  31. package/dist/_pkgs/affine-block-note/translations.d.ts +0 -1
  32. package/dist/_pkgs/affine-block-note/translations.js +0 -5
  33. package/dist/_pkgs/affine-block-root/configs/toolbar.js +17 -1
  34. package/dist/_pkgs/affine-block-root/edgeless/clipboard/clipboard.js +2 -1
  35. package/dist/_pkgs/affine-block-root/edgeless/clipboard/command.js +161 -83
  36. package/dist/_pkgs/affine-block-root/edgeless/clipboard/paste-order.d.ts +21 -0
  37. package/dist/_pkgs/affine-block-root/edgeless/clipboard/paste-order.js +85 -0
  38. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.d.ts +6 -0
  39. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.js +18 -1
  40. package/dist/_pkgs/affine-block-root/edgeless/utils/clone-utils.d.ts +9 -0
  41. package/dist/_pkgs/affine-block-root/edgeless/utils/clone-utils.js +28 -0
  42. package/dist/_pkgs/affine-block-root/keyboard/commands.js +9 -6
  43. package/dist/_pkgs/affine-block-root/preview/edgeless-root-preview-block.js +15 -2
  44. package/dist/_pkgs/affine-block-surface/adapters/markdown/element-adapter/type.d.ts +1 -1
  45. package/dist/_pkgs/affine-block-surface/element-model/index.d.ts +1 -1
  46. package/dist/_pkgs/affine-block-surface/element-model/index.js +1 -1
  47. package/dist/_pkgs/affine-block-surface/extensions/audit.js +13 -61
  48. package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/commands.d.ts +2 -0
  49. package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/commands.js +71 -0
  50. package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/index.d.ts +1 -0
  51. package/dist/_pkgs/affine-block-surface/extensions/canvas-grid/index.js +1 -0
  52. package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.js +4 -0
  53. package/dist/_pkgs/affine-block-surface/extensions/crud-extension.js +4 -0
  54. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.d.ts +12 -0
  55. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +27 -7
  56. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.d.ts +2 -1
  57. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.js +22 -3
  58. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.d.ts +6 -3
  59. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.js +4 -2
  60. package/dist/_pkgs/affine-block-surface/extensions/export-svg/parts.d.ts +63 -0
  61. package/dist/_pkgs/affine-block-surface/extensions/export-svg/parts.js +67 -0
  62. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.d.ts +62 -5
  63. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.js +98 -18
  64. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.d.ts +14 -0
  65. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.js +82 -1
  66. package/dist/_pkgs/affine-block-surface/extensions/export-svg/toolbar.d.ts +2 -2
  67. package/dist/_pkgs/affine-block-surface/extensions/export-svg/toolbar.js +112 -10
  68. package/dist/_pkgs/affine-block-surface/extensions/export-svg/translations.d.ts +13 -0
  69. package/dist/_pkgs/affine-block-surface/extensions/export-svg/translations.js +33 -0
  70. package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +3 -0
  71. package/dist/_pkgs/affine-block-surface/extensions/index.js +3 -0
  72. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.d.ts +13 -6
  73. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.js +26 -10
  74. package/dist/_pkgs/affine-block-surface/extensions/reading.d.ts +82 -6
  75. package/dist/_pkgs/affine-block-surface/extensions/reading.js +99 -21
  76. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/actions.d.ts +80 -0
  77. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/actions.js +248 -0
  78. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/commands.d.ts +82 -0
  79. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/commands.js +342 -0
  80. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/index.d.ts +3 -0
  81. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/index.js +3 -0
  82. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/tree.d.ts +137 -0
  83. package/dist/_pkgs/affine-block-surface/extensions/selection-pane/tree.js +323 -0
  84. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +112 -4
  85. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +86 -15
  86. package/dist/_pkgs/affine-block-surface/extensions/user-layers/actions.d.ts +76 -0
  87. package/dist/_pkgs/affine-block-surface/extensions/user-layers/actions.js +299 -0
  88. package/dist/_pkgs/affine-block-surface/extensions/user-layers/active-layer.d.ts +20 -0
  89. package/dist/_pkgs/affine-block-surface/extensions/user-layers/active-layer.js +34 -0
  90. package/dist/_pkgs/affine-block-surface/extensions/user-layers/commands.d.ts +67 -0
  91. package/dist/_pkgs/affine-block-surface/extensions/user-layers/commands.js +172 -0
  92. package/dist/_pkgs/affine-block-surface/extensions/user-layers/creation.d.ts +40 -0
  93. package/dist/_pkgs/affine-block-surface/extensions/user-layers/creation.js +69 -0
  94. package/dist/_pkgs/affine-block-surface/extensions/user-layers/index.d.ts +5 -0
  95. package/dist/_pkgs/affine-block-surface/extensions/user-layers/index.js +5 -0
  96. package/dist/_pkgs/affine-block-surface/extensions/user-layers/translations.d.ts +10 -0
  97. package/dist/_pkgs/affine-block-surface/extensions/user-layers/translations.js +14 -0
  98. package/dist/_pkgs/affine-block-surface/framework-background/facts.d.ts +55 -2
  99. package/dist/_pkgs/affine-block-surface/framework-background/facts.js +63 -1
  100. package/dist/_pkgs/affine-block-surface/framework-background/stacking.d.ts +16 -8
  101. package/dist/_pkgs/affine-block-surface/framework-background/stacking.js +26 -14
  102. package/dist/_pkgs/affine-block-surface/renderer/canvas-renderer.js +17 -3
  103. package/dist/_pkgs/affine-block-surface/renderer/dom-renderer.d.ts +3 -1
  104. package/dist/_pkgs/affine-block-surface/renderer/dom-renderer.js +22 -2
  105. package/dist/_pkgs/affine-block-surface/renderer/paint-predicate.d.ts +30 -0
  106. package/dist/_pkgs/affine-block-surface/renderer/paint-predicate.js +29 -0
  107. package/dist/_pkgs/affine-block-surface/surface-block.js +6 -0
  108. package/dist/_pkgs/affine-block-surface/surface-model.js +5 -0
  109. package/dist/_pkgs/affine-block-surface/surface-transformer.js +5 -0
  110. package/dist/_pkgs/affine-block-surface/tool/default-tool.js +28 -5
  111. package/dist/_pkgs/affine-block-surface/view.js +23 -1
  112. package/dist/_pkgs/affine-block-surface-ref/surface-ref-block.js +12 -2
  113. package/dist/_pkgs/affine-components/context-menu/button.js +14 -0
  114. package/dist/_pkgs/affine-components/filterable-list/index.js +30 -9
  115. package/dist/_pkgs/affine-components/icons/index.d.ts +0 -1
  116. package/dist/_pkgs/affine-components/icons/index.js +0 -1
  117. package/dist/_pkgs/affine-components/icons/text.d.ts +1 -0
  118. package/dist/_pkgs/affine-components/icons/text.js +11 -0
  119. package/dist/_pkgs/affine-fragment-adapter-panel/adapter-panel.d.ts +2 -2
  120. package/dist/_pkgs/affine-fragment-frame-panel/card/frame-card.js +2 -2
  121. package/dist/_pkgs/affine-fragment-frame-panel/header/frame-panel-header.d.ts +1 -1
  122. package/dist/_pkgs/affine-fragment-frame-panel/header/frame-panel-header.js +8 -27
  123. package/dist/_pkgs/affine-fragment-frame-panel/index.d.ts +1 -0
  124. package/dist/_pkgs/affine-fragment-frame-panel/index.js +1 -0
  125. package/dist/_pkgs/affine-gfx-connector/element-renderer/connector-dom/index.js +3 -1
  126. package/dist/_pkgs/affine-gfx-connector/element-renderer/index.js +21 -4
  127. package/dist/_pkgs/affine-gfx-connector/text/edgeless-connector-label-editor.js +3 -2
  128. package/dist/_pkgs/affine-gfx-connector/toolbar/config.js +1 -1
  129. package/dist/_pkgs/affine-gfx-group/command/group-api.js +52 -1
  130. package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/index.js +21 -3
  131. package/dist/_pkgs/affine-gfx-shape/text/edgeless-shape-text-editor.js +2 -0
  132. package/dist/_pkgs/affine-gfx-shape/toolbar/config.js +1 -1
  133. package/dist/_pkgs/affine-gfx-template/services/template-middlewares.d.ts +9 -0
  134. package/dist/_pkgs/affine-gfx-template/services/template-middlewares.js +23 -1
  135. package/dist/_pkgs/affine-gfx-template/services/template.js +2 -1
  136. package/dist/_pkgs/affine-gfx-text/edgeless-text-editor.js +2 -1
  137. package/dist/_pkgs/affine-gfx-text/element-renderer/index.js +20 -3
  138. package/dist/_pkgs/affine-gfx-text/element-renderer/utils.d.ts +67 -1
  139. package/dist/_pkgs/affine-gfx-text/element-renderer/utils.js +101 -0
  140. package/dist/_pkgs/affine-gfx-text/toolbar/actions.d.ts +11 -0
  141. package/dist/_pkgs/affine-gfx-text/toolbar/actions.js +53 -2
  142. package/dist/_pkgs/affine-gfx-text/toolbar/config.d.ts +11 -0
  143. package/dist/_pkgs/affine-gfx-text/toolbar/config.js +1 -1
  144. package/dist/_pkgs/affine-inline-preset/adapters/html/html-inline.d.ts +3 -0
  145. package/dist/_pkgs/affine-inline-preset/adapters/html/html-inline.js +31 -0
  146. package/dist/_pkgs/affine-inline-preset/adapters/html/inline-delta.d.ts +4 -0
  147. package/dist/_pkgs/affine-inline-preset/adapters/html/inline-delta.js +14 -0
  148. package/dist/_pkgs/affine-inline-preset/command/config.js +21 -3
  149. package/dist/_pkgs/affine-inline-preset/command/index.d.ts +1 -1
  150. package/dist/_pkgs/affine-inline-preset/command/index.js +1 -1
  151. package/dist/_pkgs/affine-inline-preset/command/text-style.d.ts +2 -1
  152. package/dist/_pkgs/affine-inline-preset/command/text-style.js +1 -0
  153. package/dist/_pkgs/affine-inline-preset/default-inline-manager.js +2 -1
  154. package/dist/_pkgs/affine-inline-preset/inline-spec.d.ts +4 -0
  155. package/dist/_pkgs/affine-inline-preset/inline-spec.js +14 -0
  156. package/dist/_pkgs/affine-inline-preset/translations.d.ts +2 -2
  157. package/dist/_pkgs/affine-inline-preset/translations.js +2 -2
  158. package/dist/_pkgs/affine-model/blocks/attachment/attachment-model.js +2 -0
  159. package/dist/_pkgs/affine-model/blocks/bookmark/bookmark-model.js +2 -0
  160. package/dist/_pkgs/affine-model/blocks/edgeless-text/edgeless-text-model.d.ts +1 -1
  161. package/dist/_pkgs/affine-model/blocks/edgeless-text/edgeless-text-model.js +2 -0
  162. package/dist/_pkgs/affine-model/blocks/embed/iframe/iframe-model.js +2 -0
  163. package/dist/_pkgs/affine-model/blocks/frame/frame-model.js +2 -0
  164. package/dist/_pkgs/affine-model/blocks/image/image-model.js +2 -0
  165. package/dist/_pkgs/affine-model/blocks/latex/latex-model.js +2 -0
  166. package/dist/_pkgs/affine-model/blocks/note/note-model.js +2 -0
  167. package/dist/_pkgs/affine-model/consts/text.d.ts +26 -0
  168. package/dist/_pkgs/affine-model/consts/text.js +25 -0
  169. package/dist/_pkgs/affine-model/elements/shape/shape.d.ts +6 -1
  170. package/dist/_pkgs/affine-model/elements/shape/shape.js +14 -1
  171. package/dist/_pkgs/affine-model/elements/text/text.d.ts +6 -1
  172. package/dist/_pkgs/affine-model/elements/text/text.js +13 -1
  173. package/dist/_pkgs/affine-model/utils/helper.js +2 -0
  174. package/dist/_pkgs/affine-rich-text/conversion.d.ts +25 -0
  175. package/dist/_pkgs/affine-rich-text/conversion.js +9 -0
  176. package/dist/_pkgs/affine-rich-text/index.d.ts +1 -1
  177. package/dist/_pkgs/affine-rich-text/index.js +1 -1
  178. package/dist/_pkgs/affine-shared/commands/model-crud/replace-selected-text-with-blocks.d.ts +1 -1
  179. package/dist/_pkgs/affine-shared/services/canvas-grid.d.ts +63 -0
  180. package/dist/_pkgs/affine-shared/services/canvas-grid.js +109 -0
  181. package/dist/_pkgs/affine-shared/services/canvas-local-visibility.d.ts +64 -0
  182. package/dist/_pkgs/affine-shared/services/canvas-local-visibility.js +192 -0
  183. package/dist/_pkgs/affine-shared/services/edit-props-store.d.ts +27 -0
  184. package/dist/_pkgs/affine-shared/services/edit-props-store.js +34 -0
  185. package/dist/_pkgs/affine-shared/services/editor-setting-service.d.ts +3 -0
  186. package/dist/_pkgs/affine-shared/services/editor-setting-service.js +7 -0
  187. package/dist/_pkgs/affine-shared/services/feature-flag-service.d.ts +4 -0
  188. package/dist/_pkgs/affine-shared/services/index.d.ts +3 -0
  189. package/dist/_pkgs/affine-shared/services/index.js +3 -0
  190. package/dist/_pkgs/affine-shared/services/selection-pane-service.d.ts +44 -0
  191. package/dist/_pkgs/affine-shared/services/selection-pane-service.js +28 -0
  192. package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +69 -0
  193. package/dist/_pkgs/affine-shared/services/telemetry-service/telemetry-service.d.ts +2 -2
  194. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +5 -0
  195. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +9 -0
  196. package/dist/_pkgs/affine-shared/styles/index.d.ts +1 -0
  197. package/dist/_pkgs/affine-shared/styles/index.js +1 -0
  198. package/dist/_pkgs/affine-shared/styles/panel-header.d.ts +31 -0
  199. package/dist/_pkgs/affine-shared/styles/panel-header.js +61 -0
  200. package/dist/_pkgs/affine-shared/styles/text.js +7 -7
  201. package/dist/_pkgs/affine-shared/types/index.d.ts +6 -0
  202. package/dist/_pkgs/affine-shared/utils/canvas-export-colors.d.ts +26 -0
  203. package/dist/_pkgs/affine-shared/utils/canvas-export-colors.js +127 -0
  204. package/dist/_pkgs/affine-shared/utils/file/filesys.d.ts +8 -11
  205. package/dist/_pkgs/affine-shared/utils/file/filesys.js +8 -11
  206. package/dist/_pkgs/affine-shared/utils/index.d.ts +2 -0
  207. package/dist/_pkgs/affine-shared/utils/index.js +2 -0
  208. package/dist/_pkgs/affine-shared/utils/label-mode.d.ts +47 -0
  209. package/dist/_pkgs/affine-shared/utils/label-mode.js +55 -0
  210. package/dist/_pkgs/affine-shared/utils/model/merge-to-code-model.js +7 -1
  211. package/dist/_pkgs/affine-shared/utils/model/transform-model.js +5 -0
  212. package/dist/_pkgs/affine-widget-edgeless-selected-rect/utils.js +10 -3
  213. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.js +25 -49
  214. package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.d.ts +4 -0
  215. package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.js +4 -0
  216. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.d.ts +4 -0
  217. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.js +4 -0
  218. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/drop.d.ts +61 -0
  219. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/drop.js +91 -0
  220. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/labels.d.ts +17 -0
  221. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/labels.js +95 -0
  222. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/selection-pane-default.d.ts +10 -0
  223. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/selection-pane-default.js +31 -0
  224. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/selection-pane-tool.d.ts +26 -0
  225. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/selection-pane-tool.js +82 -0
  226. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/selection-pane-widget.d.ts +170 -0
  227. package/dist/_pkgs/affine-widget-edgeless-toolbar/selection-pane/selection-pane-widget.js +1381 -0
  228. package/dist/_pkgs/affine-widget-edgeless-toolbar/translations.d.ts +52 -0
  229. package/dist/_pkgs/affine-widget-edgeless-toolbar/translations.js +161 -1
  230. package/dist/_pkgs/affine-widget-edgeless-toolbar/view.js +10 -0
  231. package/dist/_pkgs/affine-widget-note-slicer/note-slicer.js +7 -3
  232. package/dist/_pkgs/std/gfx/controller.d.ts +13 -0
  233. package/dist/_pkgs/std/gfx/controller.js +22 -2
  234. package/dist/_pkgs/std/gfx/grid.d.ts +31 -2
  235. package/dist/_pkgs/std/gfx/index.d.ts +3 -1
  236. package/dist/_pkgs/std/gfx/index.js +3 -1
  237. package/dist/_pkgs/std/gfx/interactivity/manager.js +7 -0
  238. package/dist/_pkgs/std/gfx/layer.js +73 -5
  239. package/dist/_pkgs/std/gfx/local-visibility.d.ts +88 -0
  240. package/dist/_pkgs/std/gfx/local-visibility.js +217 -0
  241. package/dist/_pkgs/std/gfx/model/gfx-block-model.d.ts +13 -0
  242. package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +34 -0
  243. package/dist/_pkgs/std/gfx/model/surface/element-model.js +65 -1
  244. package/dist/_pkgs/std/gfx/model/surface/surface-model.d.ts +27 -0
  245. package/dist/_pkgs/std/gfx/model/surface/surface-model.js +43 -0
  246. package/dist/_pkgs/std/gfx/model/surface/user-layers.d.ts +68 -0
  247. package/dist/_pkgs/std/gfx/model/surface/user-layers.js +127 -0
  248. package/dist/_pkgs/std/utils/layer.d.ts +10 -0
  249. package/dist/_pkgs/std/utils/layer.js +64 -13
  250. package/dist/_pkgs/std/view/element/gfx-block-component.js +10 -2
  251. package/dist/commands.js +4 -1
  252. package/dist/translations.js +10 -6
  253. package/package.json +9 -2
  254. package/dist/_pkgs/affine-components/icons/file-icons-rc.d.ts +0 -1
  255. package/dist/_pkgs/affine-components/icons/file-icons-rc.js +0 -130
  256. package/dist/_pkgs/affine-shared/test-utils/affine-template.d.ts +0 -6
  257. package/dist/_pkgs/affine-shared/test-utils/affine-template.js +0 -258
  258. package/dist/_pkgs/affine-shared/test-utils/affine-test-utils.d.ts +0 -8
  259. package/dist/_pkgs/affine-shared/test-utils/affine-test-utils.js +0 -81
  260. package/dist/_pkgs/affine-shared/test-utils/create-test-host.d.ts +0 -23
  261. package/dist/_pkgs/affine-shared/test-utils/create-test-host.js +0 -187
  262. package/dist/_pkgs/affine-shared/test-utils/index.d.ts +0 -3
  263. package/dist/_pkgs/affine-shared/test-utils/index.js +0 -3
@@ -0,0 +1,323 @@
1
+ import { CanvasLocalVisibility } from '../../../affine-shared/services/index.js';
2
+ import { LifeCycleWatcher } from '../../../std/index.js';
3
+ import { GfxBlockElementModel, GfxControllerIdentifier, GfxGroupLikeElementModel, GfxPrimitiveElementModel, DEFAULT_LAYER_ID, isStoredHiddenForEveryone, } from '../../../std/gfx/index.js';
4
+ import { computed, effect, signal, } from '@preact/signals-core';
5
+ import { userLayersBottomUp } from '../user-layers/actions.js';
6
+ /** The default layer's reserved id (ADR 0031 §2), re-exported from std. */
7
+ export { DEFAULT_LAYER_ID };
8
+ const NOTHING_HIDDEN = new Set();
9
+ /**
10
+ * The layers of a surface with no `layers` record: the default layer alone,
11
+ * shown but not written (ADR 0031 §2 and amendments). Every model is in it,
12
+ * whatever its own key says — with no record, every id is dangling.
13
+ */
14
+ function unrecordedDefaultLayer(hiddenLocally) {
15
+ return {
16
+ order: [DEFAULT_LAYER_ID],
17
+ ranks: new Map(),
18
+ effectiveLayerOf: () => DEFAULT_LAYER_ID,
19
+ hiddenLocally,
20
+ };
21
+ }
22
+ /**
23
+ * The group-like ELEMENT a model is listed under, or `null` for a top-level
24
+ * row.
25
+ *
26
+ * Frames are deliberately not containers here, although they are
27
+ * group-compatible for z-order: a frame is a slide, and the pane filters BY
28
+ * frame rather than nesting under one — exactly the line ADR 0031 §4 draws for
29
+ * layers. `group` walks the model's surface, which throws on a model that is
30
+ * not attached to one; such a model is simply a top-level row.
31
+ */
32
+ export function paneContainerOf(model) {
33
+ return containerOf(rawGroupOf(model));
34
+ }
35
+ /** The rule {@link paneContainerOf} applies, to a `group` already read. */
36
+ function containerOf(group) {
37
+ return group instanceof GfxGroupLikeElementModel ? group : null;
38
+ }
39
+ /** `model.groups`, nearest first, or none where the getter throws. */
40
+ function ancestorsOf(model) {
41
+ try {
42
+ return model.groups;
43
+ }
44
+ catch {
45
+ return [];
46
+ }
47
+ }
48
+ /** `model.group`, or `null` on a model no surface holds (the getter throws). */
49
+ function rawGroupOf(model) {
50
+ try {
51
+ return model.group;
52
+ }
53
+ catch {
54
+ return null;
55
+ }
56
+ }
57
+ /**
58
+ * The pane's rows, built from the canvas' models: one pass to bucket every
59
+ * model under its container, one sort per sibling list, top first.
60
+ *
61
+ * Pure, so the budget test can time exactly what a live pane pays. The order
62
+ * is the canvas' own comparator (`compareLayer`) reversed — the paint order,
63
+ * never a second opinion of it — with one shortcut that is exact: two models
64
+ * under the same group-like ancestor are ordered by `compareLayer` on their
65
+ * `index` alone (its ancestor walk finds no difference and falls through to
66
+ * `compareIndex`). So that case reads two cached strings instead of walking
67
+ * two ancestor chains per comparison, and only models stacked under different
68
+ * ancestors (a frame's members beside loose elements) walk their chains, read
69
+ * once per model rather than once per comparison.
70
+ *
71
+ * The top-level rows are the layers; with no `layers` given, the default
72
+ * layer alone, unrecorded — what a canvas without layers shows.
73
+ */
74
+ export function buildSelectionPaneTree(models, hiddenLocally = NOTHING_HIDDEN, layers = unrecordedDefaultLayer()) {
75
+ // A frame is an ordinary row (ADR 0031, amendments), listed where it
76
+ // paints: a block with no container, so its members — which paint right
77
+ // above it — are its siblings right above it, never its children.
78
+ const present = new Set();
79
+ const groupOf = new Map();
80
+ const indexOf = new Map();
81
+ for (const model of models) {
82
+ present.add(model.id);
83
+ groupOf.set(model, rawGroupOf(model));
84
+ indexOf.set(model, model.index);
85
+ }
86
+ const layerOf = (model) => layers.effectiveLayerOf(model);
87
+ // `compareLayer`'s ancestor rule, on ancestor chains read ONCE per model:
88
+ // `groups` walks the surface on every read, and a frame's members — rows
89
+ // beside loose elements since frames are rows — compared through
90
+ // `compareLayer` read it on every comparison, which blew the frame budget.
91
+ // A chain is the model's ancestors, outermost first, then the model; the
92
+ // first link two chains do not share decides by `index`, and a chain that
93
+ // is a prefix of the other is its ancestor, stacked below it. Held to
94
+ // `compareLayer` by the parity case in `selection-pane.unit.spec.ts`.
95
+ const chainOf = new Map();
96
+ const chain = (model) => {
97
+ let found = chainOf.get(model);
98
+ if (!found) {
99
+ found = [...ancestorsOf(model)].reverse().concat(model);
100
+ chainOf.set(model, found);
101
+ }
102
+ return found;
103
+ };
104
+ const byAncestors = (a, b) => {
105
+ const ac = chain(a);
106
+ const bc = chain(b);
107
+ let i = 0;
108
+ while (i < ac.length && i < bc.length && ac[i] === bc[i])
109
+ i++;
110
+ if (i === ac.length && i === bc.length)
111
+ return 0;
112
+ if (i === ac.length)
113
+ return -1;
114
+ if (i === bc.length)
115
+ return 1;
116
+ const ai = ac[i].index;
117
+ const bi = bc[i].index;
118
+ return ai === bi ? 0 : ai < bi ? -1 : 1;
119
+ };
120
+ const paintOrder = (a, b) => {
121
+ // Rank first, as `compare` does, so the index shortcut below only ever
122
+ // orders two models of the same layer.
123
+ const al = layerOf(a);
124
+ const bl = layerOf(b);
125
+ if (al !== bl) {
126
+ const ar = layers.ranks.get(al) ?? '';
127
+ const br = layers.ranks.get(bl) ?? '';
128
+ if (ar !== br)
129
+ return ar < br ? -1 : 1;
130
+ }
131
+ if (groupOf.get(a) !== groupOf.get(b))
132
+ return byAncestors(a, b);
133
+ const ai = indexOf.get(a);
134
+ const bi = indexOf.get(b);
135
+ return ai === bi ? 0 : ai < bi ? -1 : 1;
136
+ };
137
+ const buckets = new Map();
138
+ for (const model of models) {
139
+ // The `group` read once above: `paneContainerOf` would read it again.
140
+ const container = containerOf(groupOf.get(model));
141
+ // A container that is not listed (a stale group id) cannot hold a row:
142
+ // the member is listed at the top level rather than vanishing.
143
+ const key = container && present.has(container.id) ? container.id : null;
144
+ let bucket = buckets.get(key);
145
+ if (!bucket) {
146
+ bucket = [];
147
+ buckets.set(key, bucket);
148
+ }
149
+ bucket.push(model);
150
+ }
151
+ const build = (key) => {
152
+ const bucket = buckets.get(key);
153
+ if (!bucket)
154
+ return [];
155
+ bucket.sort((a, b) => paintOrder(b, a));
156
+ return bucket.map(model => {
157
+ const block = model instanceof GfxBlockElementModel;
158
+ const role = model instanceof GfxPrimitiveElementModel ? model.role : undefined;
159
+ const node = {
160
+ id: model.id,
161
+ kind: block ? 'block' : 'element',
162
+ type: block ? model.flavour : model.type,
163
+ layerId: layerOf(model),
164
+ locked: model.lockedBySelf === true,
165
+ hiddenLocal: hiddenLocally.has(model.id),
166
+ hiddenForEveryone: isStoredHiddenForEveryone(model),
167
+ };
168
+ if (role !== undefined)
169
+ node.role = role;
170
+ if (key !== null)
171
+ node.groupId = key;
172
+ if (model instanceof GfxGroupLikeElementModel) {
173
+ node.children = build(model.id);
174
+ }
175
+ return node;
176
+ });
177
+ };
178
+ const top = build(null);
179
+ // The top-level rows are the layers, top first, each holding its members
180
+ // in paint order. An empty layer is still a row — it is where a drop lands.
181
+ return layers.order.map(id => ({
182
+ id,
183
+ kind: 'layer',
184
+ type: 'layer',
185
+ layerId: id,
186
+ locked: false,
187
+ hiddenLocal: layers.hiddenLocally?.has(id) ?? false,
188
+ hiddenForEveryone: layers.hiddenForEveryone?.has(id) ?? false,
189
+ children: top.filter(node => node.layerId === id),
190
+ }));
191
+ }
192
+ /**
193
+ * The element props whose change can move, nest, lock or re-role a row. A
194
+ * drag writes `xywh` every frame and must not rebuild the tree every frame.
195
+ */
196
+ const TREE_KEYS = new Set([
197
+ 'index',
198
+ 'layer',
199
+ 'lockedBySelf',
200
+ 'children',
201
+ 'childIds',
202
+ 'role',
203
+ ]);
204
+ /**
205
+ * The surface's user layers as the tree builder takes them; while there is no
206
+ * record, the default layer alone, unrecorded (nothing is written to show it).
207
+ * Reads `layers$`, so a `computed` calling it re-derives on a create, a
208
+ * rename, a reorder — local or a peer's.
209
+ */
210
+ export function paneLayersOf(surface, hiddenLocally) {
211
+ surface.props.layers$?.value;
212
+ const ranks = surface.userLayers?.ranks;
213
+ if (!ranks)
214
+ return unrecordedDefaultLayer(hiddenLocally);
215
+ const bottomUp = userLayersBottomUp(surface);
216
+ return {
217
+ order: bottomUp.map(layer => layer.id).reverse(),
218
+ ranks,
219
+ effectiveLayerOf: model => surface.userLayers.effectiveLayerOf(model),
220
+ hiddenLocally,
221
+ hiddenForEveryone: new Set(bottomUp
222
+ .filter(layer => layer.record.hidden === true)
223
+ .map(layer => layer.id)),
224
+ };
225
+ }
226
+ /**
227
+ * The live tree behind {@link selectionPaneTree}, one per editor.
228
+ *
229
+ * The tree is a `computed` over a revision counter, bumped by the few events
230
+ * that can change a row (an element or block added or removed, a reorder, a
231
+ * regroup, a lock) — never by geometry. A `computed` is lazy, so an editor
232
+ * whose pane is closed and whose host never reads the tree pays the counter
233
+ * bump and nothing else; an open pane pays one rebuild per relevant change,
234
+ * not one per frame.
235
+ *
236
+ * It also holds whether the LIBRARY's own panel is open (`open$`), which is
237
+ * what the toggle command reads. A host that replaced the panel through
238
+ * `SelectionPaneExtension` owns its own open state; `open$` then stays false
239
+ * and the toggle always opens.
240
+ */
241
+ export class SelectionPaneModel extends LifeCycleWatcher {
242
+ constructor() {
243
+ super(...arguments);
244
+ this._revision$ = signal(0);
245
+ this._disposeSurfaceEffect = null;
246
+ this._subscriptions = [];
247
+ /** Whether the library's own panel is on screen. Written by that panel. */
248
+ this.open$ = signal(false);
249
+ this.tree$ = computed(() => {
250
+ this._revision$.value;
251
+ const gfx = this.std.get(GfxControllerIdentifier);
252
+ const surface = gfx.surface$.value;
253
+ if (!surface)
254
+ return [];
255
+ // Read here, so a local hide or show re-derives the rows' marks.
256
+ const local = this.std.getOptional(CanvasLocalVisibility);
257
+ const hidden = local?.hiddenIds$.value;
258
+ const hiddenLayers = local?.hiddenLayerIds$.value;
259
+ // And here, so a hide for everyone (local or a peer's) re-derives them too.
260
+ gfx.hiddenForEveryone?.ids$.value;
261
+ return buildSelectionPaneTree(gfx.gfxElements, hidden, paneLayersOf(surface, hiddenLayers));
262
+ });
263
+ this._surfaceSubscriptions = [];
264
+ }
265
+ static { this.key = 'selection-pane-model'; }
266
+ /** Force a rebuild; for a change no subscribed event reports. */
267
+ invalidate() {
268
+ // `peek`, not `value`: called from inside an effect, a READ would
269
+ // subscribe that effect to the counter it is about to bump.
270
+ this._revision$.value = this._revision$.peek() + 1;
271
+ }
272
+ mounted() {
273
+ super.mounted();
274
+ const gfx = this.std.get(GfxControllerIdentifier);
275
+ const store = this.std.store;
276
+ this._subscriptions.push(gfx.layer.slots.layerUpdated.subscribe(() => this.invalidate()), store.slots.blockUpdated.subscribe(payload => {
277
+ if (payload.type !== 'update' || TREE_KEYS.has(payload.props.key)) {
278
+ this.invalidate();
279
+ }
280
+ }));
281
+ // The surface is a signal: it may arrive after mount, or be replaced.
282
+ this._disposeSurfaceEffect = effect(() => {
283
+ const surface = gfx.surface$.value;
284
+ this._watchSurface(surface);
285
+ });
286
+ }
287
+ _watchSurface(surface) {
288
+ this._surfaceSubscriptions.forEach(sub => sub.unsubscribe());
289
+ this._surfaceSubscriptions = [];
290
+ if (!surface)
291
+ return;
292
+ this._surfaceSubscriptions.push(surface.elementAdded.subscribe(() => this.invalidate()), surface.elementRemoved.subscribe(() => this.invalidate()), surface.elementUpdated.subscribe(({ props }) => {
293
+ for (const key of Object.keys(props)) {
294
+ if (TREE_KEYS.has(key)) {
295
+ this.invalidate();
296
+ return;
297
+ }
298
+ }
299
+ }));
300
+ this.invalidate();
301
+ }
302
+ unmounted() {
303
+ this._disposeSurfaceEffect?.();
304
+ this._disposeSurfaceEffect = null;
305
+ this._watchSurface(null);
306
+ this._subscriptions.forEach(sub => sub.unsubscribe());
307
+ this._subscriptions = [];
308
+ this.open$.value = false;
309
+ super.unmounted();
310
+ }
311
+ }
312
+ /**
313
+ * The headless data API of the selection pane (ADR 0031 §12): a signal of
314
+ * rows, z-order, top first, ids only.
315
+ *
316
+ * What a host's own pane renders from, and what the library's panel renders
317
+ * from — one tree, so the two cannot disagree about the order of a canvas.
318
+ * Edgeless editors only: the model is registered by the surface's edgeless
319
+ * view extension.
320
+ */
321
+ export function selectionPaneTree(std) {
322
+ return std.get(SelectionPaneModel).tree$;
323
+ }
@@ -1,4 +1,5 @@
1
- import { type InterchangeImportContext, type InterchangeImportResult } from './interchange.js';
1
+ import type { TranslationParams } from '../../affine-shared/services/index.js';
2
+ import { type InterchangeImportContext, type InterchangeImportResult, type InterchangeNote, type SerializedElementProps } from './interchange.js';
2
3
  /**
3
4
  * **SVG → a canvas sketch.** The platform's VISUAL-tier importer, shared by
4
5
  * every framework that declares it (`docs/adr/0012`, P2).
@@ -9,9 +10,11 @@ import { type InterchangeImportContext, type InterchangeImportResult } from './i
9
10
  * a one-paragraph statement of its heuristics and its known failure modes" —
10
11
  * answered ONCE, here, because every framework that declares `…:svg:import`
11
12
  * wraps this one function and therefore owes the same statement. It is
12
- * referenced by `bpmn:svg:import` and by `wardley:svg:import`; a framework that
13
- * ever wants a narrower or wider guess writes its own parser and its own
14
- * paragraph beside it._
13
+ * referenced by `bpmn:svg:import`; a framework that ever wants a narrower or
14
+ * wider guess writes its own parser and its own paragraph beside it. Wardley
15
+ * did (ADR 0032): a framework may CLAIM nodes before the walk below
16
+ * ({@link sanitizeSvg}, {@link sketchSvgTree}), and then owes its own statement
17
+ * for what it claims — this one still covers everything it leaves here._
15
18
  *
16
19
  * **What it guesses.** An SVG carries a rendering, not a model, so this reader
17
20
  * recognises GEOMETRY and nothing else: `<rect>` becomes a rectangle,
@@ -91,6 +94,110 @@ import { type InterchangeImportContext, type InterchangeImportResult } from './i
91
94
  export declare const SVG_SKETCH_FORMAT_ID = "svg";
92
95
  export declare const SVG_SKETCH_EXTENSION = ".svg";
93
96
  export declare const SVG_SKETCH_MIME = "image/svg+xml";
97
+ /**
98
+ * The note channel, deduplicated on a key.
99
+ *
100
+ * D1's discipline, carried across to a tier that has no D1: nothing is dropped
101
+ * silently. What changes is the GRANULARITY — a file with four hundred
102
+ * `<use>` instances is one fact about the file, not four hundred, and a report
103
+ * nobody reads to the end is a report that was not written.
104
+ *
105
+ * Every note is a `warning`. The other four kinds would be lies here:
106
+ * `carried` and `quarantined` describe a payload this tier does not write, and
107
+ * `substituted-id` and `invented-layout` describe promises it does not make.
108
+ *
109
+ * Exported so a framework that claims part of a picture first (ADR 0032 §2)
110
+ * hands the SAME notebook to {@link sanitizeSvg} and {@link sketchSvgTree}: one
111
+ * file, one de-duplication, one list of remarks.
112
+ */
113
+ export declare class SvgSketchNotebook {
114
+ private readonly seen;
115
+ readonly notes: InterchangeNote[];
116
+ /**
117
+ * @param dedupKey De-duplication identity — a note fires once per distinct
118
+ * value (`font-unit:px`, `font-unit:pt`…), independent of the i18n key
119
+ * below, which stays the same across all of them.
120
+ * @param message The English sentence, already filled in with the file's
121
+ * own words — shown as is with no host catalogue registered.
122
+ * @param wording The key/fallback pair for this sentence's SHAPE
123
+ * (`svg-sketch-translations.ts`), and the params that fill its
124
+ * `{{name}}` holes when it has any.
125
+ */
126
+ once(dedupKey: string, message: string, wording: readonly [key: string, fallback: string], params?: TranslationParams, element?: string): void;
127
+ }
128
+ /**
129
+ * How a user-space coordinate at this depth becomes a canvas coordinate:
130
+ * `canvas = user * s + o`.
131
+ *
132
+ * Carrying the SCALE rather than only the offset is what makes a `viewBox`
133
+ * honest. `width="200" viewBox="0 0 1000 1000"` is a drawing authored at 1000
134
+ * units and displayed at 200; reading its coordinates 1:1 imports it five times
135
+ * too big, and every stroke width and font size with it. The factor is uniform
136
+ * — `min(width / viewBox width, height / viewBox height)` — because that is
137
+ * what the default `preserveAspectRatio` (`xMidYMid meet`) means, and a
138
+ * non-uniform read would need a shape model with independent axes.
139
+ *
140
+ * Exported as {@link SvgSketchFrame}: a recogniser reads a node's coordinates
141
+ * through {@link svgFrameOf} so it lands them exactly where this walk would
142
+ * have, and places what it leaves to the walk with the `place` option of
143
+ * {@link sketchSvgTree}.
144
+ */
145
+ interface Frame {
146
+ ox: number;
147
+ oy: number;
148
+ s: number;
149
+ }
150
+ export type SvgSketchFrame = Frame;
151
+ /**
152
+ * The second pass: a sanitised tree as sketch props, minus what a caller
153
+ * claimed.
154
+ *
155
+ * `skip` is the set of nodes a framework's recogniser already turned into its
156
+ * own artefacts; each one is left alone WITH its subtree, and everything else
157
+ * is read exactly as {@link parseSvgSketch} reads it. `place` composes a frame
158
+ * OUTSIDE the file's own viewport — `canvas = place.s · file + place.o` — so
159
+ * the remainder can land beside a board the recogniser laid out at another
160
+ * scale. Both default to nothing, which is {@link parseSvgSketch} itself.
161
+ *
162
+ * A framework that claims nodes owes its own heuristics statement for what it
163
+ * claims; this module's statement covers only what reaches this walk.
164
+ */
165
+ export declare function sketchSvgTree(root: Element, notes: SvgSketchNotebook, options?: {
166
+ skip?: ReadonlySet<Element>;
167
+ place?: SvgSketchFrame;
168
+ }): SerializedElementProps[];
169
+ /**
170
+ * The frame a node's OWN attributes are read in — its `cx`, its `x1`, its
171
+ * `points` — composed exactly the way the walk composes it: the root's
172
+ * viewport, then every ancestor's translate and nested viewport, then the
173
+ * node's own `transform`. So a recogniser that reads a circle's centre through
174
+ * this lands it on the very point the sketch would have drawn it.
175
+ *
176
+ * The transforms the walk ignores are ignored here too, silently: the walk
177
+ * already says so once per kind when it meets them.
178
+ */
179
+ export declare function svgFrameOf(element: Element): SvgSketchFrame;
180
+ /**
181
+ * The document, sanitized, or an exception naming what is wrong with it.
182
+ *
183
+ * Well-formedness FIRST, because `DOMParser` reports a malformed document as a
184
+ * `parsererror` element rather than by throwing — a verdict in the shape of a
185
+ * document — and because DOMPurify's parser is the forgiving HTML one, which
186
+ * would quietly repair the very thing the caller needs to be told about. So:
187
+ * parse for the verdict, reading nothing out of the result but that; sanitize
188
+ * the SOURCE; and walk only what came back from the sanitizer.
189
+ *
190
+ * The sanitizer is not optional and it is not defensive programming. An `.svg`
191
+ * is an executable document — `<script>`, `<foreignObject>` with markup, event
192
+ * handler attributes — handed over by whoever sent the file. The same
193
+ * `USE_PROFILES: { svg: true }` guard is what the edgeless clipboard already
194
+ * puts in front of a pasted SVG.
195
+ *
196
+ * The first of the two passes {@link parseSvgSketch} is made of, exported so a
197
+ * framework that recognises part of a picture reads the SAME sanitised tree
198
+ * the sketch walks (ADR 0032 §2, §7) — never the source.
199
+ */
200
+ export declare function sanitizeSvg(source: string, notes: SvgSketchNotebook): Element;
94
201
  /**
95
202
  * An SVG file as a canvas sketch — the shared VISUAL-tier importer.
96
203
  *
@@ -105,3 +212,4 @@ export declare const SVG_SKETCH_MIME = "image/svg+xml";
105
212
  * Malformed XML is the exception, because then the file was not read at all.
106
213
  */
107
214
  export declare function parseSvgSketch(source: string, context?: InterchangeImportContext): InterchangeImportResult;
215
+ export {};
@@ -12,9 +12,11 @@ import { SVG_SKETCH_CURRENT_COLOR, SVG_SKETCH_CURVE, SVG_SKETCH_EMPTY, SVG_SKETC
12
12
  * a one-paragraph statement of its heuristics and its known failure modes" —
13
13
  * answered ONCE, here, because every framework that declares `…:svg:import`
14
14
  * wraps this one function and therefore owes the same statement. It is
15
- * referenced by `bpmn:svg:import` and by `wardley:svg:import`; a framework that
16
- * ever wants a narrower or wider guess writes its own parser and its own
17
- * paragraph beside it._
15
+ * referenced by `bpmn:svg:import`; a framework that ever wants a narrower or
16
+ * wider guess writes its own parser and its own paragraph beside it. Wardley
17
+ * did (ADR 0032): a framework may CLAIM nodes before the walk below
18
+ * ({@link sanitizeSvg}, {@link sketchSvgTree}), and then owes its own statement
19
+ * for what it claims — this one still covers everything it leaves here._
18
20
  *
19
21
  * **What it guesses.** An SVG carries a rendering, not a model, so this reader
20
22
  * recognises GEOMETRY and nothing else: `<rect>` becomes a rectangle,
@@ -165,8 +167,12 @@ function numbers(raw) {
165
167
  * Every note is a `warning`. The other four kinds would be lies here:
166
168
  * `carried` and `quarantined` describe a payload this tier does not write, and
167
169
  * `substituted-id` and `invented-layout` describe promises it does not make.
170
+ *
171
+ * Exported so a framework that claims part of a picture first (ADR 0032 §2)
172
+ * hands the SAME notebook to {@link sanitizeSvg} and {@link sketchSvgTree}: one
173
+ * file, one de-duplication, one list of remarks.
168
174
  */
169
- class Notebook {
175
+ export class SvgSketchNotebook {
170
176
  constructor() {
171
177
  this.seen = new Set();
172
178
  this.notes = [];
@@ -774,6 +780,8 @@ function walk(parent, frame, inherited, sketch) {
774
780
  }
775
781
  /** One element and whatever it contains, placed in its parent's frame. */
776
782
  function visit(element, frame, inherited, sketch) {
783
+ if (sketch.skip.has(element))
784
+ return;
777
785
  const name = nameOf(element);
778
786
  if (NON_RENDERING.has(name))
779
787
  return;
@@ -851,6 +859,68 @@ function emit(element, name, frame, paint, sketch) {
851
859
  sketch.notes.once(`skipped:${name}`, `\`<${name}>\` is not recognised and was skipped.`, SVG_SKETCH_SKIPPED, { name }, name);
852
860
  }
853
861
  }
862
+ /* ── The two passes, opened to a recogniser (ADR 0032 §2) ────────────── */
863
+ /**
864
+ * The second pass: a sanitised tree as sketch props, minus what a caller
865
+ * claimed.
866
+ *
867
+ * `skip` is the set of nodes a framework's recogniser already turned into its
868
+ * own artefacts; each one is left alone WITH its subtree, and everything else
869
+ * is read exactly as {@link parseSvgSketch} reads it. `place` composes a frame
870
+ * OUTSIDE the file's own viewport — `canvas = place.s · file + place.o` — so
871
+ * the remainder can land beside a board the recogniser laid out at another
872
+ * scale. Both default to nothing, which is {@link parseSvgSketch} itself.
873
+ *
874
+ * A framework that claims nodes owes its own heuristics statement for what it
875
+ * claims; this module's statement covers only what reaches this walk.
876
+ */
877
+ export function sketchSvgTree(root, notes, options = {}) {
878
+ const sketch = {
879
+ elements: [],
880
+ notes,
881
+ skip: options.skip ?? new Set(),
882
+ };
883
+ const place = options.place ?? IDENTITY;
884
+ const own = viewportFrame(root, IDENTITY);
885
+ // The root `<svg>` paints like any other element — a `fill` on it is what
886
+ // every shape under it inherits — and it establishes the outermost viewport.
887
+ walk(root, {
888
+ ox: own.ox * place.s + place.ox,
889
+ oy: own.oy * place.s + place.oy,
890
+ s: own.s * place.s,
891
+ }, paintOf(root, INITIAL_PAINT, notes), sketch);
892
+ return sketch.elements;
893
+ }
894
+ /**
895
+ * The frame a node's OWN attributes are read in — its `cx`, its `x1`, its
896
+ * `points` — composed exactly the way the walk composes it: the root's
897
+ * viewport, then every ancestor's translate and nested viewport, then the
898
+ * node's own `transform`. So a recogniser that reads a circle's centre through
899
+ * this lands it on the very point the sketch would have drawn it.
900
+ *
901
+ * The transforms the walk ignores are ignored here too, silently: the walk
902
+ * already says so once per kind when it meets them.
903
+ */
904
+ export function svgFrameOf(element) {
905
+ const path = [];
906
+ for (let at = element; at; at = at.parentElement) {
907
+ path.unshift(at);
908
+ }
909
+ // The outermost `<svg>` on the path is the root the walk starts from: its
910
+ // viewport is the identity's, and its own `transform` is never read.
911
+ const rootIndex = path.findIndex(node => nameOf(node) === 'svg');
912
+ if (rootIndex < 0)
913
+ return IDENTITY;
914
+ const quiet = new SvgSketchNotebook();
915
+ let frame = viewportFrame(path[rootIndex], IDENTITY);
916
+ for (const node of path.slice(rootIndex + 1)) {
917
+ frame = translated(node, frame, quiet);
918
+ if (node !== element && nameOf(node) === 'svg') {
919
+ frame = viewportFrame(node, frame);
920
+ }
921
+ }
922
+ return frame;
923
+ }
854
924
  /* ── The capability's function ────────────────────────────────────────── */
855
925
  /**
856
926
  * The document, sanitized, or an exception naming what is wrong with it.
@@ -867,8 +937,12 @@ function emit(element, name, frame, paint, sketch) {
867
937
  * handler attributes — handed over by whoever sent the file. The same
868
938
  * `USE_PROFILES: { svg: true }` guard is what the edgeless clipboard already
869
939
  * puts in front of a pasted SVG.
940
+ *
941
+ * The first of the two passes {@link parseSvgSketch} is made of, exported so a
942
+ * framework that recognises part of a picture reads the SAME sanitised tree
943
+ * the sketch walks (ADR 0032 §2, §7) — never the source.
870
944
  */
871
- function parseSvgRoot(source, notes) {
945
+ export function sanitizeSvg(source, notes) {
872
946
  const doc = new DOMParser().parseFromString(source, 'image/svg+xml');
873
947
  const error = doc.querySelector('parsererror');
874
948
  if (error) {
@@ -952,25 +1026,22 @@ export function parseSvgSketch(source, context = {}) {
952
1026
  // A sketch has no document-level name to take: what a board is CALLED is the
953
1027
  // document's business, not any element's. Same answer the BPMN reader gives.
954
1028
  void context;
955
- const sketch = { elements: [], notes: new Notebook() };
956
- const root = parseSvgRoot(source, sketch.notes);
957
- // The root `<svg>` paints like any other element — a `fill` on it is what
958
- // every shape under it inherits — and it establishes the outermost viewport.
959
- walk(root, viewportFrame(root, IDENTITY), paintOf(root, INITIAL_PAINT, sketch.notes), sketch);
960
- if (sketch.elements.length === 0) {
961
- sketch.notes.once('empty', 'No shape or text was recognised in this SVG, so nothing was drawn.', SVG_SKETCH_EMPTY);
1029
+ const notes = new SvgSketchNotebook();
1030
+ const elements = sketchSvgTree(sanitizeSvg(source, notes), notes);
1031
+ if (elements.length === 0) {
1032
+ notes.once('empty', 'No shape or text was recognised in this SVG, so nothing was drawn.', SVG_SKETCH_EMPTY);
962
1033
  }
963
1034
  return {
964
- elements: sketch.elements,
1035
+ elements,
965
1036
  report: {
966
- mapped: sketch.elements.length,
1037
+ mapped: elements.length,
967
1038
  // Always zero, and it is the tier's whole contract rather than an
968
1039
  // accident of this file: a visual import carries nothing and quarantines
969
1040
  // nothing, because it writes no `interchange` payload to carry anything
970
1041
  // IN (ADR 0012, P2). Pinned by an anti-decay test.
971
1042
  carried: 0,
972
1043
  quarantined: 0,
973
- notes: sketch.notes.notes,
1044
+ notes: notes.notes,
974
1045
  // …and no `sourceVersion`. An SVG declares a version of the SVG spec, not
975
1046
  // a version of a vocabulary this reader translates, so claiming one would
976
1047
  // be a fact about the file dressed up as a fact about the import.