@formicoidea/labre-core 0.41.0 → 0.42.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 (252) hide show
  1. package/LICENSE +384 -0
  2. package/dist/_pkgs/affine-block-bookmark/bookmark-block.js +2 -2
  3. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/config.js +7 -1
  4. package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-block.js +2 -1
  5. package/dist/_pkgs/affine-block-embed/embed-github-block/embed-github-block.js +2 -1
  6. package/dist/_pkgs/affine-block-embed/embed-iframe-block/consts.d.ts +2 -0
  7. package/dist/_pkgs/affine-block-embed/embed-iframe-block/consts.js +5 -0
  8. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-block.d.ts +1 -0
  9. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-block.js +30 -8
  10. package/dist/_pkgs/affine-block-embed/embed-iframe-block/utils.d.ts +8 -0
  11. package/dist/_pkgs/affine-block-embed/embed-iframe-block/utils.js +19 -0
  12. package/dist/_pkgs/affine-block-embed/embed-loom-block/embed-loom-block.js +2 -1
  13. package/dist/_pkgs/affine-block-embed/embed-youtube-block/embed-youtube-block.js +2 -1
  14. package/dist/_pkgs/affine-block-frame/frame-manager.js +1 -1
  15. package/dist/_pkgs/affine-block-frame/frame-tool.js +1 -1
  16. package/dist/_pkgs/affine-block-frame/frame-toolbar.js +6 -1
  17. package/dist/_pkgs/affine-block-list/list-keymap.js +7 -0
  18. package/dist/_pkgs/affine-block-note/note-keymap.js +7 -1
  19. package/dist/_pkgs/affine-block-paragraph/paragraph-keymap.js +7 -1
  20. package/dist/_pkgs/affine-block-root/commands/pivot-commands.js +1 -8
  21. package/dist/_pkgs/affine-block-root/commands/tag-commands.js +1 -8
  22. package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.d.ts +9 -0
  23. package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.js +51 -0
  24. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.d.ts +22 -0
  25. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.js +46 -0
  26. package/dist/_pkgs/affine-block-root/edgeless/utils/pinch-zoom.d.ts +21 -0
  27. package/dist/_pkgs/affine-block-root/edgeless/utils/pinch-zoom.js +24 -0
  28. package/dist/_pkgs/affine-block-root/edgeless/utils/query.js +8 -0
  29. package/dist/_pkgs/affine-block-root/keyboard/keymap.js +1 -16
  30. package/dist/_pkgs/affine-block-surface/element-model/index.d.ts +21 -3
  31. package/dist/_pkgs/affine-block-surface/element-model/index.js +14 -2
  32. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.d.ts +23 -0
  33. package/dist/_pkgs/affine-block-surface/extensions/export-svg/command.js +89 -0
  34. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.d.ts +7 -0
  35. package/dist/_pkgs/affine-block-surface/extensions/export-svg/index.js +5 -0
  36. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.d.ts +41 -0
  37. package/dist/_pkgs/affine-block-surface/extensions/export-svg/render.js +70 -0
  38. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.d.ts +49 -0
  39. package/dist/_pkgs/affine-block-surface/extensions/export-svg/svg-context.js +257 -0
  40. package/dist/_pkgs/affine-block-surface/extensions/export-svg/tagged-path.d.ts +16 -0
  41. package/dist/_pkgs/affine-block-surface/extensions/export-svg/tagged-path.js +24 -0
  42. package/dist/_pkgs/affine-block-surface/extensions/export-svg/toolbar.d.ts +24 -0
  43. package/dist/_pkgs/affine-block-surface/extensions/export-svg/toolbar.js +66 -0
  44. package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +4 -0
  45. package/dist/_pkgs/affine-block-surface/extensions/index.js +4 -0
  46. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.d.ts +66 -4
  47. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +241 -11
  48. package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +18 -0
  49. package/dist/_pkgs/affine-block-surface/extensions/legend-toolbar.d.ts +81 -0
  50. package/dist/_pkgs/affine-block-surface/extensions/legend-toolbar.js +81 -0
  51. package/dist/_pkgs/affine-block-surface/extensions/legend.d.ts +136 -0
  52. package/dist/_pkgs/affine-block-surface/extensions/legend.js +439 -0
  53. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.d.ts +17 -3
  54. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.js +35 -13
  55. package/dist/_pkgs/affine-block-surface/extensions/reading.d.ts +61 -1
  56. package/dist/_pkgs/affine-block-surface/extensions/reading.js +85 -39
  57. package/dist/_pkgs/affine-block-surface/extensions/record-action.d.ts +66 -0
  58. package/dist/_pkgs/affine-block-surface/extensions/record-action.js +145 -0
  59. package/dist/_pkgs/affine-block-surface/extensions/validation.d.ts +253 -28
  60. package/dist/_pkgs/affine-block-surface/extensions/validation.js +489 -41
  61. package/dist/_pkgs/affine-block-surface/framework-background/background-view.d.ts +115 -0
  62. package/dist/_pkgs/affine-block-surface/framework-background/background-view.js +185 -0
  63. package/dist/_pkgs/affine-block-surface/framework-background/index.d.ts +1 -0
  64. package/dist/_pkgs/affine-block-surface/framework-background/index.js +1 -0
  65. package/dist/_pkgs/affine-block-surface/framework-background/labels.d.ts +16 -1
  66. package/dist/_pkgs/affine-block-surface/framework-background/labels.js +31 -21
  67. package/dist/_pkgs/affine-block-surface/framework-background/renderer.js +29 -5
  68. package/dist/_pkgs/affine-block-surface/framework-background/stacking.d.ts +6 -4
  69. package/dist/_pkgs/affine-block-surface/framework-background/stacking.js +56 -8
  70. package/dist/_pkgs/affine-block-surface/renderer/canvas-renderer.d.ts +12 -0
  71. package/dist/_pkgs/affine-block-surface/renderer/canvas-renderer.js +15 -0
  72. package/dist/_pkgs/affine-block-surface/view.js +8 -1
  73. package/dist/_pkgs/affine-block-surface-ref/configs/slash-menu.js +1 -1
  74. package/dist/_pkgs/affine-components/color-picker/button.d.ts +56 -1
  75. package/dist/_pkgs/affine-components/color-picker/button.js +168 -23
  76. package/dist/_pkgs/affine-components/color-picker/framework-palette.d.ts +106 -0
  77. package/dist/_pkgs/affine-components/color-picker/framework-palette.js +177 -0
  78. package/dist/_pkgs/affine-components/color-picker/index.d.ts +2 -0
  79. package/dist/_pkgs/affine-components/color-picker/index.js +2 -0
  80. package/dist/_pkgs/affine-components/color-picker/palette-carousel.d.ts +85 -0
  81. package/dist/_pkgs/affine-components/color-picker/palette-carousel.js +440 -0
  82. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/color-picker.d.ts +31 -1
  83. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/color-picker.js +189 -63
  84. package/dist/_pkgs/affine-components/resource/resource.js +29 -16
  85. package/dist/_pkgs/affine-components/translations.d.ts +8 -0
  86. package/dist/_pkgs/affine-components/translations.js +17 -0
  87. package/dist/_pkgs/affine-foundation/view.js +2 -1
  88. package/dist/_pkgs/affine-gfx-brush/element-renderer.js +2 -2
  89. package/dist/_pkgs/affine-gfx-brush/toolbar/configs/brush.js +6 -1
  90. package/dist/_pkgs/affine-gfx-brush/toolbar/configs/highlighter.js +6 -1
  91. package/dist/_pkgs/affine-gfx-connector/connector-manager.d.ts +9 -1
  92. package/dist/_pkgs/affine-gfx-connector/connector-manager.js +61 -0
  93. package/dist/_pkgs/affine-gfx-connector/element-renderer/connector-dom/index.d.ts +7 -1
  94. package/dist/_pkgs/affine-gfx-connector/element-renderer/connector-dom/index.js +87 -17
  95. package/dist/_pkgs/affine-gfx-connector/element-renderer/index.js +78 -17
  96. package/dist/_pkgs/affine-gfx-connector/element-renderer/utils.d.ts +24 -2
  97. package/dist/_pkgs/affine-gfx-connector/element-renderer/utils.js +31 -8
  98. package/dist/_pkgs/affine-gfx-connector/text/edgeless-connector-label-editor.d.ts +124 -3
  99. package/dist/_pkgs/affine-gfx-connector/text/edgeless-connector-label-editor.js +216 -54
  100. package/dist/_pkgs/affine-gfx-connector/text/text.d.ts +13 -4
  101. package/dist/_pkgs/affine-gfx-connector/text/text.js +13 -44
  102. package/dist/_pkgs/affine-gfx-connector/toolbar/config.d.ts +56 -0
  103. package/dist/_pkgs/affine-gfx-connector/toolbar/config.js +72 -4
  104. package/dist/_pkgs/affine-gfx-connector/toolbar/hollow-endpoint-icons.d.ts +8 -0
  105. package/dist/_pkgs/affine-gfx-connector/toolbar/hollow-endpoint-icons.js +118 -0
  106. package/dist/_pkgs/affine-gfx-connector/view/view.d.ts +37 -1
  107. package/dist/_pkgs/affine-gfx-connector/view/view.js +97 -8
  108. package/dist/_pkgs/affine-gfx-group/command/group-api.js +1 -1
  109. package/dist/_pkgs/affine-gfx-group/store.js +0 -2
  110. package/dist/_pkgs/affine-gfx-group/translations.d.ts +9 -10
  111. package/dist/_pkgs/affine-gfx-group/translations.js +9 -13
  112. package/dist/_pkgs/affine-gfx-mindmap/templates/index.js +1 -1
  113. package/dist/_pkgs/affine-gfx-mindmap/toolbar/basket-elements.js +1 -1
  114. package/dist/_pkgs/affine-gfx-mindmap/toolbar/utils/import-mindmap.js +2 -2
  115. package/dist/_pkgs/affine-gfx-mindmap/translations.d.ts +1 -1
  116. package/dist/_pkgs/affine-gfx-mindmap/translations.js +1 -1
  117. package/dist/_pkgs/affine-gfx-shape/element-renderer/highlighter.js +2 -2
  118. package/dist/_pkgs/affine-gfx-shape/overlay/polygon-drawing-overlay.d.ts +6 -0
  119. package/dist/_pkgs/affine-gfx-shape/overlay/polygon-drawing-overlay.js +30 -1
  120. package/dist/_pkgs/affine-gfx-shape/polygon-tool.d.ts +20 -1
  121. package/dist/_pkgs/affine-gfx-shape/polygon-tool.js +9 -1
  122. package/dist/_pkgs/affine-gfx-shape/toolbar/config.js +6 -1
  123. package/dist/_pkgs/affine-gfx-shape/toolbar/palette-color-action.js +11 -1
  124. package/dist/_pkgs/affine-gfx-template/builtin/other.js +1 -1
  125. package/dist/_pkgs/affine-gfx-template/snapshot-from-action.js +32 -119
  126. package/dist/_pkgs/affine-gfx-template/toolbar/template-type.d.ts +1 -1
  127. package/dist/_pkgs/affine-gfx-text/dblclick-add-edgeless-text.d.ts +13 -1
  128. package/dist/_pkgs/affine-gfx-text/dblclick-add-edgeless-text.js +19 -4
  129. package/dist/_pkgs/affine-gfx-text/edgeless-text-editor.d.ts +41 -0
  130. package/dist/_pkgs/affine-gfx-text/edgeless-text-editor.js +57 -2
  131. package/dist/_pkgs/affine-gfx-text/toolbar/actions.d.ts +8 -1
  132. package/dist/_pkgs/affine-gfx-text/toolbar/actions.js +12 -2
  133. package/dist/_pkgs/affine-gfx-text/translations.d.ts +1 -0
  134. package/dist/_pkgs/affine-gfx-text/translations.js +2 -1
  135. package/dist/_pkgs/affine-inline-preset/markdown.js +5 -1
  136. package/dist/_pkgs/affine-model/consts/connector.d.ts +28 -2
  137. package/dist/_pkgs/affine-model/consts/connector.js +26 -0
  138. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +1 -1
  139. package/dist/_pkgs/affine-model/elements/brush/brush.js +1 -1
  140. package/dist/_pkgs/affine-model/elements/c4/board.js +1 -1
  141. package/dist/_pkgs/affine-model/elements/c4/boundary.js +1 -1
  142. package/dist/_pkgs/affine-model/elements/connector/connector.d.ts +135 -0
  143. package/dist/_pkgs/affine-model/elements/connector/connector.js +246 -5
  144. package/dist/_pkgs/affine-model/elements/connector/end-label.d.ts +83 -0
  145. package/dist/_pkgs/affine-model/elements/connector/end-label.js +119 -0
  146. package/dist/_pkgs/affine-model/elements/connector/index.d.ts +1 -0
  147. package/dist/_pkgs/affine-model/elements/connector/index.js +1 -0
  148. package/dist/_pkgs/affine-model/elements/context-map/context-map.js +1 -1
  149. package/dist/_pkgs/affine-model/elements/core-domain/core-domain.d.ts +28 -0
  150. package/dist/_pkgs/affine-model/elements/core-domain/core-domain.js +129 -2
  151. package/dist/_pkgs/affine-model/elements/cynefin/cynefin.d.ts +8 -0
  152. package/dist/_pkgs/affine-model/elements/cynefin/cynefin.js +45 -2
  153. package/dist/_pkgs/affine-model/elements/edgy/board.js +1 -1
  154. package/dist/_pkgs/affine-model/elements/edgy/facets.js +1 -1
  155. package/dist/_pkgs/affine-model/elements/estuarine/estuarine.d.ts +6 -0
  156. package/dist/_pkgs/affine-model/elements/estuarine/estuarine.js +36 -2
  157. package/dist/_pkgs/affine-model/elements/event-storming/event-storming.d.ts +18 -7
  158. package/dist/_pkgs/affine-model/elements/event-storming/event-storming.js +23 -8
  159. package/dist/_pkgs/affine-model/elements/framework-background/background.d.ts +16 -0
  160. package/dist/_pkgs/affine-model/elements/framework-background/background.js +18 -0
  161. package/dist/_pkgs/affine-model/elements/highlighter/highlighter.js +1 -1
  162. package/dist/_pkgs/affine-model/elements/index.d.ts +8 -0
  163. package/dist/_pkgs/affine-model/elements/index.js +1 -0
  164. package/dist/_pkgs/affine-model/elements/shape/shape.js +1 -1
  165. package/dist/_pkgs/affine-model/elements/text/text.js +1 -1
  166. package/dist/_pkgs/affine-model/elements/uml/band.d.ts +39 -0
  167. package/dist/_pkgs/affine-model/elements/uml/band.js +31 -0
  168. package/dist/_pkgs/affine-model/elements/uml/diagram.d.ts +158 -0
  169. package/dist/_pkgs/affine-model/elements/uml/diagram.js +229 -0
  170. package/dist/_pkgs/affine-model/elements/uml/fragment.d.ts +161 -0
  171. package/dist/_pkgs/affine-model/elements/uml/fragment.js +205 -0
  172. package/dist/_pkgs/affine-model/elements/uml/index.d.ts +8 -0
  173. package/dist/_pkgs/affine-model/elements/uml/index.js +8 -0
  174. package/dist/_pkgs/affine-model/elements/uml/lifeline.d.ts +77 -0
  175. package/dist/_pkgs/affine-model/elements/uml/lifeline.js +89 -0
  176. package/dist/_pkgs/affine-model/elements/uml/node.d.ts +142 -0
  177. package/dist/_pkgs/affine-model/elements/uml/node.js +190 -0
  178. package/dist/_pkgs/affine-model/elements/uml/partition.d.ts +106 -0
  179. package/dist/_pkgs/affine-model/elements/uml/partition.js +172 -0
  180. package/dist/_pkgs/affine-model/elements/uml/region.d.ts +75 -0
  181. package/dist/_pkgs/affine-model/elements/uml/region.js +150 -0
  182. package/dist/_pkgs/affine-model/elements/uml/subject.d.ts +37 -0
  183. package/dist/_pkgs/affine-model/elements/uml/subject.js +108 -0
  184. package/dist/_pkgs/affine-model/elements/wardley/wardley.js +1 -1
  185. package/dist/_pkgs/affine-shared/services/embed-iframe/embed-iframe-config.d.ts +1 -0
  186. package/dist/_pkgs/affine-shared/services/font-loader/config.d.ts +7 -0
  187. package/dist/_pkgs/affine-shared/services/font-loader/config.js +43 -0
  188. package/dist/_pkgs/affine-shared/services/pivot-materiality-publisher.js +1 -9
  189. package/dist/_pkgs/affine-shared/services/telemetry-service/document-damage-watcher.d.ts +53 -0
  190. package/dist/_pkgs/affine-shared/services/telemetry-service/document-damage-watcher.js +95 -0
  191. package/dist/_pkgs/affine-shared/services/telemetry-service/index.d.ts +1 -0
  192. package/dist/_pkgs/affine-shared/services/telemetry-service/index.js +1 -0
  193. package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +26 -1
  194. package/dist/_pkgs/affine-shared/services/telemetry-service/telemetry-service.d.ts +2 -2
  195. package/dist/_pkgs/affine-shared/services/toolbar-service/command-action.d.ts +34 -0
  196. package/dist/_pkgs/affine-shared/services/toolbar-service/command-action.js +63 -0
  197. package/dist/_pkgs/affine-shared/services/toolbar-service/index.d.ts +1 -0
  198. package/dist/_pkgs/affine-shared/services/toolbar-service/index.js +1 -0
  199. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +77 -7
  200. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +97 -7
  201. package/dist/_pkgs/affine-shared/styles/index.d.ts +1 -0
  202. package/dist/_pkgs/affine-shared/styles/index.js +1 -0
  203. package/dist/_pkgs/affine-shared/styles/motion.d.ts +14 -0
  204. package/dist/_pkgs/affine-shared/styles/motion.js +38 -0
  205. package/dist/_pkgs/affine-shared/utils/event.d.ts +10 -0
  206. package/dist/_pkgs/affine-shared/utils/event.js +20 -0
  207. package/dist/_pkgs/affine-shared/utils/file/filesys.d.ts +16 -0
  208. package/dist/_pkgs/affine-shared/utils/file/filesys.js +25 -0
  209. package/dist/_pkgs/affine-shared/utils/index.d.ts +1 -0
  210. package/dist/_pkgs/affine-shared/utils/index.js +1 -0
  211. package/dist/_pkgs/affine-shared/utils/link-preview-refresh.d.ts +12 -0
  212. package/dist/_pkgs/affine-shared/utils/link-preview-refresh.js +13 -0
  213. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.d.ts +12 -4
  214. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.js +27 -4
  215. package/dist/_pkgs/affine-widget-edgeless-toolbar/create-popper.js +13 -0
  216. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.d.ts +2 -0
  217. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.js +2 -0
  218. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.d.ts +80 -0
  219. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.js +270 -81
  220. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/senior-menu-selection.d.ts +15 -0
  221. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/senior-menu-selection.js +15 -0
  222. package/dist/_pkgs/affine-widget-edgeless-toolbar/panel/font-weight-and-style-panel.js +8 -1
  223. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/artefact-ghost-overlay.d.ts +42 -0
  224. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/artefact-ghost-overlay.js +100 -0
  225. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/artefact-placement-tool.d.ts +100 -0
  226. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/artefact-placement-tool.js +274 -0
  227. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/index.d.ts +3 -0
  228. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/index.js +3 -0
  229. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/translate-created.d.ts +25 -0
  230. package/dist/_pkgs/affine-widget-edgeless-toolbar/placement/translate-created.js +67 -0
  231. package/dist/_pkgs/affine-widget-edgeless-toolbar/view.js +5 -0
  232. package/dist/_pkgs/global/gfx/xywh.js +7 -0
  233. package/dist/_pkgs/std/extension/command-registry.d.ts +158 -1
  234. package/dist/_pkgs/std/extension/command-registry.js +22 -0
  235. package/dist/_pkgs/std/gfx/index.d.ts +1 -1
  236. package/dist/_pkgs/std/gfx/interactivity/gfx-view-event-handler.d.ts +12 -0
  237. package/dist/_pkgs/std/gfx/interactivity/gfx-view-event-handler.js +20 -1
  238. package/dist/_pkgs/std/gfx/interactivity/manager.d.ts +10 -0
  239. package/dist/_pkgs/std/gfx/interactivity/manager.js +12 -0
  240. package/dist/_pkgs/std/gfx/model/surface/element-model.js +12 -7
  241. package/dist/_pkgs/std/gfx/model/surface/surface-model.d.ts +56 -0
  242. package/dist/_pkgs/std/gfx/model/surface/surface-model.js +84 -3
  243. package/dist/_pkgs/std/gfx/view/view.d.ts +11 -2
  244. package/dist/_pkgs/std/gfx/view/view.js +16 -2
  245. package/dist/commands.d.ts +6 -0
  246. package/dist/commands.js +8 -1
  247. package/dist/frameworks.d.ts +9 -0
  248. package/dist/frameworks.js +19 -0
  249. package/dist/translations.js +16 -0
  250. package/package.json +9 -4
  251. package/dist/_pkgs/affine-gfx-group/group-watcher.d.ts +0 -1
  252. package/dist/_pkgs/affine-gfx-group/group-watcher.js +0 -20
@@ -0,0 +1,89 @@
1
+ import { FrameworkBackgroundElementModel } from '../../../affine-model/index.js';
2
+ import { downloadBlob, safeFilename } from '../../../affine-shared/utils/index.js';
3
+ import { GfxControllerIdentifier } from '../../../std/gfx/index.js';
4
+ import { renderBoardSvg } from './render.js';
5
+ /** What the file is, told to the browser. */
6
+ const SVG_MIME = 'image/svg+xml';
7
+ /** The name a board with no document title falls back to. */
8
+ const SVG_FILENAME_FALLBACK = 'board';
9
+ /**
10
+ * The framework boards in the current canvas selection.
11
+ *
12
+ * `FrameworkBackgroundElementModel` is the whole test, and that is the point of
13
+ * this command: eleven board kinds today — a Wardley map, a BPMN pool, a C4
14
+ * board and its boundaries, the three DDD boards, Cynefin, Estuarine, the two
15
+ * EDGY ones — and whatever a framework adds next, because a board IS the class
16
+ * and nothing about SVG is per framework (ADR 0025).
17
+ */
18
+ export function selectedBoards(std) {
19
+ return std
20
+ .get(GfxControllerIdentifier)
21
+ .selection.selectedElements.filter((element) => element instanceof FrameworkBackgroundElementModel);
22
+ }
23
+ /**
24
+ * Render the selected board and hand it to the browser.
25
+ *
26
+ * ponytail: ONE board per click, the first of the selection. Browsers throttle
27
+ * (and Safari silently drops) programmatic downloads fired in a burst, so a
28
+ * selection of three boards would yield one file, two files or three depending
29
+ * on the browser — a result nobody can read off the gesture. One board, one
30
+ * file, every time.
31
+ */
32
+ export function exportBoardSvg(std) {
33
+ const [board] = selectedBoards(std);
34
+ if (!board)
35
+ return;
36
+ const { svg } = renderBoardSvg(std, board);
37
+ const title = std.store.workspace.meta.getDocMeta(std.store.id)?.title;
38
+ downloadBlob(
39
+ // The charset is the browser's business, not the format's: `image/svg+xml`
40
+ // is what the file IS, and this is how a blob is told to carry it.
41
+ new Blob([svg], { type: `${SVG_MIME};charset=utf-8` }), `${safeFilename(title, SVG_FILENAME_FALLBACK)}.svg`);
42
+ }
43
+ /**
44
+ * "Export SVG", in the command registry (ADR 0025).
45
+ *
46
+ * `owner: 'core'` and not a framework's, for the reason `map.audit` and
47
+ * `validation.mapQuality` are core: the command belongs to no framework —
48
+ * it renders whatever board is selected with the renderer that already paints
49
+ * it — and a framework owner would have forced the id prefix `wardley.` and
50
+ * then a second, identical command for every framework that follows. `'core'`
51
+ * owners are exempt from the id-prefix rule (ADR 0008).
52
+ *
53
+ * It READS, so there is no read-only guard: taking a picture of a board you
54
+ * cannot edit is precisely what a reader does.
55
+ *
56
+ * No `telemetry` field: `CommandTelemetry.framework` is typed on `FrameworkId`,
57
+ * and this command cannot name one — the surface package knows a board by its
58
+ * MODEL CLASS, and nothing in it maps `bpmnPool` back to `bpmn` without
59
+ * importing the framework packages the layering forbids. Usage is still
60
+ * measured, because `runCommand` records it outside the telemetry condition.
61
+ */
62
+ const exportSvg = {
63
+ id: 'export.svg',
64
+ owner: 'core',
65
+ kind: 'action',
66
+ labelKey: 'com.labre.command.export.svg',
67
+ labelFallback: 'Export SVG',
68
+ descriptionKey: 'com.labre.command.export.svg.description',
69
+ descriptionFallback: 'Download the selected board and everything drawn on it as an SVG file.',
70
+ // The category the framework exports already file under: this is the same
71
+ // shelf as `wardley.exportOwm`, `bpmn.exportXml` and `c4.exportMermaid`, and
72
+ // a host catalogue groups them together.
73
+ category: 'interchange',
74
+ // Deliberately NOT `'senior-menu'`: those slots are for artefacts you DRAW,
75
+ // and "Export SVG" answers no question asked of a row of shapes — the same
76
+ // arbitration `bpmn.exportXml` is filed under (ADR 0014, R4).
77
+ surfaces: ['catalogue', 'contextual-toolbar', 'palette', 'agent'],
78
+ scope: 'edgeless',
79
+ // Keyless by intent. Still bindable from Settings › Shortcuts, which is what
80
+ // `toShortcutDescriptor` being total buys.
81
+ defaultKeys: { mac: [], other: [] },
82
+ // The serializable precondition a host catalogue can show; `when` narrows it
83
+ // to "…and that selection contains a board", which no member of the closed
84
+ // union can express.
85
+ availability: 'selection:framework',
86
+ when: std => selectedBoards(std).length > 0,
87
+ run: std => exportBoardSvg(std),
88
+ };
89
+ export const exportSvgCommands = [exportSvg];
@@ -0,0 +1,7 @@
1
+ export { exportBoardSvg, exportSvgCommands, selectedBoards, } from './command.js';
2
+ export type { BoardSvgExport } from './render.js';
3
+ export { exportBoundOf, renderBoardSvg, selectBoardElements, } from './render.js';
4
+ export { taggedPath2D } from './tagged-path.js';
5
+ export type { SvgContext } from './svg-context.js';
6
+ export { createSvgContext, runWithRecordingPath2D } from './svg-context.js';
7
+ export { exportSvgToolbarConfig, exportSvgToolbarExtension, } from './toolbar.js';
@@ -0,0 +1,5 @@
1
+ export { exportBoardSvg, exportSvgCommands, selectedBoards, } from './command.js';
2
+ export { exportBoundOf, renderBoardSvg, selectBoardElements, } from './render.js';
3
+ export { taggedPath2D } from './tagged-path.js';
4
+ export { createSvgContext, runWithRecordingPath2D } from './svg-context.js';
5
+ export { exportSvgToolbarConfig, exportSvgToolbarExtension, } from './toolbar.js';
@@ -0,0 +1,41 @@
1
+ import { FrameworkBackgroundElementModel } from '../../../affine-model/index.js';
2
+ import { Bound } from '../../../global/gfx/index.js';
3
+ import type { BlockStdScope } from '../../../std/index.js';
4
+ import { type GfxPrimitiveElementModel } from '../../../std/gfx/index.js';
5
+ export interface BoardSvgExport {
6
+ /** A standalone SVG document. */
7
+ svg: string;
8
+ /** The world-space rectangle the document covers. */
9
+ bound: Bound;
10
+ }
11
+ /**
12
+ * The canvas elements that make up `board`'s picture, in paint order.
13
+ *
14
+ * `candidates` comes from a bound query, so it also holds whatever a NEIGHBOUR
15
+ * board happens to overlap with. Another framework background is kept only
16
+ * when its frame lies ENTIRELY inside the exported one (edges may touch): that
17
+ * is a nested frame — a UML subject, partition, region or fragment inside its
18
+ * diagram frame, a C4 boundary inside a C4 board — and it is part of the
19
+ * picture. One that merely overlaps is a neighbour and is dropped: without
20
+ * that, a map sitting next to this one would paint its whole frame into this
21
+ * file. So is one that ENCLOSES the board, the diagram frame around an
22
+ * exported partition. Non-background elements are kept whoever owns them — an
23
+ * element inside the exported frame belongs to the picture.
24
+ *
25
+ * Containment is read on the stored `xywh`, the same rectangle
26
+ * {@link exportBoundOf} starts from; a background at exactly the board's own
27
+ * rectangle therefore counts as nested.
28
+ */
29
+ export declare function selectBoardElements<T extends GfxPrimitiveElementModel>(board: FrameworkBackgroundElementModel, candidates: readonly T[]): (FrameworkBackgroundElementModel | T)[];
30
+ /**
31
+ * The board's frame, widened to the union of what is painted on it: a Wardley
32
+ * label overhanging the right edge, or a connector ending just outside, is part
33
+ * of the picture and must not be cut off.
34
+ */
35
+ export declare function exportBoundOf(board: FrameworkBackgroundElementModel, elements: readonly GfxPrimitiveElementModel[]): Bound;
36
+ /**
37
+ * The selected board and everything painted within its frame, as an SVG
38
+ * document — a true vector export, produced by replaying the very element
39
+ * renderers the canvas uses into an SVG-emitting 2D context.
40
+ */
41
+ export declare function renderBoardSvg(std: BlockStdScope, board: FrameworkBackgroundElementModel): BoardSvgExport;
@@ -0,0 +1,70 @@
1
+ import { FrameworkBackgroundElementModel } from '../../../affine-model/index.js';
2
+ import { Bound } from '../../../global/gfx/index.js';
3
+ import { GfxControllerIdentifier, } from '../../../std/gfx/index.js';
4
+ import { CanvasRenderer } from '../../renderer/canvas-renderer.js';
5
+ import { RoughCanvas } from '../../utils/rough/canvas.js';
6
+ import { createSvgContext, runWithRecordingPath2D } from './svg-context.js';
7
+ /**
8
+ * The canvas elements that make up `board`'s picture, in paint order.
9
+ *
10
+ * `candidates` comes from a bound query, so it also holds whatever a NEIGHBOUR
11
+ * board happens to overlap with. Another framework background is kept only
12
+ * when its frame lies ENTIRELY inside the exported one (edges may touch): that
13
+ * is a nested frame — a UML subject, partition, region or fragment inside its
14
+ * diagram frame, a C4 boundary inside a C4 board — and it is part of the
15
+ * picture. One that merely overlaps is a neighbour and is dropped: without
16
+ * that, a map sitting next to this one would paint its whole frame into this
17
+ * file. So is one that ENCLOSES the board, the diagram frame around an
18
+ * exported partition. Non-background elements are kept whoever owns them — an
19
+ * element inside the exported frame belongs to the picture.
20
+ *
21
+ * Containment is read on the stored `xywh`, the same rectangle
22
+ * {@link exportBoundOf} starts from; a background at exactly the board's own
23
+ * rectangle therefore counts as nested.
24
+ */
25
+ export function selectBoardElements(board, candidates) {
26
+ const frame = Bound.deserialize(board.xywh);
27
+ const kept = candidates.filter(element => element === board ||
28
+ !(element instanceof FrameworkBackgroundElementModel) ||
29
+ frame.contains(Bound.deserialize(element.xywh)));
30
+ // A background paints under everything, so a board missing from the query
31
+ // (an empty bound, a stub) goes in front of the list, not at the end.
32
+ return kept.some(element => element === board)
33
+ ? kept
34
+ : [board, ...kept];
35
+ }
36
+ /**
37
+ * The board's frame, widened to the union of what is painted on it: a Wardley
38
+ * label overhanging the right edge, or a connector ending just outside, is part
39
+ * of the picture and must not be cut off.
40
+ */
41
+ export function exportBoundOf(board, elements) {
42
+ let bound = Bound.deserialize(board.xywh);
43
+ for (const element of elements) {
44
+ bound = bound.unite(Bound.deserialize(element.xywh));
45
+ }
46
+ return bound;
47
+ }
48
+ /**
49
+ * The selected board and everything painted within its frame, as an SVG
50
+ * document — a true vector export, produced by replaying the very element
51
+ * renderers the canvas uses into an SVG-emitting 2D context.
52
+ */
53
+ export function renderBoardSvg(std, board) {
54
+ const gfx = std.get(GfxControllerIdentifier);
55
+ const renderer = gfx.surfaceComponent?.renderer;
56
+ if (!(renderer instanceof CanvasRenderer)) {
57
+ throw new Error('SVG export needs the surface to be painted by a CanvasRenderer.');
58
+ }
59
+ // Already sorted by `layer.compare`, which is the canvas' own z-order.
60
+ const candidates = gfx.getElementsByBound(Bound.deserialize(board.xywh), {
61
+ type: 'canvas',
62
+ });
63
+ const elements = selectBoardElements(board, candidates);
64
+ const bound = exportBoundOf(board, elements);
65
+ return runWithRecordingPath2D(() => {
66
+ const { ctx, canvas, serialize } = createSvgContext(bound.w, bound.h);
67
+ renderer.renderBoundTo(ctx, new RoughCanvas(canvas), bound, elements);
68
+ return { svg: serialize(), bound };
69
+ });
70
+ }
@@ -0,0 +1,49 @@
1
+ /**
2
+ * A 2D context that records an SVG document instead of pixels, shaped so the
3
+ * existing canvas renderers can paint into it unchanged.
4
+ */
5
+ export interface SvgContext {
6
+ /** What the element renderers draw into. */
7
+ ctx: CanvasRenderingContext2D;
8
+ /** What `new RoughCanvas(...)` takes: `getContext('2d')` returns {@link ctx}. */
9
+ canvas: HTMLCanvasElement;
10
+ /** The finished document, with `viewBox`, `width`/`height` in px and `xmlns`. */
11
+ serialize(): string;
12
+ }
13
+ /** Marks a {@link RecordingPath2D} and carries what it recorded. */
14
+ declare const RECORDED_D: unique symbol;
15
+ interface RecordedPath {
16
+ [RECORDED_D]: string;
17
+ }
18
+ /** A `Path2D` that also remembers itself as an SVG `d`, in the caller's space. */
19
+ export type RecordingPath2D = Path2D & RecordedPath & {
20
+ readonly d: string;
21
+ };
22
+ /**
23
+ * Runs `render` with `globalThis.Path2D` swapped for the recording subclass, so
24
+ * the `new Path2D(d)` a renderer builds can be replayed into the SVG document.
25
+ *
26
+ * Swapping a global is safe here because the swap and the restore bracket a
27
+ * SYNCHRONOUS call on a single-threaded runtime: no other code can run — let
28
+ * alone observe `Path2D` — between the two, and the `finally` restores the
29
+ * original even when a renderer throws.
30
+ */
31
+ export declare function runWithRecordingPath2D<T>(render: () => T): T;
32
+ /**
33
+ * The dash pattern svgcanvas holds, as the `number[]` Canvas2D's `getLineDash`
34
+ * returns.
35
+ *
36
+ * svgcanvas 2.6.0 implements `setLineDash` but not `getLineDash`, and a
37
+ * renderer that reads the pattern back (to put it back after a dashed stroke)
38
+ * would throw and abort the whole export. Its `setLineDash` stores the pattern
39
+ * in the `lineDash` style — a comma-joined string, `null` once cleared, the
40
+ * default `[]` before any call — and that style is saved and restored with the
41
+ * rest of the drawing state, so reading it is exact.
42
+ */
43
+ export declare function readLineDash(lineDash: unknown): number[];
44
+ /**
45
+ * An SVG-emitting 2D context of `width` × `height` user units, ready to be
46
+ * handed to {@link CanvasRenderer.renderBoundTo}.
47
+ */
48
+ export declare function createSvgContext(width: number, height: number): SvgContext;
49
+ export {};
@@ -0,0 +1,257 @@
1
+ import { Context } from 'svgcanvas/dist/svgcanvas.esm.js';
2
+ const PATH_OPS = ['fill', 'stroke', 'clip'];
3
+ /** Marks a {@link RecordingPath2D} and carries what it recorded. */
4
+ const RECORDED_D = Symbol('labre.export-svg.recorded-d');
5
+ function isRecordingPath(value) {
6
+ return (typeof value?.[RECORDED_D] === 'string');
7
+ }
8
+ /**
9
+ * The SVG `d` of a path argument, or `undefined` when it cannot be read.
10
+ *
11
+ * Duck-typed on a string `d` so it covers both paths this module recorded and
12
+ * the native ones `taggedPath2D` tagged — a renderer is free to memoise those,
13
+ * and a memo built during an ordinary canvas paint must still be readable here.
14
+ */
15
+ function readablePathD(value) {
16
+ const d = value?.d;
17
+ return typeof d === 'string' ? d : undefined;
18
+ }
19
+ /** A path argument whose geometry is unreadable: a bare native `Path2D`. */
20
+ function isOpaquePath(value) {
21
+ return (typeof value === 'object' &&
22
+ value !== null &&
23
+ readablePathD(value) === undefined);
24
+ }
25
+ function isEvenOdd(value) {
26
+ return value === 'evenodd';
27
+ }
28
+ function unsupported(method) {
29
+ return new Error(`SVG export: Path2D.${method}() is not recorded. Add it to RecordingPath2D ` +
30
+ `in extensions/export-svg/svg-context.ts, or draw it on the context directly.`);
31
+ }
32
+ let recordingPath2D;
33
+ /**
34
+ * Built on first use rather than at import time: it extends the platform
35
+ * `Path2D`, which does not exist under Node, and merely importing this module
36
+ * must not throw there (same arrangement as `gfx/cynefin-estuarine`'s lazy
37
+ * curves).
38
+ *
39
+ * It EXTENDS `Path2D` on purpose. A renderer is free to memoise the paths it
40
+ * builds (`gfx/cynefin-estuarine` does), so an instance created while the shim
41
+ * is installed can outlive the export and be handed to a real canvas context
42
+ * later — which only works if it is a genuine `Path2D` as well.
43
+ */
44
+ function getRecordingPath2D() {
45
+ var _a;
46
+ if (recordingPath2D)
47
+ return recordingPath2D;
48
+ const Base = globalThis.Path2D;
49
+ if (!Base) {
50
+ throw new Error('SVG export: Path2D is unavailable in this environment.');
51
+ }
52
+ class RecordingPath2DImpl extends Base {
53
+ static { _a = RECORDED_D; }
54
+ get d() {
55
+ return this[RECORDED_D];
56
+ }
57
+ constructor(source) {
58
+ super(source);
59
+ this[_a] = '';
60
+ if (typeof source === 'string') {
61
+ this[RECORDED_D] = source;
62
+ }
63
+ else if (isRecordingPath(source)) {
64
+ this[RECORDED_D] = source[RECORDED_D];
65
+ }
66
+ else if (source) {
67
+ throw unsupported('constructor(Path2D)');
68
+ }
69
+ }
70
+ _push(command) {
71
+ this[RECORDED_D] += this[RECORDED_D] ? ` ${command}` : command;
72
+ }
73
+ addPath(path, transform) {
74
+ if (transform)
75
+ throw unsupported('addPath(path, transform)');
76
+ if (!isRecordingPath(path))
77
+ throw unsupported('addPath(Path2D)');
78
+ super.addPath(path);
79
+ this._push(path[RECORDED_D]);
80
+ }
81
+ arc() {
82
+ throw unsupported('arc');
83
+ }
84
+ arcTo() {
85
+ throw unsupported('arcTo');
86
+ }
87
+ bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y) {
88
+ super.bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
89
+ this._push(`C ${cp1x} ${cp1y} ${cp2x} ${cp2y} ${x} ${y}`);
90
+ }
91
+ closePath() {
92
+ super.closePath();
93
+ this._push('Z');
94
+ }
95
+ ellipse() {
96
+ throw unsupported('ellipse');
97
+ }
98
+ lineTo(x, y) {
99
+ super.lineTo(x, y);
100
+ this._push(`L ${x} ${y}`);
101
+ }
102
+ moveTo(x, y) {
103
+ super.moveTo(x, y);
104
+ this._push(`M ${x} ${y}`);
105
+ }
106
+ quadraticCurveTo(cpx, cpy, x, y) {
107
+ super.quadraticCurveTo(cpx, cpy, x, y);
108
+ this._push(`Q ${cpx} ${cpy} ${x} ${y}`);
109
+ }
110
+ rect(x, y, w, h) {
111
+ super.rect(x, y, w, h);
112
+ this._push(`M ${x} ${y} L ${x + w} ${y} L ${x + w} ${y + h} L ${x} ${y + h} Z`);
113
+ }
114
+ roundRect() {
115
+ throw unsupported('roundRect');
116
+ }
117
+ }
118
+ recordingPath2D =
119
+ RecordingPath2DImpl;
120
+ return recordingPath2D;
121
+ }
122
+ /**
123
+ * Runs `render` with `globalThis.Path2D` swapped for the recording subclass, so
124
+ * the `new Path2D(d)` a renderer builds can be replayed into the SVG document.
125
+ *
126
+ * Swapping a global is safe here because the swap and the restore bracket a
127
+ * SYNCHRONOUS call on a single-threaded runtime: no other code can run — let
128
+ * alone observe `Path2D` — between the two, and the `finally` restores the
129
+ * original even when a renderer throws.
130
+ */
131
+ export function runWithRecordingPath2D(render) {
132
+ const original = globalThis.Path2D;
133
+ globalThis.Path2D = getRecordingPath2D();
134
+ try {
135
+ return render();
136
+ }
137
+ finally {
138
+ globalThis.Path2D = original;
139
+ }
140
+ }
141
+ /**
142
+ * Teaches `fill`/`stroke`/`clip` the two things svgcanvas drops on the floor:
143
+ * the `Path2D` argument, and the fill rule.
144
+ */
145
+ function installPathOps(ctx) {
146
+ for (const op of PATH_OPS) {
147
+ const original = ctx[op];
148
+ ctx[op] = (...args) => {
149
+ const d = args.map(readablePathD).find(value => value !== undefined);
150
+ const hasPath = d !== undefined;
151
+ const evenOdd = args.some(isEvenOdd);
152
+ if (hasPath) {
153
+ // svgcanvas ignores Path2D arguments and always paints its "current
154
+ // default path", so the path's `d` becomes exactly that.
155
+ ctx.beginPath();
156
+ ctx.__currentDefaultPath = d;
157
+ }
158
+ else if (args.some(isOpaquePath)) {
159
+ // An untagged native Path2D. Nothing can read its geometry back, so
160
+ // drop it loudly rather than let svgcanvas paint whatever path happened
161
+ // to be current. `taggedPath2D` is the fix at the call site.
162
+ ctx.beginPath();
163
+ ctx.__currentDefaultPath = '';
164
+ console.warn('SVG export: an untagged Path2D cannot be read back; that shape is ' +
165
+ 'missing from the SVG. Build it with `taggedPath2D`.');
166
+ }
167
+ // `clip()` moves this node into a <clipPath>, so grab it beforehand.
168
+ const element = ctx.__currentElement;
169
+ const lineWidth = ctx.lineWidth;
170
+ original.call(ctx);
171
+ if (hasPath) {
172
+ // svgcanvas BAKES the current matrix into every coordinate it emits;
173
+ // an injected `d` never went through that, so the matrix rides along
174
+ // as a `transform` attribute instead…
175
+ ctx.__applyTransformation(element);
176
+ // …and the stroke width, which svgcanvas pre-multiplied by the matrix
177
+ // scale, goes back to its raw value or the transform scales it twice.
178
+ if (op === 'stroke') {
179
+ element.setAttribute('stroke-width', `${lineWidth}`);
180
+ }
181
+ }
182
+ if (evenOdd) {
183
+ element.setAttribute(op === 'clip' ? 'clip-rule' : 'fill-rule', 'evenodd');
184
+ }
185
+ };
186
+ }
187
+ }
188
+ /**
189
+ * The dash pattern svgcanvas holds, as the `number[]` Canvas2D's `getLineDash`
190
+ * returns.
191
+ *
192
+ * svgcanvas 2.6.0 implements `setLineDash` but not `getLineDash`, and a
193
+ * renderer that reads the pattern back (to put it back after a dashed stroke)
194
+ * would throw and abort the whole export. Its `setLineDash` stores the pattern
195
+ * in the `lineDash` style — a comma-joined string, `null` once cleared, the
196
+ * default `[]` before any call — and that style is saved and restored with the
197
+ * rest of the drawing state, so reading it is exact.
198
+ */
199
+ export function readLineDash(lineDash) {
200
+ if (Array.isArray(lineDash))
201
+ return lineDash.map(Number);
202
+ if (typeof lineDash !== 'string' || lineDash === '')
203
+ return [];
204
+ return lineDash.split(',').map(Number);
205
+ }
206
+ /** Adds `viewBox` / `xmlns` / px units to the root `<svg>` if they are missing. */
207
+ function finishSvg(svg, width, height) {
208
+ return (svg
209
+ // Models store fonts under the loader's own face name
210
+ // (`blocksuite:surface:Inter`); outside the editor only `Inter` exists.
211
+ .replace(/blocksuite:surface:/g, '')
212
+ // svgcanvas writes the (unset) text decoration out literally.
213
+ .replace(/ text-decoration="undefined"/g, '')
214
+ .replace(/<svg\b[^>]*>/, tag => {
215
+ let out = tag;
216
+ if (!/\sviewBox=/.test(out)) {
217
+ out = out.replace(/^<svg/, `<svg viewBox="0 0 ${width} ${height}"`);
218
+ }
219
+ if (!/\sxmlns=/.test(out)) {
220
+ out = out.replace(/^<svg/, `<svg xmlns="http://www.w3.org/2000/svg"`);
221
+ }
222
+ out = /\swidth=/.test(out)
223
+ ? out.replace(/\swidth="[^"]*"/, ` width="${width}px"`)
224
+ : out.replace(/^<svg/, `<svg width="${width}px"`);
225
+ out = /\sheight=/.test(out)
226
+ ? out.replace(/\sheight="[^"]*"/, ` height="${height}px"`)
227
+ : out.replace(/^<svg/, `<svg height="${height}px"`);
228
+ return out;
229
+ }));
230
+ }
231
+ /**
232
+ * An SVG-emitting 2D context of `width` × `height` user units, ready to be
233
+ * handed to {@link CanvasRenderer.renderBoundTo}.
234
+ */
235
+ export function createSvgContext(width, height) {
236
+ const context = new Context({ width, height });
237
+ const ctx = context;
238
+ // `RoughCanvas` takes the CANVAS and calls `getContext('2d')` on it.
239
+ // svgcanvas already points `canvas` back at the context, so one method
240
+ // closes the loop.
241
+ context.getContext = () => ctx;
242
+ // Text renderers temporarily attach a detached canvas to <body> to measure
243
+ // RTL runs. This context is not a DOM node, so claim it is already attached.
244
+ context.isConnected = true;
245
+ // …and they set `dir` on the canvas before every text run; a no-op keeps
246
+ // that call from reaching an object that is not an element.
247
+ context.setAttribute = () => { };
248
+ // The one Canvas2D method a renderer calls that svgcanvas lacks — see
249
+ // `readLineDash`. Without it a single `getLineDash()` aborts the export.
250
+ context.getLineDash = () => readLineDash(context.lineDash);
251
+ installPathOps(context);
252
+ return {
253
+ ctx,
254
+ canvas: ctx.canvas,
255
+ serialize: () => finishSvg(context.getSerializedSvg(true), width, height),
256
+ };
257
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * A `Path2D` that carries the `d` it was built from.
3
+ *
4
+ * The SVG export replays the element renderers into an SVG-emitting context,
5
+ * and a native `Path2D` is a black box: nothing can read its geometry back, so
6
+ * a shape drawn through one would be missing from the export. The tag is what
7
+ * the export reads — see `svg-context.ts`.
8
+ *
9
+ * Use it anywhere a renderer builds a `Path2D` FROM A STRING. A path built by
10
+ * calling methods (`new Path2D()` then `rect(...)`) needs no tag: the export
11
+ * records those calls itself.
12
+ *
13
+ * It is a plain function, not a constant: `Path2D` does not exist under Node,
14
+ * so this must only ever run on a paint, never at module load.
15
+ */
16
+ export declare function taggedPath2D(d: string): Path2D;
@@ -0,0 +1,24 @@
1
+ /**
2
+ * A `Path2D` that carries the `d` it was built from.
3
+ *
4
+ * The SVG export replays the element renderers into an SVG-emitting context,
5
+ * and a native `Path2D` is a black box: nothing can read its geometry back, so
6
+ * a shape drawn through one would be missing from the export. The tag is what
7
+ * the export reads — see `svg-context.ts`.
8
+ *
9
+ * Use it anywhere a renderer builds a `Path2D` FROM A STRING. A path built by
10
+ * calling methods (`new Path2D()` then `rect(...)`) needs no tag: the export
11
+ * records those calls itself.
12
+ *
13
+ * It is a plain function, not a constant: `Path2D` does not exist under Node,
14
+ * so this must only ever run on a paint, never at module load.
15
+ */
16
+ export function taggedPath2D(d) {
17
+ const path = new Path2D(d);
18
+ // While the export's recording `Path2D` is installed, the instance already
19
+ // exposes `d` through a getter; writing it again would throw.
20
+ if (typeof path.d !== 'string') {
21
+ Object.defineProperty(path, 'd', { value: d, enumerable: true });
22
+ }
23
+ return path;
24
+ }
@@ -0,0 +1,24 @@
1
+ import { type ToolbarContext } from '../../../affine-shared/services/index.js';
2
+ export declare const exportSvgToolbarConfig: {
3
+ readonly actions: [import("../../../affine-shared/services/index.js").ToolbarActionGenerator];
4
+ readonly when: (ctx: ToolbarContext) => boolean;
5
+ };
6
+ /**
7
+ * "Export SVG" in the "⋮" of EVERY framework board (ADR 0025).
8
+ *
9
+ * ## Where it is registered
10
+ *
11
+ * `custom:affine:surface:*` — the wildcard slot merged into the toolbar of every
12
+ * canvas element — with an OWNER suffix. The bare key is already taken by
13
+ * `validation-toolbar.ts` (and `affine:surface:*` by the root's built-in misc
14
+ * module), so a second module on either would be a
15
+ * `DuplicateServiceDefinitionError` at setup rather than a menu entry;
16
+ * {@link toolbarModuleKey} is what gives a second contributor a distinct DI
17
+ * variant while `modulesFor` still hands it to the row drawn for that flavour.
18
+ *
19
+ * One registration therefore covers all eleven board kinds and every one that
20
+ * follows, which is the whole claim of the ADR: the export is generic, so it is
21
+ * registered once, generically, and a new framework inherits it by extending
22
+ * `FrameworkBackgroundElementModel` and doing nothing else.
23
+ */
24
+ export declare const exportSvgToolbarExtension: import("../../../store/index.js").ExtensionType;
@@ -0,0 +1,66 @@
1
+ import { FrameworkBackgroundElementModel } from '../../../affine-model/index.js';
2
+ import { commandMoreAction, ToolbarModuleExtension, toolbarModuleKey, } from '../../../affine-shared/services/index.js';
3
+ import { BlockFlavourIdentifier } from '../../../std/index.js';
4
+ import { html } from 'lit';
5
+ /**
6
+ * A sheet with an arrow leaving it: the board, on its way out of the editor.
7
+ *
8
+ * Drawn inline like its neighbours in this package (`reading-toolbar.ts`,
9
+ * `tags-toolbar.ts`, `validation-toolbar.ts`): the surface block does not
10
+ * depend on `@blocksuite/icons`, and one glyph is not worth a new dependency.
11
+ */
12
+ const ExportSvgIcon = html `<svg
13
+ width="20"
14
+ height="20"
15
+ viewBox="0 0 24 24"
16
+ fill="none"
17
+ stroke="currentColor"
18
+ stroke-width="2"
19
+ stroke-linecap="round"
20
+ stroke-linejoin="round"
21
+ >
22
+ <path d="M12 3v12" />
23
+ <path d="M8 11l4 4 4-4" />
24
+ <path d="M4 17v2a2 2 0 002 2h12a2 2 0 002-2v-2" />
25
+ </svg>`;
26
+ /** The keys the command declares; spelled once, read here and nowhere else. */
27
+ const EXPORT_SVG_COMMAND_ID = 'export.svg';
28
+ const EXPORT_SVG_LABEL_KEY = 'com.labre.command.export.svg';
29
+ const EXPORT_SVG_LABEL_FALLBACK = 'Export SVG';
30
+ export const exportSvgToolbarConfig = {
31
+ actions: [
32
+ commandMoreAction(
33
+ // `z.z-` and not `z.`: a framework's own export (`z.export-owm`,
34
+ // `z.export-xml`, `z.export-mermaid`) is the format that says what the
35
+ // board MEANS, and it reads first. The picture reads last, under
36
+ // everything, which is also where a reader looks for it.
37
+ 'z.z-export-svg', EXPORT_SVG_COMMAND_ID, EXPORT_SVG_LABEL_KEY, EXPORT_SVG_LABEL_FALLBACK, ExportSvgIcon),
38
+ ],
39
+ // The module says "a board is selected"; the command's own `when` says the
40
+ // same thing from the registry side, and the two are deliberately both there
41
+ // — the entry is drawn from the module and the behaviour is guarded by the
42
+ // command, whichever surface reaches it.
43
+ when: (ctx) => ctx.getSurfaceModelsByType(FrameworkBackgroundElementModel).length > 0,
44
+ };
45
+ /**
46
+ * "Export SVG" in the "⋮" of EVERY framework board (ADR 0025).
47
+ *
48
+ * ## Where it is registered
49
+ *
50
+ * `custom:affine:surface:*` — the wildcard slot merged into the toolbar of every
51
+ * canvas element — with an OWNER suffix. The bare key is already taken by
52
+ * `validation-toolbar.ts` (and `affine:surface:*` by the root's built-in misc
53
+ * module), so a second module on either would be a
54
+ * `DuplicateServiceDefinitionError` at setup rather than a menu entry;
55
+ * {@link toolbarModuleKey} is what gives a second contributor a distinct DI
56
+ * variant while `modulesFor` still hands it to the row drawn for that flavour.
57
+ *
58
+ * One registration therefore covers all eleven board kinds and every one that
59
+ * follows, which is the whole claim of the ADR: the export is generic, so it is
60
+ * registered once, generically, and a new framework inherits it by extending
61
+ * `FrameworkBackgroundElementModel` and doing nothing else.
62
+ */
63
+ export const exportSvgToolbarExtension = ToolbarModuleExtension({
64
+ id: BlockFlavourIdentifier(toolbarModuleKey('custom:affine:surface:*', 'export-svg')),
65
+ config: exportSvgToolbarConfig,
66
+ });