@formicoidea/labre-core 0.30.1 → 0.32.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 (1468) hide show
  1. package/dist/_pkgs/affine-block-attachment/attachment-block.js +3 -3
  2. package/dist/_pkgs/affine-block-attachment/components/rename-model.js +1 -1
  3. package/dist/_pkgs/affine-block-attachment/configs/slash-menu.js +2 -2
  4. package/dist/_pkgs/affine-block-attachment/configs/toolbar.js +3 -3
  5. package/dist/_pkgs/affine-block-attachment/effects.js +2 -2
  6. package/dist/_pkgs/affine-block-attachment/embed.js +1 -1
  7. package/dist/_pkgs/affine-block-attachment/index.d.ts +7 -7
  8. package/dist/_pkgs/affine-block-attachment/index.js +7 -7
  9. package/dist/_pkgs/affine-block-attachment/store.js +1 -1
  10. package/dist/_pkgs/affine-block-attachment/utils.d.ts +1 -1
  11. package/dist/_pkgs/affine-block-attachment/view.js +3 -3
  12. package/dist/_pkgs/affine-block-bookmark/commands/insert-link-by-quick-search.js +1 -1
  13. package/dist/_pkgs/affine-block-bookmark/components/index.d.ts +1 -1
  14. package/dist/_pkgs/affine-block-bookmark/components/index.js +1 -1
  15. package/dist/_pkgs/affine-block-bookmark/configs/slash-menu.js +1 -1
  16. package/dist/_pkgs/affine-block-bookmark/configs/toolbar.js +1 -1
  17. package/dist/_pkgs/affine-block-bookmark/effects.js +3 -3
  18. package/dist/_pkgs/affine-block-bookmark/index.d.ts +6 -6
  19. package/dist/_pkgs/affine-block-bookmark/index.js +6 -6
  20. package/dist/_pkgs/affine-block-bookmark/store.js +1 -1
  21. package/dist/_pkgs/affine-block-bookmark/view.js +5 -5
  22. package/dist/_pkgs/affine-block-callout/callout-block.d.ts +6 -0
  23. package/dist/_pkgs/affine-block-callout/callout-block.js +25 -3
  24. package/dist/_pkgs/affine-block-callout/callout-keymap.js +59 -19
  25. package/dist/_pkgs/affine-block-callout/commands/callout-to-paragraph.d.ts +20 -0
  26. package/dist/_pkgs/affine-block-callout/commands/callout-to-paragraph.js +60 -0
  27. package/dist/_pkgs/affine-block-callout/commands/split-callout.d.ts +17 -0
  28. package/dist/_pkgs/affine-block-callout/commands/split-callout.js +42 -0
  29. package/dist/_pkgs/affine-block-callout/configs/slash-menu.js +10 -7
  30. package/dist/_pkgs/affine-block-callout/effects.d.ts +2 -2
  31. package/dist/_pkgs/affine-block-callout/effects.js +2 -2
  32. package/dist/_pkgs/affine-block-callout/emoji-margin.d.ts +9 -0
  33. package/dist/_pkgs/affine-block-callout/emoji-margin.js +36 -0
  34. package/dist/_pkgs/affine-block-callout/emoji-menu.d.ts +5 -1
  35. package/dist/_pkgs/affine-block-callout/emoji-menu.js +5 -0
  36. package/dist/_pkgs/affine-block-callout/store.js +1 -1
  37. package/dist/_pkgs/affine-block-callout/view.js +3 -3
  38. package/dist/_pkgs/affine-block-code/code-block.d.ts +14 -0
  39. package/dist/_pkgs/affine-block-code/code-block.js +44 -6
  40. package/dist/_pkgs/affine-block-code/code-toolbar/components/code-toolbar.d.ts +2 -0
  41. package/dist/_pkgs/affine-block-code/code-toolbar/components/code-toolbar.js +23 -14
  42. package/dist/_pkgs/affine-block-code/code-toolbar/components/lang-button.d.ts +1 -1
  43. package/dist/_pkgs/affine-block-code/code-toolbar/components/more-menu.d.ts +16 -0
  44. package/dist/_pkgs/affine-block-code/code-toolbar/components/more-menu.js +93 -0
  45. package/dist/_pkgs/affine-block-code/code-toolbar/components/preview-button.d.ts +1 -1
  46. package/dist/_pkgs/affine-block-code/code-toolbar/components/preview-button.js +1 -1
  47. package/dist/_pkgs/affine-block-code/code-toolbar/config.js +52 -5
  48. package/dist/_pkgs/affine-block-code/code-toolbar/context.d.ts +1 -1
  49. package/dist/_pkgs/affine-block-code/effects.d.ts +6 -4
  50. package/dist/_pkgs/affine-block-code/effects.js +8 -6
  51. package/dist/_pkgs/affine-block-code/highlight/affine-code-unit.js +6 -1
  52. package/dist/_pkgs/affine-block-code/index.d.ts +11 -10
  53. package/dist/_pkgs/affine-block-code/index.js +11 -10
  54. package/dist/_pkgs/affine-block-code/line-numbers.d.ts +25 -0
  55. package/dist/_pkgs/affine-block-code/line-numbers.js +27 -0
  56. package/dist/_pkgs/affine-block-code/store.js +2 -2
  57. package/dist/_pkgs/affine-block-code/styles.js +31 -0
  58. package/dist/_pkgs/affine-block-code/turbo/code-layout-handler.d.ts +1 -1
  59. package/dist/_pkgs/affine-block-code/view.js +1 -1
  60. package/dist/_pkgs/affine-block-data-view/configs/slash-menu.js +1 -1
  61. package/dist/_pkgs/affine-block-data-view/data-view-block.js +1 -0
  62. package/dist/_pkgs/affine-block-data-view/effects.js +1 -1
  63. package/dist/_pkgs/affine-block-data-view/store.js +1 -1
  64. package/dist/_pkgs/affine-block-data-view/view.js +2 -2
  65. package/dist/_pkgs/affine-block-database/adapters/html.js +1 -1
  66. package/dist/_pkgs/affine-block-database/adapters/index.d.ts +4 -4
  67. package/dist/_pkgs/affine-block-database/adapters/index.js +4 -4
  68. package/dist/_pkgs/affine-block-database/adapters/markdown.js +1 -1
  69. package/dist/_pkgs/affine-block-database/adapters/utils.js +1 -1
  70. package/dist/_pkgs/affine-block-database/commands.js +1 -1
  71. package/dist/_pkgs/affine-block-database/components/title/index.js +4 -1
  72. package/dist/_pkgs/affine-block-database/configs/slash-menu.js +30 -3
  73. package/dist/_pkgs/affine-block-database/context/index.d.ts +1 -1
  74. package/dist/_pkgs/affine-block-database/context/index.js +1 -1
  75. package/dist/_pkgs/affine-block-database/database-block.d.ts +1 -1
  76. package/dist/_pkgs/affine-block-database/database-block.js +13 -0
  77. package/dist/_pkgs/affine-block-database/effects.js +11 -11
  78. package/dist/_pkgs/affine-block-database/index.d.ts +14 -14
  79. package/dist/_pkgs/affine-block-database/index.js +14 -14
  80. package/dist/_pkgs/affine-block-database/properties/model.js +3 -3
  81. package/dist/_pkgs/affine-block-database/properties/paste-url.d.ts +24 -0
  82. package/dist/_pkgs/affine-block-database/properties/paste-url.js +98 -0
  83. package/dist/_pkgs/affine-block-database/properties/rich-text/cell-renderer.js +9 -19
  84. package/dist/_pkgs/affine-block-database/properties/title/text.js +20 -25
  85. package/dist/_pkgs/affine-block-database/store.js +2 -2
  86. package/dist/_pkgs/affine-block-database/view.js +2 -2
  87. package/dist/_pkgs/affine-block-database/views/index.js +1 -0
  88. package/dist/_pkgs/affine-block-divider/effects.js +1 -1
  89. package/dist/_pkgs/affine-block-divider/index.d.ts +2 -2
  90. package/dist/_pkgs/affine-block-divider/index.js +2 -2
  91. package/dist/_pkgs/affine-block-divider/store.js +1 -1
  92. package/dist/_pkgs/affine-block-divider/view.js +2 -2
  93. package/dist/_pkgs/affine-block-edgeless-text/edgeless-text-block.js +10 -0
  94. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/config.d.ts +3 -0
  95. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/config.js +48 -1
  96. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/font-size.d.ts +17 -0
  97. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/font-size.js +24 -0
  98. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/index.d.ts +1 -1
  99. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/index.js +1 -1
  100. package/dist/_pkgs/affine-block-edgeless-text/effects.js +1 -1
  101. package/dist/_pkgs/affine-block-edgeless-text/index.d.ts +2 -2
  102. package/dist/_pkgs/affine-block-edgeless-text/index.js +2 -2
  103. package/dist/_pkgs/affine-block-edgeless-text/view.js +4 -4
  104. package/dist/_pkgs/affine-block-embed/configs/toolbar.d.ts +4 -4
  105. package/dist/_pkgs/affine-block-embed/effects.d.ts +17 -17
  106. package/dist/_pkgs/affine-block-embed/effects.js +18 -18
  107. package/dist/_pkgs/affine-block-embed/embed-figma-block/configs/slash-menu.js +1 -1
  108. package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-spec.js +5 -5
  109. package/dist/_pkgs/affine-block-embed/embed-figma-block/index.d.ts +1 -1
  110. package/dist/_pkgs/affine-block-embed/embed-figma-block/index.js +1 -1
  111. package/dist/_pkgs/affine-block-embed/embed-github-block/configs/slash-menu.js +1 -1
  112. package/dist/_pkgs/affine-block-embed/embed-github-block/embed-github-spec.js +5 -5
  113. package/dist/_pkgs/affine-block-embed/embed-github-block/index.d.ts +1 -1
  114. package/dist/_pkgs/affine-block-embed/embed-github-block/index.js +1 -1
  115. package/dist/_pkgs/affine-block-embed/embed-html-block/configs/toolbar.js +1 -1
  116. package/dist/_pkgs/affine-block-embed/embed-html-block/embed-html-spec.js +2 -2
  117. package/dist/_pkgs/affine-block-embed/embed-html-block/index.d.ts +1 -1
  118. package/dist/_pkgs/affine-block-embed/embed-html-block/index.js +1 -1
  119. package/dist/_pkgs/affine-block-embed/embed-iframe-block/adapters/html.js +1 -1
  120. package/dist/_pkgs/affine-block-embed/embed-iframe-block/adapters/index.d.ts +3 -3
  121. package/dist/_pkgs/affine-block-embed/embed-iframe-block/adapters/index.js +6 -6
  122. package/dist/_pkgs/affine-block-embed/embed-iframe-block/adapters/plain-text.js +1 -1
  123. package/dist/_pkgs/affine-block-embed/embed-iframe-block/commands/index.d.ts +2 -2
  124. package/dist/_pkgs/affine-block-embed/embed-iframe-block/commands/index.js +2 -2
  125. package/dist/_pkgs/affine-block-embed/embed-iframe-block/commands/insert-embed-iframe-with-url.js +1 -1
  126. package/dist/_pkgs/affine-block-embed/embed-iframe-block/commands/insert-empty-embed-iframe.d.ts +1 -1
  127. package/dist/_pkgs/affine-block-embed/embed-iframe-block/commands/insert-empty-embed-iframe.js +1 -1
  128. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-error-card.d.ts +1 -1
  129. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-error-card.js +1 -1
  130. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-idle-card.d.ts +1 -1
  131. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-idle-card.js +1 -1
  132. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-link-edit-popup.d.ts +1 -1
  133. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-link-edit-popup.js +1 -1
  134. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-link-input-popup.d.ts +1 -1
  135. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-link-input-popup.js +1 -1
  136. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-loading-card.d.ts +1 -1
  137. package/dist/_pkgs/affine-block-embed/embed-iframe-block/components/embed-iframe-loading-card.js +1 -1
  138. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/index.d.ts +2 -2
  139. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/index.js +2 -2
  140. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/excalidraw.js +1 -1
  141. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/google-docs.js +1 -1
  142. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/google-drive.js +1 -1
  143. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/index.d.ts +2 -2
  144. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/index.js +6 -6
  145. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/miro.js +1 -1
  146. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/providers/spotify.js +1 -1
  147. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/slash-menu/slash-menu.js +2 -2
  148. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +2 -2
  149. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-edgeless-iframe-block.d.ts +1 -1
  150. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-edgeless-iframe-block.js +1 -1
  151. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-spec.js +3 -3
  152. package/dist/_pkgs/affine-block-embed/embed-iframe-block/index.d.ts +8 -8
  153. package/dist/_pkgs/affine-block-embed/embed-iframe-block/index.js +8 -8
  154. package/dist/_pkgs/affine-block-embed/embed-loom-block/configs/slash-menu.js +1 -1
  155. package/dist/_pkgs/affine-block-embed/embed-loom-block/embed-loom-spec.js +5 -5
  156. package/dist/_pkgs/affine-block-embed/embed-loom-block/index.d.ts +1 -1
  157. package/dist/_pkgs/affine-block-embed/embed-loom-block/index.js +1 -1
  158. package/dist/_pkgs/affine-block-embed/embed-youtube-block/configs/slash-menu.js +1 -1
  159. package/dist/_pkgs/affine-block-embed/embed-youtube-block/embed-youtube-spec.js +5 -5
  160. package/dist/_pkgs/affine-block-embed/embed-youtube-block/index.d.ts +1 -1
  161. package/dist/_pkgs/affine-block-embed/embed-youtube-block/index.js +1 -1
  162. package/dist/_pkgs/affine-block-embed/index.d.ts +16 -16
  163. package/dist/_pkgs/affine-block-embed/index.js +16 -16
  164. package/dist/_pkgs/affine-block-embed/store.js +6 -6
  165. package/dist/_pkgs/affine-block-embed/types.d.ts +4 -4
  166. package/dist/_pkgs/affine-block-embed/types.js +4 -4
  167. package/dist/_pkgs/affine-block-embed/view.js +13 -13
  168. package/dist/_pkgs/affine-block-embed-doc/common/doc-trashed.d.ts +9 -0
  169. package/dist/_pkgs/affine-block-embed-doc/common/doc-trashed.js +10 -0
  170. package/dist/_pkgs/affine-block-embed-doc/common/render-linked-doc.d.ts +2 -2
  171. package/dist/_pkgs/affine-block-embed-doc/effects.d.ts +5 -5
  172. package/dist/_pkgs/affine-block-embed-doc/effects.js +5 -5
  173. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/commands/index.d.ts +1 -1
  174. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/commands/index.js +1 -1
  175. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/slash-menu.js +1 -1
  176. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/toolbar.js +1 -1
  177. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +10 -6
  178. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-spec.js +3 -3
  179. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/index.d.ts +8 -8
  180. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/index.js +8 -8
  181. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/commands/index.d.ts +1 -1
  182. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/commands/index.js +1 -1
  183. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js +2 -2
  184. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/edgeless-interaction.js +1 -1
  185. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +1 -1
  186. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-edgeless-synced-doc-block.d.ts +1 -1
  187. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-edgeless-synced-doc-block.js +2 -2
  188. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-synced-doc-block.d.ts +4 -0
  189. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-synced-doc-block.js +22 -3
  190. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-synced-doc-spec.js +2 -2
  191. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/index.d.ts +7 -7
  192. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/index.js +7 -7
  193. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/init-height-extension.js +2 -2
  194. package/dist/_pkgs/affine-block-embed-doc/index.d.ts +2 -2
  195. package/dist/_pkgs/affine-block-embed-doc/index.js +2 -2
  196. package/dist/_pkgs/affine-block-embed-doc/store.js +2 -2
  197. package/dist/_pkgs/affine-block-embed-doc/view.js +4 -4
  198. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/frame-dense-menu.js +1 -1
  199. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/frame-menu.d.ts +1 -1
  200. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/frame-menu.js +1 -1
  201. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/frame-tool-button.d.ts +1 -1
  202. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/frame-tool-button.js +1 -1
  203. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/index.d.ts +5 -5
  204. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/index.js +5 -5
  205. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/presentation-toolbar.d.ts +1 -1
  206. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/presentation-toolbar.js +2 -2
  207. package/dist/_pkgs/affine-block-frame/edgeless-toolbar/quick-tool.js +1 -1
  208. package/dist/_pkgs/affine-block-frame/effects.d.ts +8 -8
  209. package/dist/_pkgs/affine-block-frame/effects.js +8 -8
  210. package/dist/_pkgs/affine-block-frame/frame-block.js +1 -1
  211. package/dist/_pkgs/affine-block-frame/frame-highlight-manager.d.ts +1 -1
  212. package/dist/_pkgs/affine-block-frame/frame-highlight-manager.js +1 -1
  213. package/dist/_pkgs/affine-block-frame/frame-manager.d.ts +13 -0
  214. package/dist/_pkgs/affine-block-frame/frame-manager.js +32 -2
  215. package/dist/_pkgs/affine-block-frame/frame-spec.js +2 -2
  216. package/dist/_pkgs/affine-block-frame/frame-tool.d.ts +2 -2
  217. package/dist/_pkgs/affine-block-frame/frame-tool.js +1 -1
  218. package/dist/_pkgs/affine-block-frame/frame-toolbar.js +1 -1
  219. package/dist/_pkgs/affine-block-frame/index.d.ts +9 -9
  220. package/dist/_pkgs/affine-block-frame/index.js +9 -9
  221. package/dist/_pkgs/affine-block-frame/present/frame-order-menu.js +1 -1
  222. package/dist/_pkgs/affine-block-frame/present/navigator-bg-widget.js +1 -1
  223. package/dist/_pkgs/affine-block-frame/present/present-button.d.ts +1 -1
  224. package/dist/_pkgs/affine-block-frame/present/present-button.js +1 -1
  225. package/dist/_pkgs/affine-block-frame/present-tool.d.ts +1 -1
  226. package/dist/_pkgs/affine-block-frame/view.js +10 -10
  227. package/dist/_pkgs/affine-block-image/adapters/html.js +1 -1
  228. package/dist/_pkgs/affine-block-image/adapters/markdown.js +1 -1
  229. package/dist/_pkgs/affine-block-image/adapters/notion-html.js +1 -1
  230. package/dist/_pkgs/affine-block-image/commands/insert-images.js +1 -1
  231. package/dist/_pkgs/affine-block-image/components/page-image-block.d.ts +1 -1
  232. package/dist/_pkgs/affine-block-image/components/page-image-block.js +3 -3
  233. package/dist/_pkgs/affine-block-image/configs/slash-menu.js +2 -2
  234. package/dist/_pkgs/affine-block-image/configs/toolbar.js +3 -3
  235. package/dist/_pkgs/affine-block-image/image-block.js +1 -1
  236. package/dist/_pkgs/affine-block-image/image-edgeless-block.js +1 -1
  237. package/dist/_pkgs/affine-block-image/image-spec.js +4 -4
  238. package/dist/_pkgs/affine-block-image/index.d.ts +10 -10
  239. package/dist/_pkgs/affine-block-image/index.js +10 -10
  240. package/dist/_pkgs/affine-block-image/store.js +1 -1
  241. package/dist/_pkgs/affine-block-image/turbo/image-layout-handler.d.ts +1 -1
  242. package/dist/_pkgs/affine-block-image/utils.d.ts +2 -2
  243. package/dist/_pkgs/affine-block-image/utils.js +1 -1
  244. package/dist/_pkgs/affine-block-image/view.js +4 -4
  245. package/dist/_pkgs/affine-block-latex/configs/slash-menu.js +2 -2
  246. package/dist/_pkgs/affine-block-latex/effects.js +1 -1
  247. package/dist/_pkgs/affine-block-latex/index.d.ts +3 -3
  248. package/dist/_pkgs/affine-block-latex/index.js +3 -3
  249. package/dist/_pkgs/affine-block-latex/store.js +2 -2
  250. package/dist/_pkgs/affine-block-latex/styles.js +6 -1
  251. package/dist/_pkgs/affine-block-latex/view.js +2 -2
  252. package/dist/_pkgs/affine-block-list/index.d.ts +4 -4
  253. package/dist/_pkgs/affine-block-list/index.js +4 -4
  254. package/dist/_pkgs/affine-block-list/store.js +1 -1
  255. package/dist/_pkgs/affine-block-list/turbo/list-layout-handler.d.ts +1 -1
  256. package/dist/_pkgs/affine-block-note/adapters/index.d.ts +3 -3
  257. package/dist/_pkgs/affine-block-note/adapters/index.js +6 -6
  258. package/dist/_pkgs/affine-block-note/commands/dedent-block-to-root.js +1 -1
  259. package/dist/_pkgs/affine-block-note/commands/dedent-blocks-to-root.js +1 -1
  260. package/dist/_pkgs/affine-block-note/commands/dedent-blocks.js +1 -1
  261. package/dist/_pkgs/affine-block-note/commands/indent-blocks.js +1 -1
  262. package/dist/_pkgs/affine-block-note/components/edgeless-note-background.css.js +1 -1
  263. package/dist/_pkgs/affine-block-note/components/edgeless-note-background.js +2 -2
  264. package/dist/_pkgs/affine-block-note/components/edgeless-note-mask.js +1 -1
  265. package/dist/_pkgs/affine-block-note/components/edgeless-note-shadow-menu.js +1 -1
  266. package/dist/_pkgs/affine-block-note/components/edgeless-page-block-title.js +2 -2
  267. package/dist/_pkgs/affine-block-note/configs/slash-menu.js +2 -2
  268. package/dist/_pkgs/affine-block-note/configs/toolbar.js +2 -2
  269. package/dist/_pkgs/affine-block-note/effects.js +9 -9
  270. package/dist/_pkgs/affine-block-note/index.d.ts +9 -9
  271. package/dist/_pkgs/affine-block-note/index.js +9 -9
  272. package/dist/_pkgs/affine-block-note/note-edgeless-block.d.ts +10 -1
  273. package/dist/_pkgs/affine-block-note/note-edgeless-block.js +35 -7
  274. package/dist/_pkgs/affine-block-note/note-keymap.js +3 -3
  275. package/dist/_pkgs/affine-block-note/store.js +1 -1
  276. package/dist/_pkgs/affine-block-note/turbo/note-layout-handler.d.ts +1 -1
  277. package/dist/_pkgs/affine-block-note/view.js +6 -6
  278. package/dist/_pkgs/affine-block-paragraph/index.d.ts +3 -3
  279. package/dist/_pkgs/affine-block-paragraph/index.js +3 -3
  280. package/dist/_pkgs/affine-block-paragraph/store.js +1 -1
  281. package/dist/_pkgs/affine-block-paragraph/turbo/paragraph-layout-handler.d.ts +1 -1
  282. package/dist/_pkgs/affine-block-paragraph/view.js +1 -1
  283. package/dist/_pkgs/affine-block-root/clipboard/page-clipboard.d.ts +1 -1
  284. package/dist/_pkgs/affine-block-root/clipboard/page-clipboard.js +1 -1
  285. package/dist/_pkgs/affine-block-root/commands/pivot-commands.d.ts +37 -0
  286. package/dist/_pkgs/affine-block-root/commands/pivot-commands.js +168 -0
  287. package/dist/_pkgs/affine-block-root/commands/reading-commands.d.ts +23 -0
  288. package/dist/_pkgs/affine-block-root/commands/reading-commands.js +107 -0
  289. package/dist/_pkgs/affine-block-root/commands/tag-commands.d.ts +47 -0
  290. package/dist/_pkgs/affine-block-root/commands/tag-commands.js +159 -0
  291. package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +1 -0
  292. package/dist/_pkgs/affine-block-root/edgeless/clipboard/command.js +2 -2
  293. package/dist/_pkgs/affine-block-root/edgeless/clipboard/utils.js +2 -2
  294. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/index.js +1 -1
  295. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.js +2 -2
  296. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +16 -9
  297. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/render-linked-doc.js +1 -1
  298. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/utils.d.ts +1 -1
  299. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/utils.js +1 -1
  300. package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.js +26 -1
  301. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.js +11 -2
  302. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-service.js +15 -1
  303. package/dist/_pkgs/affine-block-root/edgeless/index.d.ts +4 -3
  304. package/dist/_pkgs/affine-block-root/edgeless/index.js +4 -3
  305. package/dist/_pkgs/affine-block-root/edgeless/reserved-keys.d.ts +21 -0
  306. package/dist/_pkgs/affine-block-root/edgeless/reserved-keys.js +34 -0
  307. package/dist/_pkgs/affine-block-root/edgeless/utils/clipboard-utils.js +1 -1
  308. package/dist/_pkgs/affine-block-root/edgeless/utils/clone-utils.d.ts +7 -1
  309. package/dist/_pkgs/affine-block-root/edgeless/utils/clone-utils.js +11 -2
  310. package/dist/_pkgs/affine-block-root/edgeless/utils/query.js +1 -1
  311. package/dist/_pkgs/affine-block-root/index.d.ts +5 -2
  312. package/dist/_pkgs/affine-block-root/index.js +5 -2
  313. package/dist/_pkgs/affine-block-root/keyboard/commands.d.ts +2 -0
  314. package/dist/_pkgs/affine-block-root/keyboard/commands.js +147 -0
  315. package/dist/_pkgs/affine-block-root/preview/edgeless-root-preview-block.js +1 -1
  316. package/dist/_pkgs/affine-block-root/store.js +1 -1
  317. package/dist/_pkgs/affine-block-root/view.js +21 -12
  318. package/dist/_pkgs/affine-block-surface/commands/auto-align.js +1 -1
  319. package/dist/_pkgs/affine-block-surface/commands/reassociate-connectors.js +0 -1
  320. package/dist/_pkgs/affine-block-surface/effects.js +6 -0
  321. package/dist/_pkgs/affine-block-surface/element-model/index.d.ts +5 -2
  322. package/dist/_pkgs/affine-block-surface/element-model/index.js +4 -2
  323. package/dist/_pkgs/affine-block-surface/extensions/audit.d.ts +107 -0
  324. package/dist/_pkgs/affine-block-surface/extensions/audit.js +472 -0
  325. package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.d.ts +1 -1
  326. package/dist/_pkgs/affine-block-surface/extensions/clipboard-config.js +2 -2
  327. package/dist/_pkgs/affine-block-surface/extensions/crud-extension.d.ts +14 -0
  328. package/dist/_pkgs/affine-block-surface/extensions/crud-extension.js +29 -2
  329. package/dist/_pkgs/affine-block-surface/extensions/edit-props-middleware-builder.js +1 -1
  330. package/dist/_pkgs/affine-block-surface/extensions/editor-anchored-panel.d.ts +133 -0
  331. package/dist/_pkgs/affine-block-surface/extensions/editor-anchored-panel.js +326 -0
  332. package/dist/_pkgs/affine-block-surface/extensions/element-renderer.d.ts +1 -1
  333. package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +22 -9
  334. package/dist/_pkgs/affine-block-surface/extensions/index.js +22 -9
  335. package/dist/_pkgs/affine-block-surface/extensions/map-quality-widget.d.ts +146 -0
  336. package/dist/_pkgs/affine-block-surface/extensions/map-quality-widget.js +528 -0
  337. package/dist/_pkgs/affine-block-surface/extensions/map-quality.d.ts +109 -0
  338. package/dist/_pkgs/affine-block-surface/extensions/map-quality.js +84 -0
  339. package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.d.ts +15 -0
  340. package/dist/_pkgs/affine-block-surface/extensions/reading-toolbar.js +158 -0
  341. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.d.ts +145 -0
  342. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.js +590 -0
  343. package/dist/_pkgs/affine-block-surface/extensions/reading.d.ts +494 -0
  344. package/dist/_pkgs/affine-block-surface/extensions/reading.js +767 -0
  345. package/dist/_pkgs/affine-block-surface/extensions/spotlight.d.ts +45 -0
  346. package/dist/_pkgs/affine-block-surface/extensions/spotlight.js +177 -0
  347. package/dist/_pkgs/affine-block-surface/extensions/surface-middleware.d.ts +1 -1
  348. package/dist/_pkgs/affine-block-surface/extensions/tags-toolbar.d.ts +10 -0
  349. package/dist/_pkgs/affine-block-surface/extensions/tags-toolbar.js +295 -0
  350. package/dist/_pkgs/affine-block-surface/extensions/validation-toolbar.d.ts +27 -0
  351. package/dist/_pkgs/affine-block-surface/extensions/validation-toolbar.js +361 -0
  352. package/dist/_pkgs/affine-block-surface/extensions/validation.d.ts +1340 -0
  353. package/dist/_pkgs/affine-block-surface/extensions/validation.js +3009 -0
  354. package/dist/_pkgs/affine-block-surface/extensions/violation-detail-widget.d.ts +286 -0
  355. package/dist/_pkgs/affine-block-surface/extensions/violation-detail-widget.js +907 -0
  356. package/dist/_pkgs/affine-block-surface/extensions/violation-timeline.d.ts +80 -0
  357. package/dist/_pkgs/affine-block-surface/extensions/violation-timeline.js +123 -0
  358. package/dist/_pkgs/affine-block-surface/framework-background/def.d.ts +321 -0
  359. package/dist/_pkgs/affine-block-surface/framework-background/def.js +81 -0
  360. package/dist/_pkgs/affine-block-surface/framework-background/facts.d.ts +188 -0
  361. package/dist/_pkgs/affine-block-surface/framework-background/facts.js +284 -0
  362. package/dist/_pkgs/affine-block-surface/framework-background/index.d.ts +5 -0
  363. package/dist/_pkgs/affine-block-surface/framework-background/index.js +5 -0
  364. package/dist/_pkgs/affine-block-surface/framework-background/interaction.d.ts +12 -0
  365. package/dist/_pkgs/affine-block-surface/framework-background/interaction.js +24 -0
  366. package/dist/_pkgs/affine-block-surface/framework-background/labels.d.ts +59 -0
  367. package/dist/_pkgs/affine-block-surface/framework-background/labels.js +117 -0
  368. package/dist/_pkgs/affine-block-surface/framework-background/renderer.d.ts +8 -0
  369. package/dist/_pkgs/affine-block-surface/framework-background/renderer.js +238 -0
  370. package/dist/_pkgs/affine-block-surface/index.d.ts +6 -5
  371. package/dist/_pkgs/affine-block-surface/index.js +5 -4
  372. package/dist/_pkgs/affine-block-surface/renderer/canvas-renderer.d.ts +69 -3
  373. package/dist/_pkgs/affine-block-surface/renderer/canvas-renderer.js +334 -45
  374. package/dist/_pkgs/affine-block-surface/renderer/dom-renderer.js +18 -2
  375. package/dist/_pkgs/affine-block-surface/renderer/tool-overlay.d.ts +2 -2
  376. package/dist/_pkgs/affine-block-surface/renderer/tool-overlay.js +1 -1
  377. package/dist/_pkgs/affine-block-surface/store.js +2 -2
  378. package/dist/_pkgs/affine-block-surface/tool/default-tool.js +8 -0
  379. package/dist/_pkgs/affine-block-surface/utils/font.js +9 -3
  380. package/dist/_pkgs/affine-block-surface/utils/get-bg-grip-gap.js +1 -1
  381. package/dist/_pkgs/affine-block-surface/utils/get-surface-block.d.ts +2 -2
  382. package/dist/_pkgs/affine-block-surface/utils/index.d.ts +3 -3
  383. package/dist/_pkgs/affine-block-surface/utils/index.js +3 -3
  384. package/dist/_pkgs/affine-block-surface/view.d.ts +17 -0
  385. package/dist/_pkgs/affine-block-surface/view.js +60 -5
  386. package/dist/_pkgs/affine-block-surface-ref/components/index.d.ts +2 -2
  387. package/dist/_pkgs/affine-block-surface-ref/components/index.js +2 -2
  388. package/dist/_pkgs/affine-block-surface-ref/components/placeholder.js +2 -2
  389. package/dist/_pkgs/affine-block-surface-ref/configs/slash-menu.js +2 -2
  390. package/dist/_pkgs/affine-block-surface-ref/configs/toolbar.js +1 -1
  391. package/dist/_pkgs/affine-block-surface-ref/effects.js +5 -5
  392. package/dist/_pkgs/affine-block-surface-ref/view.js +3 -3
  393. package/dist/_pkgs/affine-block-table/adapters/html.js +2 -2
  394. package/dist/_pkgs/affine-block-table/adapters/index.d.ts +5 -5
  395. package/dist/_pkgs/affine-block-table/adapters/index.js +5 -5
  396. package/dist/_pkgs/affine-block-table/adapters/markdown.js +1 -1
  397. package/dist/_pkgs/affine-block-table/add-button.d.ts +1 -1
  398. package/dist/_pkgs/affine-block-table/add-button.js +2 -2
  399. package/dist/_pkgs/affine-block-table/commands.js +1 -1
  400. package/dist/_pkgs/affine-block-table/configs/slash-menu.js +2 -2
  401. package/dist/_pkgs/affine-block-table/effects.js +4 -4
  402. package/dist/_pkgs/affine-block-table/index.d.ts +1 -1
  403. package/dist/_pkgs/affine-block-table/index.js +1 -1
  404. package/dist/_pkgs/affine-block-table/selection-controller.d.ts +8 -2
  405. package/dist/_pkgs/affine-block-table/selection-controller.js +27 -4
  406. package/dist/_pkgs/affine-block-table/selection-layer.d.ts +1 -1
  407. package/dist/_pkgs/affine-block-table/store.js +2 -2
  408. package/dist/_pkgs/affine-block-table/table-block.d.ts +2 -2
  409. package/dist/_pkgs/affine-block-table/table-block.js +11 -5
  410. package/dist/_pkgs/affine-block-table/table-cell.d.ts +2 -2
  411. package/dist/_pkgs/affine-block-table/table-cell.js +9 -10
  412. package/dist/_pkgs/affine-block-table/table-data-manager.d.ts +1 -1
  413. package/dist/_pkgs/affine-block-table/table-keymap.d.ts +18 -0
  414. package/dist/_pkgs/affine-block-table/table-keymap.js +41 -0
  415. package/dist/_pkgs/affine-block-table/utils.d.ts +7 -0
  416. package/dist/_pkgs/affine-block-table/utils.js +12 -0
  417. package/dist/_pkgs/affine-block-table/view.js +4 -2
  418. package/dist/_pkgs/affine-components/caption/captioned-block-component.js +2 -1
  419. package/dist/_pkgs/affine-components/card-style-dropdown-menu/dropdown-menu.js +1 -1
  420. package/dist/_pkgs/affine-components/card-style-dropdown-menu/index.d.ts +1 -1
  421. package/dist/_pkgs/affine-components/card-style-dropdown-menu/index.js +2 -2
  422. package/dist/_pkgs/affine-components/citation/index.d.ts +1 -1
  423. package/dist/_pkgs/affine-components/citation/index.js +2 -2
  424. package/dist/_pkgs/affine-components/color-picker/button.d.ts +2 -2
  425. package/dist/_pkgs/affine-components/color-picker/color-panel.js +2 -2
  426. package/dist/_pkgs/affine-components/color-picker/color-picker.js +1 -1
  427. package/dist/_pkgs/affine-components/color-picker/index.d.ts +9 -9
  428. package/dist/_pkgs/affine-components/color-picker/index.js +9 -9
  429. package/dist/_pkgs/affine-components/color-picker/utils.js +1 -1
  430. package/dist/_pkgs/affine-components/context-menu/button.d.ts +7 -0
  431. package/dist/_pkgs/affine-components/context-menu/button.js +7 -2
  432. package/dist/_pkgs/affine-components/context-menu/index.d.ts +10 -10
  433. package/dist/_pkgs/affine-components/context-menu/index.js +15 -15
  434. package/dist/_pkgs/affine-components/context-menu/input.d.ts +4 -0
  435. package/dist/_pkgs/affine-components/context-menu/input.js +12 -0
  436. package/dist/_pkgs/affine-components/context-menu/menu-all.d.ts +12 -10
  437. package/dist/_pkgs/affine-components/context-menu/menu-all.js +5 -5
  438. package/dist/_pkgs/affine-components/context-menu/menu-renderer.js +8 -1
  439. package/dist/_pkgs/affine-components/context-menu/sub-menu.js +3 -0
  440. package/dist/_pkgs/affine-components/drop-indicator/file-drop-manager.d.ts +1 -1
  441. package/dist/_pkgs/affine-components/drop-indicator/file-drop-manager.js +2 -2
  442. package/dist/_pkgs/affine-components/drop-indicator/index.d.ts +2 -2
  443. package/dist/_pkgs/affine-components/drop-indicator/index.js +2 -2
  444. package/dist/_pkgs/affine-components/edgeless-line-styles-panel/index.d.ts +1 -1
  445. package/dist/_pkgs/affine-components/edgeless-line-styles-panel/index.js +2 -2
  446. package/dist/_pkgs/affine-components/edgeless-line-width-panel/index.d.ts +1 -1
  447. package/dist/_pkgs/affine-components/edgeless-line-width-panel/index.js +2 -2
  448. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/color-picker.d.ts +1 -1
  449. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/color-picker.js +1 -1
  450. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/index.d.ts +1 -1
  451. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/index.js +2 -2
  452. package/dist/_pkgs/affine-components/embed-card-modal/embed-card-create-modal.js +1 -1
  453. package/dist/_pkgs/affine-components/embed-card-modal/embed-card-edit-modal.js +1 -1
  454. package/dist/_pkgs/affine-components/embed-card-modal/index.d.ts +2 -2
  455. package/dist/_pkgs/affine-components/embed-card-modal/index.js +4 -4
  456. package/dist/_pkgs/affine-components/filterable-list/index.d.ts +1 -1
  457. package/dist/_pkgs/affine-components/filterable-list/index.js +1 -1
  458. package/dist/_pkgs/affine-components/highlight-dropdown-menu/dropdown-menu.js +1 -1
  459. package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
  460. package/dist/_pkgs/affine-components/highlight-dropdown-menu/index.d.ts +3 -3
  461. package/dist/_pkgs/affine-components/highlight-dropdown-menu/index.js +6 -6
  462. package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
  463. package/dist/_pkgs/affine-components/icon-picker/affine-icon-picker-panel.d.ts +28 -0
  464. package/dist/_pkgs/affine-components/icon-picker/affine-icon-picker-panel.js +231 -0
  465. package/dist/_pkgs/affine-components/icon-picker/emoji-data.d.ts +40 -0
  466. package/dist/_pkgs/affine-components/icon-picker/emoji-data.js +99 -0
  467. package/dist/_pkgs/affine-components/icon-picker/emoji-picker-panel.d.ts +32 -0
  468. package/dist/_pkgs/affine-components/icon-picker/emoji-picker-panel.js +276 -0
  469. package/dist/_pkgs/affine-components/icon-picker/icon-data.d.ts +34 -0
  470. package/dist/_pkgs/affine-components/icon-picker/icon-data.js +83 -0
  471. package/dist/_pkgs/affine-components/icon-picker/icon-picker.d.ts +39 -0
  472. package/dist/_pkgs/affine-components/icon-picker/icon-picker.js +233 -0
  473. package/dist/_pkgs/affine-components/icon-picker/index.d.ts +8 -0
  474. package/dist/_pkgs/affine-components/icon-picker/index.js +15 -0
  475. package/dist/_pkgs/affine-components/icon-picker/recent-store.d.ts +8 -0
  476. package/dist/_pkgs/affine-components/icon-picker/recent-store.js +41 -0
  477. package/dist/_pkgs/affine-components/icon-picker/styles.d.ts +5 -0
  478. package/dist/_pkgs/affine-components/icon-picker/styles.js +166 -0
  479. package/dist/_pkgs/affine-components/icon-picker/types.d.ts +22 -0
  480. package/dist/_pkgs/affine-components/icon-picker/types.js +10 -0
  481. package/dist/_pkgs/affine-components/icons/index.d.ts +1 -1
  482. package/dist/_pkgs/affine-components/icons/index.js +1 -1
  483. package/dist/_pkgs/affine-components/icons/text.js +426 -426
  484. package/dist/_pkgs/affine-components/link-preview/index.d.ts +1 -1
  485. package/dist/_pkgs/affine-components/link-preview/index.js +2 -2
  486. package/dist/_pkgs/affine-components/linked-doc-title/index.d.ts +1 -1
  487. package/dist/_pkgs/affine-components/linked-doc-title/index.js +2 -2
  488. package/dist/_pkgs/affine-components/open-doc-dropdown-menu/dropdown-menu.d.ts +1 -0
  489. package/dist/_pkgs/affine-components/open-doc-dropdown-menu/dropdown-menu.js +1 -1
  490. package/dist/_pkgs/affine-components/open-doc-dropdown-menu/index.d.ts +1 -1
  491. package/dist/_pkgs/affine-components/open-doc-dropdown-menu/index.js +2 -2
  492. package/dist/_pkgs/affine-components/portal/portal.d.ts +1 -1
  493. package/dist/_pkgs/affine-components/resource/index.d.ts +2 -2
  494. package/dist/_pkgs/affine-components/resource/index.js +3 -3
  495. package/dist/_pkgs/affine-components/size-dropdown-menu/dropdown-menu.d.ts +1 -1
  496. package/dist/_pkgs/affine-components/size-dropdown-menu/dropdown-menu.js +2 -2
  497. package/dist/_pkgs/affine-components/size-dropdown-menu/index.d.ts +1 -1
  498. package/dist/_pkgs/affine-components/size-dropdown-menu/index.js +2 -2
  499. package/dist/_pkgs/affine-components/slider/index.d.ts +1 -1
  500. package/dist/_pkgs/affine-components/slider/index.js +2 -2
  501. package/dist/_pkgs/affine-components/slider/slider.d.ts +1 -1
  502. package/dist/_pkgs/affine-components/slider/slider.js +2 -2
  503. package/dist/_pkgs/affine-components/slider/utils.d.ts +1 -1
  504. package/dist/_pkgs/affine-components/toolbar/config.d.ts +1 -1
  505. package/dist/_pkgs/affine-components/toolbar/menu-button.js +1 -0
  506. package/dist/_pkgs/affine-components/toolbar/toolbar.js +11 -10
  507. package/dist/_pkgs/affine-components/view-dropdown-menu/dropdown-menu.js +1 -1
  508. package/dist/_pkgs/affine-components/view-dropdown-menu/index.d.ts +1 -1
  509. package/dist/_pkgs/affine-components/view-dropdown-menu/index.js +2 -2
  510. package/dist/_pkgs/affine-ext-loader/index.d.ts +4 -4
  511. package/dist/_pkgs/affine-ext-loader/index.js +4 -4
  512. package/dist/_pkgs/affine-ext-loader/manager.d.ts +1 -1
  513. package/dist/_pkgs/affine-ext-loader/store-manager.d.ts +1 -1
  514. package/dist/_pkgs/affine-ext-loader/store-manager.js +1 -1
  515. package/dist/_pkgs/affine-ext-loader/store-provider.d.ts +1 -1
  516. package/dist/_pkgs/affine-ext-loader/store-provider.js +1 -1
  517. package/dist/_pkgs/affine-ext-loader/view-manager.d.ts +2 -2
  518. package/dist/_pkgs/affine-ext-loader/view-manager.js +1 -1
  519. package/dist/_pkgs/affine-ext-loader/view-provider.d.ts +1 -1
  520. package/dist/_pkgs/affine-ext-loader/view-provider.js +1 -1
  521. package/dist/_pkgs/affine-foundation/view.js +13 -4
  522. package/dist/_pkgs/affine-fragment-adapter-panel/adapter-panel.d.ts +1 -1
  523. package/dist/_pkgs/affine-fragment-adapter-panel/adapter-panel.js +1 -1
  524. package/dist/_pkgs/affine-fragment-adapter-panel/body/adapter-panel-body.d.ts +1 -1
  525. package/dist/_pkgs/affine-fragment-adapter-panel/body/adapter-panel-body.js +1 -1
  526. package/dist/_pkgs/affine-fragment-adapter-panel/effects.js +4 -4
  527. package/dist/_pkgs/affine-fragment-adapter-panel/header/adapter-menu.d.ts +1 -1
  528. package/dist/_pkgs/affine-fragment-adapter-panel/header/adapter-menu.js +1 -1
  529. package/dist/_pkgs/affine-fragment-adapter-panel/header/adapter-panel-header.d.ts +1 -1
  530. package/dist/_pkgs/affine-fragment-adapter-panel/header/adapter-panel-header.js +1 -1
  531. package/dist/_pkgs/affine-fragment-adapter-panel/view.js +1 -1
  532. package/dist/_pkgs/affine-fragment-doc-title/effects.d.ts +1 -1
  533. package/dist/_pkgs/affine-fragment-doc-title/effects.js +1 -1
  534. package/dist/_pkgs/affine-fragment-doc-title/index.d.ts +2 -2
  535. package/dist/_pkgs/affine-fragment-doc-title/index.js +2 -2
  536. package/dist/_pkgs/affine-fragment-doc-title/utils.d.ts +1 -1
  537. package/dist/_pkgs/affine-fragment-doc-title/view.js +1 -1
  538. package/dist/_pkgs/affine-fragment-frame-panel/card/frame-preview.js +1 -1
  539. package/dist/_pkgs/affine-fragment-frame-panel/effects.js +8 -8
  540. package/dist/_pkgs/affine-fragment-frame-panel/index.d.ts +1 -1
  541. package/dist/_pkgs/affine-fragment-frame-panel/index.js +1 -1
  542. package/dist/_pkgs/affine-fragment-frame-panel/view.js +1 -1
  543. package/dist/_pkgs/affine-fragment-outline/body/outline-notice.js +3 -3
  544. package/dist/_pkgs/affine-fragment-outline/body/outline-panel-body.js +4 -4
  545. package/dist/_pkgs/affine-fragment-outline/card/outline-card.js +2 -2
  546. package/dist/_pkgs/affine-fragment-outline/card/outline-preview.js +1 -1
  547. package/dist/_pkgs/affine-fragment-outline/effects.js +9 -9
  548. package/dist/_pkgs/affine-fragment-outline/header/outline-panel-header.js +2 -2
  549. package/dist/_pkgs/affine-fragment-outline/header/outline-setting-menu.js +2 -2
  550. package/dist/_pkgs/affine-fragment-outline/outline-panel.js +1 -1
  551. package/dist/_pkgs/affine-fragment-outline/view.js +1 -1
  552. package/dist/_pkgs/affine-gfx-brush/adapter/index.d.ts +2 -2
  553. package/dist/_pkgs/affine-gfx-brush/adapter/index.js +2 -2
  554. package/dist/_pkgs/affine-gfx-brush/effects.d.ts +3 -3
  555. package/dist/_pkgs/affine-gfx-brush/effects.js +3 -3
  556. package/dist/_pkgs/affine-gfx-brush/index.d.ts +8 -7
  557. package/dist/_pkgs/affine-gfx-brush/index.js +8 -7
  558. package/dist/_pkgs/affine-gfx-brush/renderer/dom/brush.d.ts +1 -0
  559. package/dist/_pkgs/affine-gfx-brush/renderer/dom/brush.js +11 -0
  560. package/dist/_pkgs/affine-gfx-brush/renderer/dom/highlighter.d.ts +1 -0
  561. package/dist/_pkgs/affine-gfx-brush/renderer/dom/highlighter.js +11 -0
  562. package/dist/_pkgs/affine-gfx-brush/renderer/dom/index.d.ts +2 -0
  563. package/dist/_pkgs/affine-gfx-brush/renderer/dom/index.js +2 -0
  564. package/dist/_pkgs/affine-gfx-brush/renderer/dom/shared.d.ts +14 -0
  565. package/dist/_pkgs/affine-gfx-brush/renderer/dom/shared.js +54 -0
  566. package/dist/_pkgs/affine-gfx-brush/renderer/index.d.ts +1 -0
  567. package/dist/_pkgs/affine-gfx-brush/renderer/index.js +4 -0
  568. package/dist/_pkgs/affine-gfx-brush/store.js +1 -1
  569. package/dist/_pkgs/affine-gfx-brush/toolbar/components/eraser/eraser-tool-button.d.ts +1 -1
  570. package/dist/_pkgs/affine-gfx-brush/toolbar/components/eraser/eraser-tool-button.js +1 -1
  571. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/consts.d.ts +1 -1
  572. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/consts.js +1 -1
  573. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/pen-menu.d.ts +3 -3
  574. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/pen-menu.js +3 -3
  575. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/pen-tool-button.d.ts +2 -2
  576. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/pen-tool-button.js +3 -3
  577. package/dist/_pkgs/affine-gfx-brush/toolbar/configs/index.d.ts +2 -2
  578. package/dist/_pkgs/affine-gfx-brush/toolbar/configs/index.js +2 -2
  579. package/dist/_pkgs/affine-gfx-brush/view.d.ts +14 -0
  580. package/dist/_pkgs/affine-gfx-brush/view.js +36 -10
  581. package/dist/_pkgs/affine-gfx-connector/adapter/index.d.ts +2 -2
  582. package/dist/_pkgs/affine-gfx-connector/adapter/index.js +2 -2
  583. package/dist/_pkgs/affine-gfx-connector/adapter/markdown/index.js +1 -1
  584. package/dist/_pkgs/affine-gfx-connector/adapter/plain-text/index.js +1 -1
  585. package/dist/_pkgs/affine-gfx-connector/components/connector-handle.d.ts +1 -1
  586. package/dist/_pkgs/affine-gfx-connector/connector-manager.js +33 -7
  587. package/dist/_pkgs/affine-gfx-connector/connector-tool.d.ts +7 -0
  588. package/dist/_pkgs/affine-gfx-connector/connector-tool.js +3 -2
  589. package/dist/_pkgs/affine-gfx-connector/connector-watcher.js +1 -1
  590. package/dist/_pkgs/affine-gfx-connector/direction/direction-reveal.d.ts +123 -0
  591. package/dist/_pkgs/affine-gfx-connector/direction/direction-reveal.js +281 -0
  592. package/dist/_pkgs/affine-gfx-connector/direction/direction-widget.d.ts +65 -0
  593. package/dist/_pkgs/affine-gfx-connector/direction/direction-widget.js +395 -0
  594. package/dist/_pkgs/affine-gfx-connector/direction/index.d.ts +4 -0
  595. package/dist/_pkgs/affine-gfx-connector/direction/index.js +4 -0
  596. package/dist/_pkgs/affine-gfx-connector/direction/invert-direction.d.ts +72 -0
  597. package/dist/_pkgs/affine-gfx-connector/direction/invert-direction.js +187 -0
  598. package/dist/_pkgs/affine-gfx-connector/direction/typed-edge.d.ts +69 -0
  599. package/dist/_pkgs/affine-gfx-connector/direction/typed-edge.js +66 -0
  600. package/dist/_pkgs/affine-gfx-connector/effects.d.ts +6 -4
  601. package/dist/_pkgs/affine-gfx-connector/effects.js +6 -4
  602. package/dist/_pkgs/affine-gfx-connector/element-renderer/connector-dom/index.js +78 -41
  603. package/dist/_pkgs/affine-gfx-connector/element-transform/index.d.ts +1 -1
  604. package/dist/_pkgs/affine-gfx-connector/element-transform/index.js +1 -1
  605. package/dist/_pkgs/affine-gfx-connector/index.d.ts +11 -10
  606. package/dist/_pkgs/affine-gfx-connector/index.js +11 -10
  607. package/dist/_pkgs/affine-gfx-connector/store.js +2 -2
  608. package/dist/_pkgs/affine-gfx-connector/text/text.js +1 -1
  609. package/dist/_pkgs/affine-gfx-connector/toolbar/config.d.ts +50 -0
  610. package/dist/_pkgs/affine-gfx-connector/toolbar/config.js +69 -3
  611. package/dist/_pkgs/affine-gfx-connector/toolbar/connector-dense-menu.js +1 -1
  612. package/dist/_pkgs/affine-gfx-connector/toolbar/connector-menu.d.ts +1 -1
  613. package/dist/_pkgs/affine-gfx-connector/toolbar/connector-menu.js +1 -1
  614. package/dist/_pkgs/affine-gfx-connector/toolbar/connector-tool-button.d.ts +1 -1
  615. package/dist/_pkgs/affine-gfx-connector/toolbar/connector-tool-button.js +1 -1
  616. package/dist/_pkgs/affine-gfx-connector/view/index.d.ts +1 -1
  617. package/dist/_pkgs/affine-gfx-connector/view/index.js +1 -1
  618. package/dist/_pkgs/affine-gfx-connector/view/view.js +1 -1
  619. package/dist/_pkgs/affine-gfx-connector/view.js +25 -9
  620. package/dist/_pkgs/affine-gfx-group/adapter/index.d.ts +2 -2
  621. package/dist/_pkgs/affine-gfx-group/adapter/index.js +2 -2
  622. package/dist/_pkgs/affine-gfx-group/adapter/markdown/index.js +1 -1
  623. package/dist/_pkgs/affine-gfx-group/adapter/plain-text/index.js +1 -1
  624. package/dist/_pkgs/affine-gfx-group/command/group-api.js +150 -62
  625. package/dist/_pkgs/affine-gfx-group/command/index.d.ts +1 -1
  626. package/dist/_pkgs/affine-gfx-group/command/index.js +1 -1
  627. package/dist/_pkgs/affine-gfx-group/effects.d.ts +1 -1
  628. package/dist/_pkgs/affine-gfx-group/effects.js +1 -1
  629. package/dist/_pkgs/affine-gfx-group/element-view.js +1 -1
  630. package/dist/_pkgs/affine-gfx-group/index.d.ts +6 -6
  631. package/dist/_pkgs/affine-gfx-group/index.js +6 -6
  632. package/dist/_pkgs/affine-gfx-group/store.js +2 -2
  633. package/dist/_pkgs/affine-gfx-group/text/edgeless-group-title-editor.js +5 -2
  634. package/dist/_pkgs/affine-gfx-group/text/text.js +1 -1
  635. package/dist/_pkgs/affine-gfx-group/toolbar/config.js +2 -2
  636. package/dist/_pkgs/affine-gfx-group/view.js +5 -5
  637. package/dist/_pkgs/affine-gfx-link/effects.js +1 -1
  638. package/dist/_pkgs/affine-gfx-link/undo-tool.js +1 -1
  639. package/dist/_pkgs/affine-gfx-link/view.js +2 -2
  640. package/dist/_pkgs/affine-gfx-mindmap/adapter/index.d.ts +2 -2
  641. package/dist/_pkgs/affine-gfx-mindmap/adapter/index.js +2 -2
  642. package/dist/_pkgs/affine-gfx-mindmap/adapter/plain-text/index.js +1 -1
  643. package/dist/_pkgs/affine-gfx-mindmap/effects.d.ts +3 -3
  644. package/dist/_pkgs/affine-gfx-mindmap/effects.js +3 -3
  645. package/dist/_pkgs/affine-gfx-mindmap/index.d.ts +9 -9
  646. package/dist/_pkgs/affine-gfx-mindmap/index.js +9 -9
  647. package/dist/_pkgs/affine-gfx-mindmap/indicator-overlay/index.js +2 -2
  648. package/dist/_pkgs/affine-gfx-mindmap/interactivity/drag-utils.js +2 -2
  649. package/dist/_pkgs/affine-gfx-mindmap/interactivity/index.d.ts +1 -1
  650. package/dist/_pkgs/affine-gfx-mindmap/interactivity/index.js +1 -1
  651. package/dist/_pkgs/affine-gfx-mindmap/interactivity/mind-map-drag.js +4 -4
  652. package/dist/_pkgs/affine-gfx-mindmap/store.js +1 -1
  653. package/dist/_pkgs/affine-gfx-mindmap/toolbar/config.js +1 -1
  654. package/dist/_pkgs/affine-gfx-mindmap/toolbar/senior-tool.js +2 -2
  655. package/dist/_pkgs/affine-gfx-mindmap/view/index.d.ts +4 -4
  656. package/dist/_pkgs/affine-gfx-mindmap/view/index.js +4 -4
  657. package/dist/_pkgs/affine-gfx-mindmap/view/utils.js +2 -2
  658. package/dist/_pkgs/affine-gfx-mindmap/view.js +8 -8
  659. package/dist/_pkgs/affine-gfx-note/components/note-shadow-panel.js +1 -1
  660. package/dist/_pkgs/affine-gfx-note/effects.d.ts +5 -5
  661. package/dist/_pkgs/affine-gfx-note/effects.js +5 -5
  662. package/dist/_pkgs/affine-gfx-note/index.d.ts +3 -3
  663. package/dist/_pkgs/affine-gfx-note/index.js +3 -3
  664. package/dist/_pkgs/affine-gfx-note/note-tool.js +1 -1
  665. package/dist/_pkgs/affine-gfx-note/overlay/index.d.ts +2 -2
  666. package/dist/_pkgs/affine-gfx-note/overlay/index.js +2 -2
  667. package/dist/_pkgs/affine-gfx-note/view.js +3 -3
  668. package/dist/_pkgs/affine-gfx-pointer/effects.js +1 -1
  669. package/dist/_pkgs/affine-gfx-pointer/index.d.ts +1 -1
  670. package/dist/_pkgs/affine-gfx-pointer/index.js +1 -1
  671. package/dist/_pkgs/affine-gfx-pointer/quick-tool/default-tool-button.d.ts +1 -1
  672. package/dist/_pkgs/affine-gfx-pointer/quick-tool/default-tool-button.js +1 -1
  673. package/dist/_pkgs/affine-gfx-pointer/snap/snap-manager.d.ts +1 -1
  674. package/dist/_pkgs/affine-gfx-pointer/tools/pan-tool.d.ts +16 -0
  675. package/dist/_pkgs/affine-gfx-pointer/tools/pan-tool.js +102 -32
  676. package/dist/_pkgs/affine-gfx-pointer/view.js +5 -5
  677. package/dist/_pkgs/affine-gfx-shape/adapter/index.d.ts +3 -3
  678. package/dist/_pkgs/affine-gfx-shape/adapter/index.js +3 -3
  679. package/dist/_pkgs/affine-gfx-shape/adapter/markdown/index.js +1 -1
  680. package/dist/_pkgs/affine-gfx-shape/adapter/plain-text/index.js +1 -1
  681. package/dist/_pkgs/affine-gfx-shape/commands.d.ts +2 -0
  682. package/dist/_pkgs/affine-gfx-shape/commands.js +33 -0
  683. package/dist/_pkgs/affine-gfx-shape/components/shape-panel.d.ts +1 -1
  684. package/dist/_pkgs/affine-gfx-shape/components/shape-panel.js +1 -1
  685. package/dist/_pkgs/affine-gfx-shape/draggable/index.d.ts +4 -4
  686. package/dist/_pkgs/affine-gfx-shape/draggable/index.js +4 -4
  687. package/dist/_pkgs/affine-gfx-shape/draggable/shape-menu.js +3 -3
  688. package/dist/_pkgs/affine-gfx-shape/draggable/shape-tool-element.js +1 -1
  689. package/dist/_pkgs/affine-gfx-shape/effects.d.ts +4 -4
  690. package/dist/_pkgs/affine-gfx-shape/effects.js +4 -4
  691. package/dist/_pkgs/affine-gfx-shape/element-renderer/index.d.ts +2 -2
  692. package/dist/_pkgs/affine-gfx-shape/element-renderer/index.js +2 -2
  693. package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/index.d.ts +1 -1
  694. package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/index.js +1 -1
  695. package/dist/_pkgs/affine-gfx-shape/element-renderer/shape-dom/index.js +49 -20
  696. package/dist/_pkgs/affine-gfx-shape/element-view.d.ts +1 -1
  697. package/dist/_pkgs/affine-gfx-shape/element-view.js +3 -3
  698. package/dist/_pkgs/affine-gfx-shape/index.d.ts +12 -12
  699. package/dist/_pkgs/affine-gfx-shape/index.js +12 -12
  700. package/dist/_pkgs/affine-gfx-shape/overlay/diamond.d.ts +1 -1
  701. package/dist/_pkgs/affine-gfx-shape/overlay/diamond.js +2 -2
  702. package/dist/_pkgs/affine-gfx-shape/overlay/ellipse.d.ts +1 -1
  703. package/dist/_pkgs/affine-gfx-shape/overlay/ellipse.js +2 -2
  704. package/dist/_pkgs/affine-gfx-shape/overlay/factory.d.ts +1 -1
  705. package/dist/_pkgs/affine-gfx-shape/overlay/factory.js +6 -6
  706. package/dist/_pkgs/affine-gfx-shape/overlay/index.d.ts +5 -5
  707. package/dist/_pkgs/affine-gfx-shape/overlay/index.js +5 -5
  708. package/dist/_pkgs/affine-gfx-shape/overlay/polygon.d.ts +1 -1
  709. package/dist/_pkgs/affine-gfx-shape/overlay/polygon.js +2 -2
  710. package/dist/_pkgs/affine-gfx-shape/overlay/rect.d.ts +1 -1
  711. package/dist/_pkgs/affine-gfx-shape/overlay/rect.js +2 -2
  712. package/dist/_pkgs/affine-gfx-shape/overlay/rounded-rect.d.ts +1 -1
  713. package/dist/_pkgs/affine-gfx-shape/overlay/rounded-rect.js +2 -2
  714. package/dist/_pkgs/affine-gfx-shape/overlay/shape-overlay.d.ts +1 -1
  715. package/dist/_pkgs/affine-gfx-shape/overlay/shape-overlay.js +2 -2
  716. package/dist/_pkgs/affine-gfx-shape/overlay/triangle.d.ts +1 -1
  717. package/dist/_pkgs/affine-gfx-shape/overlay/triangle.js +2 -2
  718. package/dist/_pkgs/affine-gfx-shape/store.js +1 -1
  719. package/dist/_pkgs/affine-gfx-shape/text/edgeless-shape-text-editor.d.ts +26 -0
  720. package/dist/_pkgs/affine-gfx-shape/text/edgeless-shape-text-editor.js +124 -13
  721. package/dist/_pkgs/affine-gfx-shape/text/index.d.ts +1 -1
  722. package/dist/_pkgs/affine-gfx-shape/text/index.js +1 -1
  723. package/dist/_pkgs/affine-gfx-shape/toolbar/config.js +6 -6
  724. package/dist/_pkgs/affine-gfx-shape/toolbar/index.d.ts +4 -4
  725. package/dist/_pkgs/affine-gfx-shape/toolbar/index.js +4 -4
  726. package/dist/_pkgs/affine-gfx-shape/toolbar/shape-menu-config.js +1 -1
  727. package/dist/_pkgs/affine-gfx-shape/view.js +10 -10
  728. package/dist/_pkgs/affine-gfx-template/effects.d.ts +4 -4
  729. package/dist/_pkgs/affine-gfx-template/effects.js +4 -4
  730. package/dist/_pkgs/affine-gfx-template/services/template.js +1 -1
  731. package/dist/_pkgs/affine-gfx-template/toolbar/template-tool-button.d.ts +3 -1
  732. package/dist/_pkgs/affine-gfx-template/toolbar/template-tool-button.js +19 -2
  733. package/dist/_pkgs/affine-gfx-template/view.js +3 -3
  734. package/dist/_pkgs/affine-gfx-text/adapter/index.d.ts +2 -2
  735. package/dist/_pkgs/affine-gfx-text/adapter/index.js +2 -2
  736. package/dist/_pkgs/affine-gfx-text/adapter/markdown/index.js +1 -1
  737. package/dist/_pkgs/affine-gfx-text/adapter/plain-text/index.js +1 -1
  738. package/dist/_pkgs/affine-gfx-text/dblclick-add-edgeless-text.js +2 -2
  739. package/dist/_pkgs/affine-gfx-text/edgeless-text-editor.js +10 -6
  740. package/dist/_pkgs/affine-gfx-text/effects.d.ts +2 -2
  741. package/dist/_pkgs/affine-gfx-text/effects.js +2 -2
  742. package/dist/_pkgs/affine-gfx-text/element-renderer/index.d.ts +1 -1
  743. package/dist/_pkgs/affine-gfx-text/element-renderer/index.js +1 -1
  744. package/dist/_pkgs/affine-gfx-text/element-view.js +2 -2
  745. package/dist/_pkgs/affine-gfx-text/index.d.ts +7 -7
  746. package/dist/_pkgs/affine-gfx-text/index.js +7 -7
  747. package/dist/_pkgs/affine-gfx-text/store.js +1 -1
  748. package/dist/_pkgs/affine-gfx-text/tool.js +2 -2
  749. package/dist/_pkgs/affine-gfx-text/toolbar/actions.js +1 -1
  750. package/dist/_pkgs/affine-gfx-text/toolbar/config.js +2 -2
  751. package/dist/_pkgs/affine-gfx-text/toolbar/index.d.ts +2 -2
  752. package/dist/_pkgs/affine-gfx-text/toolbar/index.js +2 -2
  753. package/dist/_pkgs/affine-gfx-text/toolbar/text-menu.d.ts +1 -1
  754. package/dist/_pkgs/affine-gfx-text/toolbar/text-menu.js +1 -1
  755. package/dist/_pkgs/affine-gfx-text/view.js +6 -6
  756. package/dist/_pkgs/affine-gfx-turbo-renderer/index.d.ts +5 -5
  757. package/dist/_pkgs/affine-gfx-turbo-renderer/index.js +5 -5
  758. package/dist/_pkgs/affine-gfx-turbo-renderer/layout/block-layout-provider.d.ts +1 -1
  759. package/dist/_pkgs/affine-gfx-turbo-renderer/painter/painter.worker.d.ts +1 -1
  760. package/dist/_pkgs/affine-gfx-turbo-renderer/renderer-utils.d.ts +1 -1
  761. package/dist/_pkgs/affine-gfx-turbo-renderer/renderer-utils.js +1 -1
  762. package/dist/_pkgs/affine-gfx-turbo-renderer/text-utils.d.ts +1 -1
  763. package/dist/_pkgs/affine-gfx-turbo-renderer/turbo-renderer.d.ts +1 -1
  764. package/dist/_pkgs/affine-gfx-turbo-renderer/turbo-renderer.js +1 -1
  765. package/dist/_pkgs/affine-inline-comment/effects.d.ts +1 -1
  766. package/dist/_pkgs/affine-inline-comment/effects.js +1 -1
  767. package/dist/_pkgs/affine-inline-comment/index.d.ts +2 -2
  768. package/dist/_pkgs/affine-inline-comment/index.js +2 -2
  769. package/dist/_pkgs/affine-inline-comment/inline-comment-manager.js +2 -2
  770. package/dist/_pkgs/affine-inline-comment/inline-spec.js +1 -1
  771. package/dist/_pkgs/affine-inline-comment/view.js +3 -3
  772. package/dist/_pkgs/affine-inline-footnote/adapters/index.d.ts +3 -3
  773. package/dist/_pkgs/affine-inline-footnote/adapters/index.js +3 -3
  774. package/dist/_pkgs/affine-inline-footnote/effects.d.ts +3 -3
  775. package/dist/_pkgs/affine-inline-footnote/effects.js +3 -3
  776. package/dist/_pkgs/affine-inline-footnote/footnote-node/footnote-node.d.ts +2 -2
  777. package/dist/_pkgs/affine-inline-footnote/footnote-node/footnote-popup.d.ts +1 -1
  778. package/dist/_pkgs/affine-inline-footnote/index.d.ts +2 -2
  779. package/dist/_pkgs/affine-inline-footnote/index.js +2 -2
  780. package/dist/_pkgs/affine-inline-footnote/inline-spec.js +1 -1
  781. package/dist/_pkgs/affine-inline-footnote/store.js +1 -1
  782. package/dist/_pkgs/affine-inline-footnote/view.js +2 -2
  783. package/dist/_pkgs/affine-inline-latex/adapters/index.d.ts +3 -3
  784. package/dist/_pkgs/affine-inline-latex/adapters/index.js +3 -3
  785. package/dist/_pkgs/affine-inline-latex/command.js +46 -26
  786. package/dist/_pkgs/affine-inline-latex/effects.d.ts +3 -3
  787. package/dist/_pkgs/affine-inline-latex/effects.js +3 -3
  788. package/dist/_pkgs/affine-inline-latex/index.d.ts +4 -4
  789. package/dist/_pkgs/affine-inline-latex/index.js +4 -4
  790. package/dist/_pkgs/affine-inline-latex/latex-node/latex-editor-menu.js +1 -1
  791. package/dist/_pkgs/affine-inline-latex/latex-node/latex-node.d.ts +10 -0
  792. package/dist/_pkgs/affine-inline-latex/latex-node/latex-node.js +23 -0
  793. package/dist/_pkgs/affine-inline-latex/store.js +1 -1
  794. package/dist/_pkgs/affine-inline-latex/view.js +3 -3
  795. package/dist/_pkgs/affine-inline-link/adapters/index.d.ts +6 -6
  796. package/dist/_pkgs/affine-inline-link/adapters/index.js +6 -6
  797. package/dist/_pkgs/affine-inline-link/adapters/markdown/index.d.ts +2 -2
  798. package/dist/_pkgs/affine-inline-link/adapters/markdown/index.js +2 -2
  799. package/dist/_pkgs/affine-inline-link/command.js +1 -1
  800. package/dist/_pkgs/affine-inline-link/effects.js +2 -2
  801. package/dist/_pkgs/affine-inline-link/index.d.ts +6 -6
  802. package/dist/_pkgs/affine-inline-link/index.js +6 -6
  803. package/dist/_pkgs/affine-inline-link/link-node/affine-link.js +4 -1
  804. package/dist/_pkgs/affine-inline-link/link-node/configs/toolbar.js +2 -2
  805. package/dist/_pkgs/affine-inline-link/link-node/link-popup/link-popup.js +1 -1
  806. package/dist/_pkgs/affine-inline-link/link-node/link-popup/toggle-link-popup.d.ts +1 -1
  807. package/dist/_pkgs/affine-inline-link/link-node/link-popup/toggle-link-popup.js +1 -1
  808. package/dist/_pkgs/affine-inline-link/store.js +1 -1
  809. package/dist/_pkgs/affine-inline-link/view.js +3 -3
  810. package/dist/_pkgs/affine-inline-mention/effects.d.ts +1 -1
  811. package/dist/_pkgs/affine-inline-mention/effects.js +1 -1
  812. package/dist/_pkgs/affine-inline-mention/index.d.ts +3 -3
  813. package/dist/_pkgs/affine-inline-mention/index.js +3 -3
  814. package/dist/_pkgs/affine-inline-mention/view.js +2 -2
  815. package/dist/_pkgs/affine-inline-preset/adapters/extensions.d.ts +5 -5
  816. package/dist/_pkgs/affine-inline-preset/adapters/extensions.js +10 -10
  817. package/dist/_pkgs/affine-inline-preset/adapters/html/html-inline.d.ts +3 -0
  818. package/dist/_pkgs/affine-inline-preset/adapters/html/html-inline.js +29 -1
  819. package/dist/_pkgs/affine-inline-preset/default-inline-manager.js +1 -1
  820. package/dist/_pkgs/affine-inline-preset/effects.d.ts +1 -1
  821. package/dist/_pkgs/affine-inline-preset/effects.js +1 -1
  822. package/dist/_pkgs/affine-inline-preset/index.d.ts +6 -6
  823. package/dist/_pkgs/affine-inline-preset/index.js +6 -6
  824. package/dist/_pkgs/affine-inline-preset/store.js +1 -1
  825. package/dist/_pkgs/affine-inline-preset/view.js +4 -4
  826. package/dist/_pkgs/affine-inline-reference/adapters/index.d.ts +3 -3
  827. package/dist/_pkgs/affine-inline-reference/adapters/index.js +3 -3
  828. package/dist/_pkgs/affine-inline-reference/effects.d.ts +1 -1
  829. package/dist/_pkgs/affine-inline-reference/effects.js +1 -1
  830. package/dist/_pkgs/affine-inline-reference/index.d.ts +5 -5
  831. package/dist/_pkgs/affine-inline-reference/index.js +5 -5
  832. package/dist/_pkgs/affine-inline-reference/inline-spec.js +1 -1
  833. package/dist/_pkgs/affine-inline-reference/reference-node/configs/toolbar.js +1 -1
  834. package/dist/_pkgs/affine-inline-reference/reference-node/index.d.ts +6 -6
  835. package/dist/_pkgs/affine-inline-reference/reference-node/index.js +5 -5
  836. package/dist/_pkgs/affine-inline-reference/reference-node/reference-config.d.ts +1 -1
  837. package/dist/_pkgs/affine-inline-reference/reference-node/reference-node.d.ts +2 -2
  838. package/dist/_pkgs/affine-inline-reference/reference-node/reference-node.js +1 -1
  839. package/dist/_pkgs/affine-inline-reference/reference-node/reference-popup/toggle-reference-popup.d.ts +1 -1
  840. package/dist/_pkgs/affine-inline-reference/reference-node/reference-popup/toggle-reference-popup.js +1 -1
  841. package/dist/_pkgs/affine-inline-reference/store.js +1 -1
  842. package/dist/_pkgs/affine-inline-reference/toolbar.js +1 -1
  843. package/dist/_pkgs/affine-inline-reference/view.js +4 -4
  844. package/dist/_pkgs/affine-model/blocks/callout/callout-model.d.ts +1 -1
  845. package/dist/_pkgs/affine-model/blocks/code/code-model.d.ts +2 -1
  846. package/dist/_pkgs/affine-model/blocks/code/code-model.js +1 -0
  847. package/dist/_pkgs/affine-model/blocks/edgeless-text/edgeless-text-model.d.ts +1 -1
  848. package/dist/_pkgs/affine-model/blocks/edgeless-text/edgeless-text-model.js +3 -3
  849. package/dist/_pkgs/affine-model/blocks/embed/iframe/iframe-schema.d.ts +2 -2
  850. package/dist/_pkgs/affine-model/blocks/embed/iframe/iframe-schema.js +1 -1
  851. package/dist/_pkgs/affine-model/blocks/embed/iframe/index.d.ts +2 -2
  852. package/dist/_pkgs/affine-model/blocks/embed/iframe/index.js +2 -2
  853. package/dist/_pkgs/affine-model/blocks/embed/index.d.ts +9 -9
  854. package/dist/_pkgs/affine-model/blocks/embed/index.js +9 -9
  855. package/dist/_pkgs/affine-model/blocks/embed/types.d.ts +9 -9
  856. package/dist/_pkgs/affine-model/blocks/embed/types.js +6 -6
  857. package/dist/_pkgs/affine-model/blocks/index.d.ts +1 -1
  858. package/dist/_pkgs/affine-model/blocks/index.js +1 -1
  859. package/dist/_pkgs/affine-model/blocks/list/list-model.d.ts +1 -1
  860. package/dist/_pkgs/affine-model/blocks/note/note-model.d.ts +2 -2
  861. package/dist/_pkgs/affine-model/blocks/note/note-model.js +2 -2
  862. package/dist/_pkgs/affine-model/blocks/paragraph/paragraph-model.d.ts +1 -1
  863. package/dist/_pkgs/affine-model/blocks/table/index.d.ts +1 -1
  864. package/dist/_pkgs/affine-model/blocks/table/index.js +1 -1
  865. package/dist/_pkgs/affine-model/blocks/table/table-model.d.ts +1 -1
  866. package/dist/_pkgs/affine-model/elements/brush/brush.d.ts +1 -1
  867. package/dist/_pkgs/affine-model/elements/brush/brush.js +1 -1
  868. package/dist/_pkgs/affine-model/elements/connector/connector.d.ts +2 -2
  869. package/dist/_pkgs/affine-model/elements/connector/connector.js +26 -2
  870. package/dist/_pkgs/affine-model/elements/connector/local-connector.d.ts +2 -2
  871. package/dist/_pkgs/affine-model/elements/connector/local-connector.js +2 -2
  872. package/dist/_pkgs/affine-model/elements/edgy/board.d.ts +32 -0
  873. package/dist/_pkgs/affine-model/elements/edgy/board.js +112 -0
  874. package/dist/_pkgs/affine-model/elements/edgy/facets.d.ts +12 -0
  875. package/dist/_pkgs/affine-model/elements/edgy/facets.js +25 -1
  876. package/dist/_pkgs/affine-model/elements/edgy/index.d.ts +1 -0
  877. package/dist/_pkgs/affine-model/elements/edgy/index.js +1 -0
  878. package/dist/_pkgs/affine-model/elements/framework-background/background.d.ts +42 -0
  879. package/dist/_pkgs/affine-model/elements/framework-background/background.js +42 -0
  880. package/dist/_pkgs/affine-model/elements/framework-background/index.d.ts +1 -0
  881. package/dist/_pkgs/affine-model/elements/framework-background/index.js +1 -0
  882. package/dist/_pkgs/affine-model/elements/highlighter/highlighter.d.ts +1 -1
  883. package/dist/_pkgs/affine-model/elements/highlighter/highlighter.js +2 -2
  884. package/dist/_pkgs/affine-model/elements/highlighter/index.d.ts +1 -1
  885. package/dist/_pkgs/affine-model/elements/highlighter/index.js +1 -1
  886. package/dist/_pkgs/affine-model/elements/index.d.ts +3 -1
  887. package/dist/_pkgs/affine-model/elements/index.js +1 -0
  888. package/dist/_pkgs/affine-model/elements/mindmap/index.d.ts +3 -3
  889. package/dist/_pkgs/affine-model/elements/mindmap/index.js +3 -3
  890. package/dist/_pkgs/affine-model/elements/mindmap/mindmap.js +10 -5
  891. package/dist/_pkgs/affine-model/elements/mindmap/snapshot/index.d.ts +2 -2
  892. package/dist/_pkgs/affine-model/elements/mindmap/snapshot/index.js +2 -2
  893. package/dist/_pkgs/affine-model/elements/mindmap/snapshot/utils.d.ts +1 -1
  894. package/dist/_pkgs/affine-model/elements/mindmap/style.d.ts +10 -0
  895. package/dist/_pkgs/affine-model/elements/mindmap/style.js +10 -2
  896. package/dist/_pkgs/affine-model/elements/text/text.d.ts +2 -2
  897. package/dist/_pkgs/affine-model/elements/text/text.js +2 -2
  898. package/dist/_pkgs/affine-model/elements/wardley/wardley.d.ts +21 -29
  899. package/dist/_pkgs/affine-model/elements/wardley/wardley.js +41 -48
  900. package/dist/_pkgs/affine-model/themes/default.d.ts +1 -1
  901. package/dist/_pkgs/affine-model/themes/default.js +1 -1
  902. package/dist/_pkgs/affine-model/themes/index.d.ts +4 -4
  903. package/dist/_pkgs/affine-model/themes/index.js +4 -4
  904. package/dist/_pkgs/affine-model/themes/types.js +1 -1
  905. package/dist/_pkgs/affine-model/themes/utils.d.ts +2 -2
  906. package/dist/_pkgs/affine-model/utils/helper.d.ts +1 -1
  907. package/dist/_pkgs/affine-model/utils/types.d.ts +1 -1
  908. package/dist/_pkgs/affine-rich-text/index.d.ts +4 -4
  909. package/dist/_pkgs/affine-rich-text/index.js +4 -4
  910. package/dist/_pkgs/affine-rich-text/utils.js +1 -1
  911. package/dist/_pkgs/affine-shared/adapters/attachment.js +1 -1
  912. package/dist/_pkgs/affine-shared/adapters/clipboard/index.d.ts +2 -2
  913. package/dist/_pkgs/affine-shared/adapters/clipboard/index.js +2 -2
  914. package/dist/_pkgs/affine-shared/adapters/html/html.d.ts +3 -3
  915. package/dist/_pkgs/affine-shared/adapters/html/html.js +5 -5
  916. package/dist/_pkgs/affine-shared/adapters/html/rehype-plugins/index.d.ts +2 -2
  917. package/dist/_pkgs/affine-shared/adapters/html/rehype-plugins/index.js +2 -2
  918. package/dist/_pkgs/affine-shared/adapters/html/rehype-plugins/inline-to-block.js +1 -1
  919. package/dist/_pkgs/affine-shared/adapters/html/rehype-plugins/wrap-inline-element.js +1 -1
  920. package/dist/_pkgs/affine-shared/adapters/image.js +1 -1
  921. package/dist/_pkgs/affine-shared/adapters/index.d.ts +12 -12
  922. package/dist/_pkgs/affine-shared/adapters/index.js +12 -12
  923. package/dist/_pkgs/affine-shared/adapters/markdown/delta-converter.d.ts +6 -1
  924. package/dist/_pkgs/affine-shared/adapters/markdown/delta-converter.js +160 -2
  925. package/dist/_pkgs/affine-shared/adapters/markdown/markdown.d.ts +5 -5
  926. package/dist/_pkgs/affine-shared/adapters/markdown/markdown.js +11 -7
  927. package/dist/_pkgs/affine-shared/adapters/markdown/preprocessor.d.ts +2 -2
  928. package/dist/_pkgs/affine-shared/adapters/markdown/preprocessor.js +1 -1
  929. package/dist/_pkgs/affine-shared/adapters/markdown/remark-plugins/index.d.ts +1 -1
  930. package/dist/_pkgs/affine-shared/adapters/markdown/remark-plugins/index.js +1 -1
  931. package/dist/_pkgs/affine-shared/adapters/middlewares/index.d.ts +12 -12
  932. package/dist/_pkgs/affine-shared/adapters/middlewares/index.js +12 -12
  933. package/dist/_pkgs/affine-shared/adapters/middlewares/paste.js +4 -4
  934. package/dist/_pkgs/affine-shared/adapters/middlewares/proxy.js +1 -1
  935. package/dist/_pkgs/affine-shared/adapters/middlewares/replace-id.js +1 -1
  936. package/dist/_pkgs/affine-shared/adapters/mix-text.js +2 -2
  937. package/dist/_pkgs/affine-shared/adapters/notion-html/delta-converter.js +1 -1
  938. package/dist/_pkgs/affine-shared/adapters/notion-html/notion-html.d.ts +2 -2
  939. package/dist/_pkgs/affine-shared/adapters/notion-html/notion-html.js +4 -4
  940. package/dist/_pkgs/affine-shared/adapters/notion-text.js +1 -1
  941. package/dist/_pkgs/affine-shared/adapters/plain-text/plain-text.d.ts +3 -3
  942. package/dist/_pkgs/affine-shared/adapters/plain-text/plain-text.js +3 -3
  943. package/dist/_pkgs/affine-shared/adapters/types/delta-converter.d.ts +2 -1
  944. package/dist/_pkgs/affine-shared/adapters/utils/color.d.ts +24 -0
  945. package/dist/_pkgs/affine-shared/adapters/utils/color.js +280 -0
  946. package/dist/_pkgs/affine-shared/adapters/utils/index.d.ts +1 -0
  947. package/dist/_pkgs/affine-shared/adapters/utils/index.js +1 -0
  948. package/dist/_pkgs/affine-shared/adapters/utils/text.js +1 -1
  949. package/dist/_pkgs/affine-shared/commands/block-crud/get-selected-blocks.js +10 -2
  950. package/dist/_pkgs/affine-shared/commands/model-crud/duplicate-selected-model.js +1 -1
  951. package/dist/_pkgs/affine-shared/commands/model-crud/get-selected-models.js +2 -2
  952. package/dist/_pkgs/affine-shared/commands/model-crud/replace-selected-text-with-blocks.js +1 -1
  953. package/dist/_pkgs/affine-shared/commands/selection/get-block-selections.d.ts +1 -1
  954. package/dist/_pkgs/affine-shared/commands/selection/get-surface-selection.d.ts +1 -1
  955. package/dist/_pkgs/affine-shared/commands/selection/get-text-selection.d.ts +1 -1
  956. package/dist/_pkgs/affine-shared/commands/selection/index.d.ts +6 -6
  957. package/dist/_pkgs/affine-shared/commands/selection/index.js +6 -6
  958. package/dist/_pkgs/affine-shared/commands/selection/is-nothing-selected.d.ts +1 -1
  959. package/dist/_pkgs/affine-shared/commands/selection/is-nothing-selected.js +1 -1
  960. package/dist/_pkgs/affine-shared/commands/selection/types.d.ts +1 -1
  961. package/dist/_pkgs/affine-shared/services/audit-service.d.ts +300 -0
  962. package/dist/_pkgs/affine-shared/services/audit-service.js +228 -0
  963. package/dist/_pkgs/affine-shared/services/block-meta-service.js +2 -2
  964. package/dist/_pkgs/affine-shared/services/citation-service/citation-service.d.ts +2 -2
  965. package/dist/_pkgs/affine-shared/services/citation-service/citation-service.js +2 -2
  966. package/dist/_pkgs/affine-shared/services/citation-service/index.d.ts +1 -1
  967. package/dist/_pkgs/affine-shared/services/citation-service/index.js +1 -1
  968. package/dist/_pkgs/affine-shared/services/comment-service/block-comment-manager.d.ts +1 -1
  969. package/dist/_pkgs/affine-shared/services/comment-service/block-comment-manager.js +5 -5
  970. package/dist/_pkgs/affine-shared/services/comment-service/index.d.ts +3 -3
  971. package/dist/_pkgs/affine-shared/services/comment-service/index.js +3 -3
  972. package/dist/_pkgs/affine-shared/services/comment-service/utils.d.ts +2 -2
  973. package/dist/_pkgs/affine-shared/services/comment-service/utils.js +1 -1
  974. package/dist/_pkgs/affine-shared/services/edit-props-store.d.ts +7 -0
  975. package/dist/_pkgs/affine-shared/services/edit-props-store.js +13 -2
  976. package/dist/_pkgs/affine-shared/services/embed-iframe/embed-iframe-service.d.ts +1 -1
  977. package/dist/_pkgs/affine-shared/services/embed-iframe/embed-iframe-service.js +1 -1
  978. package/dist/_pkgs/affine-shared/services/embed-iframe/index.d.ts +2 -2
  979. package/dist/_pkgs/affine-shared/services/embed-iframe/index.js +2 -2
  980. package/dist/_pkgs/affine-shared/services/font-loader/font-loader-service.d.ts +26 -1
  981. package/dist/_pkgs/affine-shared/services/font-loader/font-loader-service.js +124 -7
  982. package/dist/_pkgs/affine-shared/services/index.d.ts +37 -32
  983. package/dist/_pkgs/affine-shared/services/index.js +37 -32
  984. package/dist/_pkgs/affine-shared/services/link-preview-service/index.d.ts +3 -3
  985. package/dist/_pkgs/affine-shared/services/link-preview-service/index.js +3 -3
  986. package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-cache.js +2 -2
  987. package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-service.d.ts +1 -1
  988. package/dist/_pkgs/affine-shared/services/link-preview-service/link-preview-service.js +3 -3
  989. package/dist/_pkgs/affine-shared/services/page-viewport-service.d.ts +1 -1
  990. package/dist/_pkgs/affine-shared/services/pivot-materiality-publisher.d.ts +129 -0
  991. package/dist/_pkgs/affine-shared/services/pivot-materiality-publisher.js +311 -0
  992. package/dist/_pkgs/affine-shared/services/pivot-properties-service.d.ts +217 -0
  993. package/dist/_pkgs/affine-shared/services/pivot-properties-service.js +114 -0
  994. package/dist/_pkgs/affine-shared/services/telemetry-service/citation.d.ts +1 -1
  995. package/dist/_pkgs/affine-shared/services/telemetry-service/code-block.d.ts +1 -1
  996. package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +182 -0
  997. package/dist/_pkgs/affine-shared/services/telemetry-service/telemetry-service.d.ts +2 -2
  998. package/dist/_pkgs/affine-shared/services/telemetry-service/types.d.ts +6 -1
  999. package/dist/_pkgs/affine-shared/services/theme-service.js +1 -1
  1000. package/dist/_pkgs/affine-shared/services/toolbar-service/action.d.ts +11 -1
  1001. package/dist/_pkgs/affine-shared/services/toolbar-service/config.d.ts +2 -2
  1002. package/dist/_pkgs/affine-shared/services/toolbar-service/context.d.ts +6 -6
  1003. package/dist/_pkgs/affine-shared/services/toolbar-service/context.js +6 -6
  1004. package/dist/_pkgs/affine-shared/services/toolbar-service/index.d.ts +7 -6
  1005. package/dist/_pkgs/affine-shared/services/toolbar-service/index.js +7 -6
  1006. package/dist/_pkgs/affine-shared/services/toolbar-service/layout.d.ts +134 -0
  1007. package/dist/_pkgs/affine-shared/services/toolbar-service/layout.js +146 -0
  1008. package/dist/_pkgs/affine-shared/services/toolbar-service/module.d.ts +1 -1
  1009. package/dist/_pkgs/affine-shared/services/toolbar-service/registry.d.ts +4 -4
  1010. package/dist/_pkgs/affine-shared/services/toolbar-service/registry.js +1 -1
  1011. package/dist/_pkgs/affine-shared/services/translation-service/index.d.ts +68 -0
  1012. package/dist/_pkgs/affine-shared/services/translation-service/index.js +39 -0
  1013. package/dist/_pkgs/affine-shared/services/universe-tag-defs-service.d.ts +155 -0
  1014. package/dist/_pkgs/affine-shared/services/universe-tag-defs-service.js +254 -0
  1015. package/dist/_pkgs/affine-shared/services/user-service/index.d.ts +4 -4
  1016. package/dist/_pkgs/affine-shared/services/user-service/index.js +4 -4
  1017. package/dist/_pkgs/affine-shared/services/user-service/user-list-service.d.ts +1 -1
  1018. package/dist/_pkgs/affine-shared/services/user-service/user-service.d.ts +1 -1
  1019. package/dist/_pkgs/affine-shared/services/user-service/writer-info-service.d.ts +1 -1
  1020. package/dist/_pkgs/affine-shared/services/viewport-element-service.d.ts +1 -1
  1021. package/dist/_pkgs/affine-shared/styles/index.d.ts +4 -4
  1022. package/dist/_pkgs/affine-shared/styles/index.js +4 -4
  1023. package/dist/_pkgs/affine-shared/styles/panel.js +2 -2
  1024. package/dist/_pkgs/affine-shared/styles/text.d.ts +1 -1
  1025. package/dist/_pkgs/affine-shared/test-utils/affine-template.js +1 -1
  1026. package/dist/_pkgs/affine-shared/test-utils/index.d.ts +3 -3
  1027. package/dist/_pkgs/affine-shared/test-utils/index.js +3 -3
  1028. package/dist/_pkgs/affine-shared/types/index.d.ts +1 -1
  1029. package/dist/_pkgs/affine-shared/types/index.js +1 -1
  1030. package/dist/_pkgs/affine-shared/utils/computing.js +2 -2
  1031. package/dist/_pkgs/affine-shared/utils/dom/point-to-range.d.ts +4 -0
  1032. package/dist/_pkgs/affine-shared/utils/dom/point-to-range.js +58 -2
  1033. package/dist/_pkgs/affine-shared/utils/dom/viewport.d.ts +60 -0
  1034. package/dist/_pkgs/affine-shared/utils/dom/viewport.js +58 -0
  1035. package/dist/_pkgs/affine-shared/utils/drag-helper/cell-select.d.ts +1 -1
  1036. package/dist/_pkgs/affine-shared/utils/drag-helper/index.d.ts +3 -3
  1037. package/dist/_pkgs/affine-shared/utils/drag-helper/index.js +3 -3
  1038. package/dist/_pkgs/affine-shared/utils/drag-helper/linear-move.d.ts +1 -1
  1039. package/dist/_pkgs/affine-shared/utils/index.d.ts +27 -27
  1040. package/dist/_pkgs/affine-shared/utils/index.js +27 -27
  1041. package/dist/_pkgs/affine-shared/utils/popper-position.js +1 -1
  1042. package/dist/_pkgs/affine-shared/utils/print-to-pdf.d.ts +21 -0
  1043. package/dist/_pkgs/affine-shared/utils/print-to-pdf.js +166 -32
  1044. package/dist/_pkgs/affine-shared/utils/reference.js +1 -1
  1045. package/dist/_pkgs/affine-shared/utils/virtual-padding.js +1 -1
  1046. package/dist/_pkgs/affine-shared/utils/zod-schema.d.ts +7 -0
  1047. package/dist/_pkgs/affine-shared/utils/zod-schema.js +27 -1
  1048. package/dist/_pkgs/affine-widget-drag-handle/effects.js +3 -3
  1049. package/dist/_pkgs/affine-widget-drag-handle/index.d.ts +4 -4
  1050. package/dist/_pkgs/affine-widget-drag-handle/index.js +4 -4
  1051. package/dist/_pkgs/affine-widget-drag-handle/view.js +2 -2
  1052. package/dist/_pkgs/affine-widget-drag-handle/watchers/drag-event-watcher.js +3 -3
  1053. package/dist/_pkgs/affine-widget-drag-handle/watchers/edgeless-watcher.d.ts +19 -10
  1054. package/dist/_pkgs/affine-widget-drag-handle/watchers/edgeless-watcher.js +122 -23
  1055. package/dist/_pkgs/affine-widget-drag-handle/watchers/pointer-event-watcher.d.ts +2 -0
  1056. package/dist/_pkgs/affine-widget-drag-handle/watchers/pointer-event-watcher.js +26 -5
  1057. package/dist/_pkgs/affine-widget-edgeless-auto-connect/effects.js +1 -1
  1058. package/dist/_pkgs/affine-widget-edgeless-auto-connect/view.js +2 -2
  1059. package/dist/_pkgs/affine-widget-edgeless-dragging-area/effects.js +1 -1
  1060. package/dist/_pkgs/affine-widget-edgeless-dragging-area/index.d.ts +1 -1
  1061. package/dist/_pkgs/affine-widget-edgeless-dragging-area/index.js +1 -1
  1062. package/dist/_pkgs/affine-widget-edgeless-dragging-area/view.js +2 -2
  1063. package/dist/_pkgs/affine-widget-edgeless-selected-rect/auto-complete-panel.js +10 -5
  1064. package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-auto-complete.d.ts +15 -1
  1065. package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-auto-complete.js +37 -32
  1066. package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-element-link.js +5 -4
  1067. package/dist/_pkgs/affine-widget-edgeless-selected-rect/edgeless-selected-rect.js +19 -9
  1068. package/dist/_pkgs/affine-widget-edgeless-selected-rect/effects.js +4 -4
  1069. package/dist/_pkgs/affine-widget-edgeless-selected-rect/index.d.ts +1 -1
  1070. package/dist/_pkgs/affine-widget-edgeless-selected-rect/index.js +1 -1
  1071. package/dist/_pkgs/affine-widget-edgeless-selected-rect/spec.js +2 -2
  1072. package/dist/_pkgs/affine-widget-edgeless-selected-rect/utils.d.ts +16 -2
  1073. package/dist/_pkgs/affine-widget-edgeless-selected-rect/utils.js +19 -2
  1074. package/dist/_pkgs/affine-widget-edgeless-selected-rect/view.js +2 -2
  1075. package/dist/_pkgs/affine-widget-edgeless-toolbar/config/utils.d.ts +1 -1
  1076. package/dist/_pkgs/affine-widget-edgeless-toolbar/edgeless-toolbar.js +1 -1
  1077. package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.d.ts +7 -7
  1078. package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.js +7 -7
  1079. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.d.ts +8 -7
  1080. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.js +8 -7
  1081. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.d.ts +38 -0
  1082. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.js +99 -0
  1083. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/slide-menu.d.ts +1 -1
  1084. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/slide-menu.js +1 -1
  1085. package/dist/_pkgs/affine-widget-edgeless-toolbar/mixins/index.d.ts +3 -3
  1086. package/dist/_pkgs/affine-widget-edgeless-toolbar/mixins/index.js +3 -3
  1087. package/dist/_pkgs/affine-widget-edgeless-toolbar/mixins/tool.mixin.d.ts +3 -3
  1088. package/dist/_pkgs/affine-widget-edgeless-toolbar/mixins/tool.mixin.js +2 -2
  1089. package/dist/_pkgs/affine-widget-edgeless-toolbar/view.js +2 -2
  1090. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/effects.js +3 -3
  1091. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/view.js +2 -2
  1092. package/dist/_pkgs/affine-widget-edgeless-zoom-toolbar/zoom-toolbar.js +1 -1
  1093. package/dist/_pkgs/affine-widget-frame-title/edgeless-frame-title-editor.js +6 -3
  1094. package/dist/_pkgs/affine-widget-frame-title/mount-frame-title-editor.js +1 -1
  1095. package/dist/_pkgs/affine-widget-frame-title/view.js +2 -2
  1096. package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
  1097. package/dist/_pkgs/affine-widget-keyboard-toolbar/keyboard-toolbar.d.ts +2 -2
  1098. package/dist/_pkgs/affine-widget-keyboard-toolbar/keyboard-toolbar.js +3 -3
  1099. package/dist/_pkgs/affine-widget-keyboard-toolbar/view.js +2 -2
  1100. package/dist/_pkgs/affine-widget-linked-doc/config.d.ts +1 -1
  1101. package/dist/_pkgs/affine-widget-linked-doc/index.d.ts +4 -4
  1102. package/dist/_pkgs/affine-widget-linked-doc/index.js +4 -4
  1103. package/dist/_pkgs/affine-widget-linked-doc/linked-doc-popover.js +3 -16
  1104. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.d.ts +10 -1
  1105. package/dist/_pkgs/affine-widget-linked-doc/transformers/markdown.js +133 -6
  1106. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.d.ts +10 -0
  1107. package/dist/_pkgs/affine-widget-linked-doc/transformers/utils.js +120 -3
  1108. package/dist/_pkgs/affine-widget-linked-doc/utils.d.ts +12 -0
  1109. package/dist/_pkgs/affine-widget-linked-doc/utils.js +33 -0
  1110. package/dist/_pkgs/affine-widget-linked-doc/view.d.ts +1 -1
  1111. package/dist/_pkgs/affine-widget-linked-doc/view.js +3 -3
  1112. package/dist/_pkgs/affine-widget-note-slicer/effects.js +1 -1
  1113. package/dist/_pkgs/affine-widget-note-slicer/index.d.ts +1 -1
  1114. package/dist/_pkgs/affine-widget-note-slicer/index.js +1 -1
  1115. package/dist/_pkgs/affine-widget-note-slicer/view.js +2 -2
  1116. package/dist/_pkgs/affine-widget-page-dragging-area/effects.js +1 -1
  1117. package/dist/_pkgs/affine-widget-page-dragging-area/index.d.ts +1 -1
  1118. package/dist/_pkgs/affine-widget-page-dragging-area/index.js +1 -1
  1119. package/dist/_pkgs/affine-widget-page-dragging-area/view.js +2 -2
  1120. package/dist/_pkgs/affine-widget-remote-selection/doc/doc-remote-selection.js +3 -3
  1121. package/dist/_pkgs/affine-widget-remote-selection/edgeless/index.js +7 -5
  1122. package/dist/_pkgs/affine-widget-remote-selection/effects.js +3 -3
  1123. package/dist/_pkgs/affine-widget-remote-selection/index.d.ts +2 -2
  1124. package/dist/_pkgs/affine-widget-remote-selection/index.js +4 -4
  1125. package/dist/_pkgs/affine-widget-remote-selection/manager/remote-color-manager.js +1 -1
  1126. package/dist/_pkgs/affine-widget-remote-selection/view.js +2 -2
  1127. package/dist/_pkgs/affine-widget-scroll-anchoring/view.js +2 -2
  1128. package/dist/_pkgs/affine-widget-slash-menu/config.d.ts +1 -1
  1129. package/dist/_pkgs/affine-widget-slash-menu/config.js +2 -2
  1130. package/dist/_pkgs/affine-widget-slash-menu/effects.d.ts +2 -2
  1131. package/dist/_pkgs/affine-widget-slash-menu/effects.js +3 -3
  1132. package/dist/_pkgs/affine-widget-slash-menu/extensions.d.ts +1 -1
  1133. package/dist/_pkgs/affine-widget-slash-menu/extensions.js +3 -3
  1134. package/dist/_pkgs/affine-widget-slash-menu/index.d.ts +3 -3
  1135. package/dist/_pkgs/affine-widget-slash-menu/index.js +3 -3
  1136. package/dist/_pkgs/affine-widget-slash-menu/slash-menu-popover.js +10 -6
  1137. package/dist/_pkgs/affine-widget-slash-menu/tooltips/index.d.ts +1 -1
  1138. package/dist/_pkgs/affine-widget-slash-menu/tooltips/index.js +8 -8
  1139. package/dist/_pkgs/affine-widget-slash-menu/utils.d.ts +10 -1
  1140. package/dist/_pkgs/affine-widget-slash-menu/utils.js +24 -0
  1141. package/dist/_pkgs/affine-widget-slash-menu/view.js +2 -2
  1142. package/dist/_pkgs/affine-widget-slash-menu/widget.d.ts +1 -1
  1143. package/dist/_pkgs/affine-widget-slash-menu/widget.js +5 -5
  1144. package/dist/_pkgs/affine-widget-toolbar/effects.d.ts +1 -1
  1145. package/dist/_pkgs/affine-widget-toolbar/effects.js +1 -1
  1146. package/dist/_pkgs/affine-widget-toolbar/index.d.ts +1 -1
  1147. package/dist/_pkgs/affine-widget-toolbar/index.js +2 -2
  1148. package/dist/_pkgs/affine-widget-toolbar/toolbar.d.ts +6 -0
  1149. package/dist/_pkgs/affine-widget-toolbar/toolbar.js +37 -9
  1150. package/dist/_pkgs/affine-widget-toolbar/utils.d.ts +146 -3
  1151. package/dist/_pkgs/affine-widget-toolbar/utils.js +743 -61
  1152. package/dist/_pkgs/affine-widget-toolbar/view.js +2 -2
  1153. package/dist/_pkgs/affine-widget-viewport-overlay/effects.js +1 -1
  1154. package/dist/_pkgs/affine-widget-viewport-overlay/view.js +2 -2
  1155. package/dist/_pkgs/data-view/core/common/property-menu.js +3 -16
  1156. package/dist/_pkgs/data-view/core/component/tags/multi-tag-select.d.ts +4 -0
  1157. package/dist/_pkgs/data-view/core/component/tags/multi-tag-select.js +28 -1
  1158. package/dist/_pkgs/data-view/core/data-view.d.ts +3 -1
  1159. package/dist/_pkgs/data-view/core/data-view.js +25 -3
  1160. package/dist/_pkgs/data-view/core/detail/field.js +2 -2
  1161. package/dist/_pkgs/data-view/core/detail/selection.d.ts +5 -3
  1162. package/dist/_pkgs/data-view/core/detail/selection.js +23 -7
  1163. package/dist/_pkgs/data-view/core/effect.js +2 -0
  1164. package/dist/_pkgs/data-view/core/filter/eval.js +36 -23
  1165. package/dist/_pkgs/data-view/core/filter/filter-fn/matcher.d.ts +7 -0
  1166. package/dist/_pkgs/data-view/core/filter/filter-fn/matcher.js +12 -0
  1167. package/dist/_pkgs/data-view/core/group-by/compare-date-keys.d.ts +11 -0
  1168. package/dist/_pkgs/data-view/core/group-by/compare-date-keys.js +55 -0
  1169. package/dist/_pkgs/data-view/core/group-by/define.d.ts +6 -0
  1170. package/dist/_pkgs/data-view/core/group-by/define.js +114 -0
  1171. package/dist/_pkgs/data-view/core/group-by/matcher.d.ts +7 -0
  1172. package/dist/_pkgs/data-view/core/group-by/matcher.js +13 -0
  1173. package/dist/_pkgs/data-view/core/group-by/renderer/boolean-group.js +11 -11
  1174. package/dist/_pkgs/data-view/core/group-by/renderer/date-group.d.ts +9 -0
  1175. package/dist/_pkgs/data-view/core/group-by/renderer/date-group.js +97 -0
  1176. package/dist/_pkgs/data-view/core/group-by/setting.js +86 -7
  1177. package/dist/_pkgs/data-view/core/group-by/trait.d.ts +34 -0
  1178. package/dist/_pkgs/data-view/core/group-by/trait.js +106 -10
  1179. package/dist/_pkgs/data-view/core/index.d.ts +1 -1
  1180. package/dist/_pkgs/data-view/core/index.js +1 -1
  1181. package/dist/_pkgs/data-view/core/property/utils.d.ts +2 -2
  1182. package/dist/_pkgs/data-view/core/types.d.ts +1 -1
  1183. package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
  1184. package/dist/_pkgs/data-view/core/view-manager/index.d.ts +3 -3
  1185. package/dist/_pkgs/data-view/core/view-manager/index.js +3 -3
  1186. package/dist/_pkgs/data-view/property-presets/multi-select/cell-renderer.js +3 -1
  1187. package/dist/_pkgs/data-view/property-presets/number/define.js +2 -1
  1188. package/dist/_pkgs/data-view/property-presets/select/cell-renderer.js +3 -1
  1189. package/dist/_pkgs/data-view/view-presets/calendar/calendar-view-manager.d.ts +70 -0
  1190. package/dist/_pkgs/data-view/view-presets/calendar/calendar-view-manager.js +449 -0
  1191. package/dist/_pkgs/data-view/view-presets/calendar/define.d.ts +10 -0
  1192. package/dist/_pkgs/data-view/view-presets/calendar/define.js +27 -0
  1193. package/dist/_pkgs/data-view/view-presets/calendar/effect.d.ts +1 -0
  1194. package/dist/_pkgs/data-view/view-presets/calendar/effect.js +4 -0
  1195. package/dist/_pkgs/data-view/view-presets/calendar/index.d.ts +6 -0
  1196. package/dist/_pkgs/data-view/view-presets/calendar/index.js +6 -0
  1197. package/dist/_pkgs/data-view/view-presets/calendar/layout.d.ts +37 -0
  1198. package/dist/_pkgs/data-view/view-presets/calendar/layout.js +138 -0
  1199. package/dist/_pkgs/data-view/view-presets/calendar/pc/actions.d.ts +7 -0
  1200. package/dist/_pkgs/data-view/view-presets/calendar/pc/actions.js +68 -0
  1201. package/dist/_pkgs/data-view/view-presets/calendar/pc/dnd.d.ts +32 -0
  1202. package/dist/_pkgs/data-view/view-presets/calendar/pc/dnd.js +159 -0
  1203. package/dist/_pkgs/data-view/view-presets/calendar/pc/effect.d.ts +1 -0
  1204. package/dist/_pkgs/data-view/view-presets/calendar/pc/effect.js +7 -0
  1205. package/dist/_pkgs/data-view/view-presets/calendar/pc/hit-test.d.ts +1 -0
  1206. package/dist/_pkgs/data-view/view-presets/calendar/pc/hit-test.js +28 -0
  1207. package/dist/_pkgs/data-view/view-presets/calendar/pc/styles.d.ts +1 -0
  1208. package/dist/_pkgs/data-view/view-presets/calendar/pc/styles.js +707 -0
  1209. package/dist/_pkgs/data-view/view-presets/calendar/pc/view.d.ts +89 -0
  1210. package/dist/_pkgs/data-view/view-presets/calendar/pc/view.js +837 -0
  1211. package/dist/_pkgs/data-view/view-presets/calendar/renderer.d.ts +2 -0
  1212. package/dist/_pkgs/data-view/view-presets/calendar/renderer.js +8 -0
  1213. package/dist/_pkgs/data-view/view-presets/calendar/source.d.ts +8 -0
  1214. package/dist/_pkgs/data-view/view-presets/calendar/source.js +3 -0
  1215. package/dist/_pkgs/data-view/view-presets/calendar/types.d.ts +79 -0
  1216. package/dist/_pkgs/data-view/view-presets/calendar/types.js +1 -0
  1217. package/dist/_pkgs/data-view/view-presets/convert.js +28 -0
  1218. package/dist/_pkgs/data-view/view-presets/effect.js +2 -0
  1219. package/dist/_pkgs/data-view/view-presets/index.d.ts +2 -0
  1220. package/dist/_pkgs/data-view/view-presets/index.js +3 -0
  1221. package/dist/_pkgs/data-view/view-presets/kanban/kanban-view-manager.js +4 -2
  1222. package/dist/_pkgs/data-view/view-presets/kanban/mobile/cell.js +2 -2
  1223. package/dist/_pkgs/data-view/view-presets/kanban/mobile/kanban-view-ui-logic.d.ts +1 -1
  1224. package/dist/_pkgs/data-view/view-presets/kanban/pc/cell.d.ts +1 -1
  1225. package/dist/_pkgs/data-view/view-presets/kanban/pc/controller/selection.d.ts +1 -1
  1226. package/dist/_pkgs/data-view/view-presets/table/collapsed-state.d.ts +18 -0
  1227. package/dist/_pkgs/data-view/view-presets/table/collapsed-state.js +36 -0
  1228. package/dist/_pkgs/data-view/view-presets/table/mobile/cell.js +3 -3
  1229. package/dist/_pkgs/data-view/view-presets/table/mobile/group.d.ts +11 -0
  1230. package/dist/_pkgs/data-view/view-presets/table/mobile/group.js +87 -4
  1231. package/dist/_pkgs/data-view/view-presets/table/mobile/table-view-ui-logic.d.ts +1 -1
  1232. package/dist/_pkgs/data-view/view-presets/table/pc/cell.d.ts +4 -1
  1233. package/dist/_pkgs/data-view/view-presets/table/pc/cell.js +12 -3
  1234. package/dist/_pkgs/data-view/view-presets/table/pc/controller/clipboard.js +1 -1
  1235. package/dist/_pkgs/data-view/view-presets/table/pc/controller/drag-to-fill.d.ts +1 -1
  1236. package/dist/_pkgs/data-view/view-presets/table/pc/controller/drag-to-fill.js +1 -1
  1237. package/dist/_pkgs/data-view/view-presets/table/pc/controller/hotkeys.d.ts +3 -2
  1238. package/dist/_pkgs/data-view/view-presets/table/pc/controller/hotkeys.js +40 -34
  1239. package/dist/_pkgs/data-view/view-presets/table/pc/controller/selection.d.ts +1 -1
  1240. package/dist/_pkgs/data-view/view-presets/table/pc/controller/selection.js +1 -1
  1241. package/dist/_pkgs/data-view/view-presets/table/pc/group.d.ts +10 -0
  1242. package/dist/_pkgs/data-view/view-presets/table/pc/group.js +85 -6
  1243. package/dist/_pkgs/data-view/view-presets/table/pc/header/column-header.js +1 -1
  1244. package/dist/_pkgs/data-view/view-presets/table/pc/menu.js +1 -1
  1245. package/dist/_pkgs/data-view/view-presets/table/pc/row/row-select-checkbox.d.ts +1 -1
  1246. package/dist/_pkgs/data-view/view-presets/table/pc/row/row-select-checkbox.js +1 -1
  1247. package/dist/_pkgs/data-view/view-presets/table/pc/row/row.d.ts +1 -1
  1248. package/dist/_pkgs/data-view/view-presets/table/pc/row/row.js +2 -2
  1249. package/dist/_pkgs/data-view/view-presets/table/pc/table-view-style.js +1 -1
  1250. package/dist/_pkgs/data-view/view-presets/table/pc/table-view-ui-logic.d.ts +1 -1
  1251. package/dist/_pkgs/data-view/view-presets/table/pc/table-view-ui-logic.js +1 -1
  1252. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/clipboard.js +1 -1
  1253. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/drag-to-fill.d.ts +1 -1
  1254. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/drag-to-fill.js +1 -1
  1255. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/drag.d.ts +2 -2
  1256. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/hotkeys.d.ts +3 -2
  1257. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/hotkeys.js +41 -35
  1258. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/selection.d.ts +2 -2
  1259. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/controller/selection.js +1 -1
  1260. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/effect.js +17 -17
  1261. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/bottom/group-footer.d.ts +2 -2
  1262. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/bottom/group-footer.js +2 -2
  1263. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/bottom/stats/column-stats-bar.d.ts +2 -2
  1264. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/bottom/stats/column-stats-bar.js +1 -1
  1265. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/bottom/stats/column-stats-column.d.ts +3 -3
  1266. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/bottom/stats/column-stats-column.js +2 -2
  1267. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-header.d.ts +2 -2
  1268. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-header.js +3 -3
  1269. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.d.ts +1 -1
  1270. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.js +20 -20
  1271. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/column-header-css.js +1 -1
  1272. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/column-header.d.ts +1 -1
  1273. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/column-header.js +4 -4
  1274. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/column-move-preview.d.ts +3 -3
  1275. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/number-format-bar.d.ts +1 -1
  1276. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/number-format-bar.js +1 -1
  1277. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/single-column-header.d.ts +3 -3
  1278. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/single-column-header.js +11 -11
  1279. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/vertical-indicator.d.ts +1 -1
  1280. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/header/vertical-indicator.js +2 -2
  1281. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/row/cell.d.ts +8 -5
  1282. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/row/cell.js +12 -4
  1283. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/row/menu.d.ts +2 -2
  1284. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/row/menu.js +1 -1
  1285. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/types.d.ts +1 -1
  1286. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/virtual/batch-task-manager.d.ts +1 -1
  1287. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/virtual/batch-task-manager.js +1 -1
  1288. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/virtual/virtual-scroll.d.ts +2 -2
  1289. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/virtual/virtual-scroll.js +2 -2
  1290. package/dist/_pkgs/data-view/view-presets/table/renderer.js +1 -1
  1291. package/dist/_pkgs/data-view/view-presets/table/table-view-manager.js +4 -2
  1292. package/dist/_pkgs/data-view/view-presets/table/utils.d.ts +4 -1
  1293. package/dist/_pkgs/data-view/view-presets/table/utils.js +17 -2
  1294. package/dist/_pkgs/data-view/widget-presets/tools/presets/view-options/view-options.js +3 -1
  1295. package/dist/_pkgs/global/gfx/curve.js +4 -3
  1296. package/dist/_pkgs/std/clipboard/clipboard.js +13 -3
  1297. package/dist/_pkgs/std/clipboard/index.d.ts +2 -2
  1298. package/dist/_pkgs/std/clipboard/index.js +2 -2
  1299. package/dist/_pkgs/std/clipboard/utils.d.ts +5 -0
  1300. package/dist/_pkgs/std/clipboard/utils.js +7 -0
  1301. package/dist/_pkgs/std/event/dispatcher.js +27 -1
  1302. package/dist/_pkgs/std/event/keymap.js +16 -6
  1303. package/dist/_pkgs/std/extension/command-registry.d.ts +321 -0
  1304. package/dist/_pkgs/std/extension/command-registry.js +233 -0
  1305. package/dist/_pkgs/std/extension/dnd/index.d.ts +7 -7
  1306. package/dist/_pkgs/std/extension/dnd/index.js +8 -8
  1307. package/dist/_pkgs/std/extension/dnd/types.d.ts +3 -3
  1308. package/dist/_pkgs/std/extension/editor-life-cycle.d.ts +2 -2
  1309. package/dist/_pkgs/std/extension/editor-life-cycle.js +1 -1
  1310. package/dist/_pkgs/std/extension/index.d.ts +2 -0
  1311. package/dist/_pkgs/std/extension/index.js +2 -0
  1312. package/dist/_pkgs/std/extension/translation-manifest.d.ts +49 -0
  1313. package/dist/_pkgs/std/extension/translation-manifest.js +71 -0
  1314. package/dist/_pkgs/std/gfx/controller.js +1 -1
  1315. package/dist/_pkgs/std/gfx/index.d.ts +7 -3
  1316. package/dist/_pkgs/std/gfx/index.js +6 -2
  1317. package/dist/_pkgs/std/gfx/interactivity/event.d.ts +1 -1
  1318. package/dist/_pkgs/std/gfx/interactivity/extension/view.d.ts +6 -6
  1319. package/dist/_pkgs/std/gfx/interactivity/gfx-view-event-handler.d.ts +1 -1
  1320. package/dist/_pkgs/std/gfx/interactivity/gfx-view-event-handler.js +7 -2
  1321. package/dist/_pkgs/std/gfx/interactivity/manager.d.ts +8 -0
  1322. package/dist/_pkgs/std/gfx/interactivity/manager.js +18 -0
  1323. package/dist/_pkgs/std/gfx/interactivity/resize/manager.d.ts +2 -2
  1324. package/dist/_pkgs/std/gfx/interactivity/types/clone.d.ts +1 -1
  1325. package/dist/_pkgs/std/gfx/interactivity/types/drag.d.ts +3 -3
  1326. package/dist/_pkgs/std/gfx/interactivity/types/resize.d.ts +2 -2
  1327. package/dist/_pkgs/std/gfx/interactivity/types/select.d.ts +1 -1
  1328. package/dist/_pkgs/std/gfx/layer.d.ts +1 -1
  1329. package/dist/_pkgs/std/gfx/layer.js +2 -3
  1330. package/dist/_pkgs/std/gfx/model/surface/decorators/field.js +9 -0
  1331. package/dist/_pkgs/std/gfx/model/surface/decorators/index.d.ts +2 -2
  1332. package/dist/_pkgs/std/gfx/model/surface/decorators/index.js +2 -2
  1333. package/dist/_pkgs/std/gfx/model/surface/decorators/local.d.ts +14 -0
  1334. package/dist/_pkgs/std/gfx/model/surface/decorators/local.js +22 -0
  1335. package/dist/_pkgs/std/gfx/model/surface/decorators/observer.d.ts +2 -0
  1336. package/dist/_pkgs/std/gfx/model/surface/decorators/observer.js +40 -1
  1337. package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +282 -0
  1338. package/dist/_pkgs/std/gfx/model/surface/element-model.js +437 -26
  1339. package/dist/_pkgs/std/gfx/model/surface/pivot.d.ts +48 -0
  1340. package/dist/_pkgs/std/gfx/model/surface/pivot.js +34 -0
  1341. package/dist/_pkgs/std/gfx/model/surface/role.d.ts +153 -0
  1342. package/dist/_pkgs/std/gfx/model/surface/role.js +115 -0
  1343. package/dist/_pkgs/std/gfx/model/surface/surface-model.d.ts +70 -0
  1344. package/dist/_pkgs/std/gfx/model/surface/surface-model.js +304 -17
  1345. package/dist/_pkgs/std/gfx/model/surface/tags.d.ts +77 -0
  1346. package/dist/_pkgs/std/gfx/model/surface/tags.js +246 -0
  1347. package/dist/_pkgs/std/gfx/perf.d.ts +10 -0
  1348. package/dist/_pkgs/std/gfx/perf.js +30 -0
  1349. package/dist/_pkgs/std/gfx/selection.js +1 -1
  1350. package/dist/_pkgs/std/gfx/tool/tool-controller.d.ts +13 -0
  1351. package/dist/_pkgs/std/gfx/tool/tool-controller.js +26 -0
  1352. package/dist/_pkgs/std/gfx/viewport-element.d.ts +3 -3
  1353. package/dist/_pkgs/std/gfx/viewport-element.js +5 -5
  1354. package/dist/_pkgs/std/gfx/viewport.d.ts +13 -1
  1355. package/dist/_pkgs/std/gfx/viewport.js +20 -2
  1356. package/dist/_pkgs/std/inline/extensions/index.d.ts +4 -4
  1357. package/dist/_pkgs/std/inline/extensions/index.js +4 -4
  1358. package/dist/_pkgs/std/inline/index.d.ts +8 -8
  1359. package/dist/_pkgs/std/inline/index.js +8 -8
  1360. package/dist/_pkgs/std/inline/range/active.js +1 -1
  1361. package/dist/_pkgs/std/inline/range/range-binding.js +1 -1
  1362. package/dist/_pkgs/std/inline/services/event.d.ts +2 -0
  1363. package/dist/_pkgs/std/inline/services/event.js +96 -21
  1364. package/dist/_pkgs/std/utils/layer.js +2 -6
  1365. package/dist/_pkgs/std/utils/tree.d.ts +14 -0
  1366. package/dist/_pkgs/std/utils/tree.js +59 -8
  1367. package/dist/_pkgs/std/view/element/gfx-block-component.js +23 -13
  1368. package/dist/_pkgs/store/extension/history/history-extension.d.ts +2 -2
  1369. package/dist/_pkgs/store/extension/history/history-extension.js +1 -1
  1370. package/dist/_pkgs/store/extension/history/index.d.ts +1 -1
  1371. package/dist/_pkgs/store/extension/history/index.js +1 -1
  1372. package/dist/_pkgs/store/extension/index.d.ts +6 -6
  1373. package/dist/_pkgs/store/extension/index.js +6 -6
  1374. package/dist/_pkgs/store/extension/schema.d.ts +2 -2
  1375. package/dist/_pkgs/store/extension/selection/base.d.ts +1 -1
  1376. package/dist/_pkgs/store/extension/selection/identifier.d.ts +2 -2
  1377. package/dist/_pkgs/store/extension/selection/index.d.ts +4 -4
  1378. package/dist/_pkgs/store/extension/selection/index.js +4 -4
  1379. package/dist/_pkgs/store/extension/selection/selection-extension.d.ts +3 -3
  1380. package/dist/_pkgs/store/extension/selection/selection-extension.js +3 -3
  1381. package/dist/_pkgs/store/extension/selection/types.d.ts +1 -1
  1382. package/dist/_pkgs/store/extension/store-extension.d.ts +2 -2
  1383. package/dist/_pkgs/store/extension/store-extension.js +2 -2
  1384. package/dist/_pkgs/store/extension/workspace/doc.d.ts +2 -2
  1385. package/dist/_pkgs/store/extension/workspace/index.d.ts +3 -3
  1386. package/dist/_pkgs/store/extension/workspace/index.js +3 -3
  1387. package/dist/_pkgs/store/extension/workspace/workspace-meta.d.ts +10 -0
  1388. package/dist/_pkgs/store/extension/workspace/workspace.d.ts +1 -1
  1389. package/dist/_pkgs/store/impls/doc.d.ts +45 -0
  1390. package/dist/_pkgs/store/impls/doc.js +110 -0
  1391. package/dist/_pkgs/store/impls/index.d.ts +4 -0
  1392. package/dist/_pkgs/store/impls/index.js +4 -0
  1393. package/dist/_pkgs/store/impls/workspace-base.d.ts +81 -0
  1394. package/dist/_pkgs/store/impls/workspace-base.js +143 -0
  1395. package/dist/_pkgs/store/impls/workspace-meta.d.ts +33 -0
  1396. package/dist/_pkgs/store/impls/workspace-meta.js +125 -0
  1397. package/dist/_pkgs/store/impls/workspace.d.ts +14 -0
  1398. package/dist/_pkgs/store/impls/workspace.js +14 -0
  1399. package/dist/_pkgs/store/index.d.ts +9 -8
  1400. package/dist/_pkgs/store/index.js +9 -8
  1401. package/dist/_pkgs/store/model/block/flat-sync-controller.js +2 -12
  1402. package/dist/_pkgs/store/model/store/crud.js +7 -4
  1403. package/dist/_pkgs/store/model/store/identifier.d.ts +1 -1
  1404. package/dist/_pkgs/store/model/store-container.d.ts +2 -2
  1405. package/dist/_pkgs/store/model/store-container.js +2 -2
  1406. package/dist/_pkgs/store/reactive/base-reactive-data.d.ts +1 -1
  1407. package/dist/_pkgs/store/reactive/flat-native-y/index.d.ts +4 -4
  1408. package/dist/_pkgs/store/reactive/flat-native-y/index.js +31 -9
  1409. package/dist/_pkgs/store/reactive/flat-native-y/initialize.d.ts +2 -2
  1410. package/dist/_pkgs/store/reactive/flat-native-y/initialize.js +3 -3
  1411. package/dist/_pkgs/store/reactive/flat-native-y/proxy.d.ts +2 -2
  1412. package/dist/_pkgs/store/reactive/flat-native-y/proxy.js +5 -5
  1413. package/dist/_pkgs/store/reactive/flat-native-y/signal-updater.d.ts +1 -1
  1414. package/dist/_pkgs/store/reactive/flat-native-y/signal-updater.js +1 -1
  1415. package/dist/_pkgs/store/reactive/flat-native-y/types.d.ts +1 -1
  1416. package/dist/_pkgs/store/reactive/flat-native-y/utils.d.ts +1 -1
  1417. package/dist/_pkgs/store/reactive/flat-native-y/utils.js +3 -3
  1418. package/dist/_pkgs/store/reactive/flat-native-y/y-event-handler.d.ts +1 -1
  1419. package/dist/_pkgs/store/reactive/flat-native-y/y-event-handler.js +2 -2
  1420. package/dist/_pkgs/store/reactive/flat-native-y/y-map-updater.d.ts +1 -1
  1421. package/dist/_pkgs/store/reactive/flat-native-y/y-map-updater.js +3 -3
  1422. package/dist/_pkgs/store/reactive/memory.d.ts +1 -1
  1423. package/dist/_pkgs/store/reactive/stash-pop.js +1 -1
  1424. package/dist/_pkgs/store/reactive/text/index.d.ts +3 -3
  1425. package/dist/_pkgs/store/reactive/text/index.js +3 -3
  1426. package/dist/_pkgs/store/reactive/text/text.d.ts +2 -2
  1427. package/dist/_pkgs/store/reactive/text/types.d.ts +1 -1
  1428. package/dist/_pkgs/store/test/test-doc.d.ts +1 -45
  1429. package/dist/_pkgs/store/test/test-doc.js +1 -110
  1430. package/dist/_pkgs/store/test/test-meta.d.ts +1 -33
  1431. package/dist/_pkgs/store/test/test-meta.js +1 -125
  1432. package/dist/_pkgs/store/test/test-workspace.d.ts +8 -68
  1433. package/dist/_pkgs/store/test/test-workspace.js +7 -132
  1434. package/dist/_pkgs/store/transformer/assets.d.ts +2 -2
  1435. package/dist/_pkgs/store/transformer/base.d.ts +5 -5
  1436. package/dist/_pkgs/store/transformer/base.js +4 -4
  1437. package/dist/_pkgs/store/transformer/index.d.ts +7 -7
  1438. package/dist/_pkgs/store/transformer/index.js +7 -7
  1439. package/dist/_pkgs/store/transformer/json.js +4 -4
  1440. package/dist/_pkgs/store/transformer/slice.d.ts +1 -1
  1441. package/dist/_pkgs/store/transformer/slice.js +1 -1
  1442. package/dist/_pkgs/store/transformer/type.d.ts +2 -2
  1443. package/dist/_pkgs/sync/doc/peer.js +1 -1
  1444. package/dist/commands.d.ts +58 -0
  1445. package/dist/commands.js +66 -0
  1446. package/dist/extensions/command-telemetry.d.ts +21 -0
  1447. package/dist/extensions/command-telemetry.js +89 -0
  1448. package/dist/extensions/store.d.ts +9 -4
  1449. package/dist/extensions/store.js +27 -24
  1450. package/dist/extensions/view.d.ts +9 -4
  1451. package/dist/extensions/view.js +23 -8
  1452. package/dist/flags.d.ts +98 -21
  1453. package/dist/flags.js +103 -22
  1454. package/dist/frameworks.d.ts +45 -0
  1455. package/dist/frameworks.js +154 -0
  1456. package/dist/schemas.d.ts +8 -5
  1457. package/dist/schemas.js +28 -32
  1458. package/dist/shortcuts.d.ts +16 -16
  1459. package/dist/shortcuts.js +8 -36
  1460. package/dist/translations.d.ts +56 -0
  1461. package/dist/translations.js +112 -0
  1462. package/package.json +16 -4
  1463. package/dist/_pkgs/affine-block-root/keyboard/shortcuts.d.ts +0 -8
  1464. package/dist/_pkgs/affine-block-root/keyboard/shortcuts.js +0 -48
  1465. package/dist/_pkgs/affine-gfx-shape/shortcuts.d.ts +0 -6
  1466. package/dist/_pkgs/affine-gfx-shape/shortcuts.js +0 -28
  1467. package/dist/_pkgs/data-view/core/utils/cache.d.ts +0 -5
  1468. package/dist/_pkgs/data-view/core/utils/cache.js +0 -28
@@ -0,0 +1,3009 @@
1
+ import { createIdentifier } from '../../global/di/index.js';
2
+ import { Bound, lineIntersects } from '../../global/gfx/index.js';
3
+ import { GfxGroupLikeElementModel, GfxPrimitiveElementModel, InteractivityExtension, roleIsA, } from '../../std/gfx/index.js';
4
+ import { effect, signal } from '@preact/signals-core';
5
+ import { backgroundAxisFact, backgroundTransitionBands, backgroundTransitionVisibleProps, } from '../framework-background/facts.js';
6
+ import { Overlay, OverlayIdentifier } from '../renderer/overlay.js';
7
+ import { QualityNudgeIdentifier } from './map-quality.js';
8
+ import { ViolationTimeline } from './violation-timeline.js';
9
+ /**
10
+ * Severity `profile` gives `rule` — the rule's own when the profile is silent
11
+ * about it, or when there is no profile at all.
12
+ */
13
+ export function profileSeverity(rule, profile) {
14
+ // Own keys only: `rules` is an object literal shipped by a framework, so
15
+ // `rules['constructor']` must not resolve to something off the prototype.
16
+ return profile !== undefined && Object.hasOwn(profile.rules, rule.id)
17
+ ? profile.rules[rule.id]
18
+ : rule.severity;
19
+ }
20
+ /**
21
+ * The profile a background of `framework` is checked against when it names
22
+ * none: the one flagged {@link ValidationProfile.isDefault}, or — failing that
23
+ * — the first one registered, so a framework that forgot the flag still has a
24
+ * resolvable answer instead of silently losing its profiles.
25
+ */
26
+ export function defaultProfileOf(profiles, framework) {
27
+ let first;
28
+ for (const profile of profiles) {
29
+ if (profile.framework !== framework)
30
+ continue;
31
+ if (profile.isDefault)
32
+ return profile;
33
+ first ??= profile;
34
+ }
35
+ return first;
36
+ }
37
+ function indexProfiles(profiles) {
38
+ const byId = new Map();
39
+ const defaults = new Map();
40
+ for (const profile of profiles) {
41
+ byId.set(profile.id, profile);
42
+ if (defaults.has(profile.framework))
43
+ continue;
44
+ const fallback = defaultProfileOf(profiles, profile.framework);
45
+ if (fallback)
46
+ defaults.set(profile.framework, fallback);
47
+ }
48
+ return { byId, defaults };
49
+ }
50
+ /**
51
+ * The profile a finding of `rule` is judged by: the one its background NAMES,
52
+ * or the framework's default.
53
+ *
54
+ * A profile id belonging to another framework is ignored rather than honoured:
55
+ * a background carrying `bpmn.executable` says nothing about a Wardley rule,
56
+ * and a stale id left behind by a paste must never silence one.
57
+ */
58
+ function resolveProfile(rule, chosenId, index) {
59
+ const named = chosenId === undefined ? undefined : index.byId.get(chosenId);
60
+ if (named !== undefined && named.framework === rule.framework)
61
+ return named;
62
+ return index.defaults.get(rule.framework);
63
+ }
64
+ /**
65
+ * The profile id each role-carrying element names, in one pass.
66
+ *
67
+ * Role-carrying only: a profile is a decision about a framework's background,
68
+ * and a neutral element cannot be one. That keeps the pass on exactly the same
69
+ * cheap guard the rule families already use.
70
+ */
71
+ function readChosenProfiles(elements) {
72
+ const chosen = new Map();
73
+ for (const el of elements) {
74
+ if (el.role === undefined)
75
+ continue;
76
+ const id = el.validationProfile;
77
+ // Whatever a peer wrote: a client that got it wrong must not break
78
+ // evaluation on this one.
79
+ if (typeof id === 'string')
80
+ chosen.set(el.id, id);
81
+ }
82
+ return chosen;
83
+ }
84
+ /**
85
+ * Whether `rule` is `'off'` under EVERY profile in play on this surface — in
86
+ * which case it is not evaluated at all, which is what `'off'` is for.
87
+ *
88
+ * The default counts as in play unconditionally: a background naming nothing
89
+ * falls back to it, and the surface can gain one at any moment. So the
90
+ * short-circuit only fires when the answer cannot change, and errs towards
91
+ * evaluating — a missed skip costs a linear pass, a wrong skip costs a silent
92
+ * rule.
93
+ */
94
+ function isRuleSilent(rule, chosen, index) {
95
+ if (profileSeverity(rule, index.defaults.get(rule.framework)) !== 'off') {
96
+ return false;
97
+ }
98
+ for (const id of chosen.values()) {
99
+ const profile = index.byId.get(id);
100
+ // Unknown id, or another framework's: falls back to the default, already
101
+ // known to be off.
102
+ if (profile === undefined || profile.framework !== rule.framework)
103
+ continue;
104
+ if (profileSeverity(rule, profile) !== 'off')
105
+ return false;
106
+ }
107
+ return true;
108
+ }
109
+ /**
110
+ * Re-judge what a rule raised, background by background: rewrite each finding's
111
+ * severity to what its own profile says, and drop the ones the profile turned
112
+ * off.
113
+ *
114
+ * After the family, never before: only the family knows which background a
115
+ * finding was measured against ({@link Violation.backgroundId}), and that is
116
+ * precisely what names the profile. A family measuring against no background
117
+ * has no instance to read a choice from and falls back to the default.
118
+ */
119
+ function applyProfiles(rule, raised, chosen, index) {
120
+ const kept = [];
121
+ for (const violation of raised) {
122
+ const chosenId = violation.backgroundId === undefined
123
+ ? undefined
124
+ : chosen.get(violation.backgroundId);
125
+ const severity = profileSeverity(rule, resolveProfile(rule, chosenId, index));
126
+ if (severity === 'off')
127
+ continue;
128
+ violation.severity = severity;
129
+ kept.push(violation);
130
+ }
131
+ return kept;
132
+ }
133
+ /**
134
+ * Complain once per distinct problem. A family runs on every evaluation, so a
135
+ * bare `console.warn` about a malformed rule would fill the console at 8 Hz
136
+ * while somebody drags.
137
+ */
138
+ const warned = new Set();
139
+ function warnOnce(reason) {
140
+ if (warned.has(reason))
141
+ return;
142
+ warned.add(reason);
143
+ console.warn(`[validation] ${reason}`);
144
+ }
145
+ /**
146
+ * Every background instance on the surface carrying the rule's frame role.
147
+ *
148
+ * Empty when the rule declares no frame role, and empty on a board whose maps
149
+ * were all authored before that role existed — which is what makes an old
150
+ * document stay a sketch instead of lighting up.
151
+ */
152
+ function backgroundsOf(rule, elements) {
153
+ const backgroundRole = rule.backgroundRole;
154
+ if (backgroundRole === undefined)
155
+ return [];
156
+ const backgrounds = [];
157
+ for (const el of elements) {
158
+ if (el.role === undefined)
159
+ continue;
160
+ if (roleIsA(el.role, backgroundRole, rule.roles)) {
161
+ backgrounds.push({
162
+ id: el.id,
163
+ bound: el.elementBound,
164
+ props: el,
165
+ });
166
+ }
167
+ }
168
+ return backgrounds;
169
+ }
170
+ /**
171
+ * Every element on the surface acting as a framework background for a REAL-TIME
172
+ * rule.
173
+ *
174
+ * Recorded by the manager after each evaluation, and read on the next one — the
175
+ * one fact about a deleted background that nothing else can reconstruct. See
176
+ * {@link ValidationManager._backgrounds}.
177
+ *
178
+ * ## Why the moment is filtered here too
179
+ *
180
+ * This runs on the gesture path, once per evaluation, and it is a FULL surface
181
+ * walk per rule. `evaluateRules` already refuses to walk an on-demand rule, so
182
+ * leaving it in here would have handed the drawing budget back exactly what the
183
+ * second moment took away from it — measured at +58 % on the manager's tick with
184
+ * the two Wardley check-up rules registered, and invisible to a bench that times
185
+ * `evaluateRules` alone.
186
+ *
187
+ * It is also the right answer and not merely the cheap one: `_backgrounds`
188
+ * exists to decide whether an incremental REAL-TIME pass must fall back to a
189
+ * full sweep. An on-demand rule never takes part in that pass, so the frames it
190
+ * measures against have nothing to invalidate — and a framework whose only rules
191
+ * are on-demand has no incremental pass to protect in the first place.
192
+ */
193
+ export function backgroundElementIds(rules, elements) {
194
+ const ids = new Set();
195
+ for (const rule of rules) {
196
+ if (!isRealtime(rule))
197
+ continue;
198
+ for (const background of backgroundsOf(rule, elements))
199
+ ids.add(background.id);
200
+ }
201
+ return ids;
202
+ }
203
+ /**
204
+ * The instance a finding is attributed to: the one that CONTAINS the subject,
205
+ * failing that the nearest by edge-to-edge gap.
206
+ *
207
+ * Same question `evaluateElementInBackground` answers inline, asked by the
208
+ * families whose subjects normally ARE on the map — an inertia bar, an arrow, a
209
+ * pair of overlapping labels. Ties go to the smaller id so a persisted
210
+ * arbitration never depends on the order a `Y.Map` was rebuilt in.
211
+ */
212
+ function attributeBackground(bound, backgrounds) {
213
+ let nearest = null;
214
+ let nearestDistance = Infinity;
215
+ for (const background of backgrounds) {
216
+ if (background.bound.contains(bound))
217
+ return background;
218
+ const distance = gapSquared(background.bound, bound);
219
+ if (distance < nearestDistance ||
220
+ (distance === nearestDistance &&
221
+ nearest !== null &&
222
+ background.id < nearest.id)) {
223
+ nearestDistance = distance;
224
+ nearest = background;
225
+ }
226
+ }
227
+ return nearest;
228
+ }
229
+ /**
230
+ * Build the finding of `rule` against `elementIds`. One place, so every family
231
+ * carries the rule's keys, its fallbacks and its background attribution the
232
+ * same way.
233
+ *
234
+ * `words` is how a family with several ways of failing says WHICH one did: the
235
+ * finding is still the rule's — same id, same severity, same arbitration — but
236
+ * the sentence the user reads is the one that fits their mistake.
237
+ */
238
+ function raise(rule, elementIds, backgroundId, words = rule) {
239
+ return {
240
+ ruleId: rule.id,
241
+ elementIds,
242
+ severity: rule.severity,
243
+ messageKey: words.messageKey,
244
+ ...(words.messageFallback !== undefined
245
+ ? { messageFallback: words.messageFallback }
246
+ : {}),
247
+ ...(words.suggestionKey ? { suggestion: words.suggestionKey } : {}),
248
+ ...(words.suggestionFallback !== undefined
249
+ ? { suggestionFallback: words.suggestionFallback }
250
+ : {}),
251
+ ...(backgroundId !== undefined ? { backgroundId } : {}),
252
+ };
253
+ }
254
+ /**
255
+ * "Is this element on the framework's background?"
256
+ *
257
+ * An element carrying `rule.appliesTo` (or a specialisation of it) whose bounds
258
+ * are not fully contained by any element carrying `rule.backgroundRole` is in
259
+ * violation. BOTH sides are roles — the engine never reads a shape type.
260
+ *
261
+ * When the surface carries NO background with that role there is no map to be
262
+ * outside of, so the rule yields nothing: a Wardley node dropped on a blank
263
+ * canvas is a sketch, not an error — and so is a map authored before the role
264
+ * existed.
265
+ *
266
+ * Cost: two linear passes, one `elementBound` per candidate. Backgrounds are
267
+ * counted in units, so the per-element cost is constant in practice.
268
+ */
269
+ function evaluateElementInBackground(rule, elements) {
270
+ const subjectRole = rule.appliesTo;
271
+ if (subjectRole === undefined)
272
+ return [];
273
+ const backgrounds = backgroundsOf(rule, elements);
274
+ if (backgrounds.length === 0)
275
+ return [];
276
+ const violations = [];
277
+ for (const el of elements) {
278
+ // Cheapest possible exit for a neutral element: no role, no evaluation.
279
+ if (el.role === undefined)
280
+ continue;
281
+ if (!roleIsA(el.role, subjectRole, rule.roles))
282
+ continue;
283
+ const bound = el.elementBound;
284
+ // One pass, and it answers two questions at once: is this element on ANY
285
+ // map (in which case there is nothing to report), and if not, which map is
286
+ // it nearest to. The second answer is the finding's `backgroundId`, and it
287
+ // has to be taken here — a board carries several maps, and by the time the
288
+ // violation reaches the UI there is no way left to tell which one the user
289
+ // was working on.
290
+ let nearest = null;
291
+ let nearestDistance = Infinity;
292
+ let framed = false;
293
+ for (const background of backgrounds) {
294
+ if (background.bound.contains(bound)) {
295
+ framed = true;
296
+ break;
297
+ }
298
+ const distance = gapSquared(background.bound, bound);
299
+ // Strictly nearer wins; an exact tie is broken by the smaller id, never
300
+ // by which background the surface happened to be walked in first.
301
+ // `backgroundId` decides where a PERSISTED decision gets written and read
302
+ // back, so it cannot depend on the iteration order of a Map rebuilt from
303
+ // a Y.Map on every load and every merge.
304
+ if (distance < nearestDistance ||
305
+ (distance === nearestDistance &&
306
+ nearest !== null &&
307
+ background.id < nearest.id)) {
308
+ nearestDistance = distance;
309
+ nearest = background;
310
+ }
311
+ }
312
+ if (framed)
313
+ continue;
314
+ violations.push(raise(rule, [el.id], nearest?.id));
315
+ }
316
+ return violations;
317
+ }
318
+ /**
319
+ * Squared width of the GAP between two bounds — zero when they touch or
320
+ * overlap, otherwise the distance from edge to edge. Squared because it is only
321
+ * ever compared against another one, so the square root would buy nothing and
322
+ * cost a call per background per violating element.
323
+ *
324
+ * Edges, not centres. "Nearest centre" reads plausibly and is wrong as soon as
325
+ * the maps differ in size: a component 20 units off the right edge of a large
326
+ * map is 900 units from its centre, so a small map 60 units further away wins —
327
+ * and the element gets attributed to a map it is nowhere near. Comparing gaps
328
+ * gives the answer the eye gives, whatever the two maps measure.
329
+ *
330
+ * A background that CONTAINS the element never reaches this: containment exits
331
+ * the loop first, and a framed element raises nothing at all.
332
+ */
333
+ function gapSquared(background, bound) {
334
+ const dx = Math.max(background.x - bound.maxX, bound.x - background.maxX, 0);
335
+ const dy = Math.max(background.y - bound.maxY, bound.y - background.maxY, 0);
336
+ return dx * dx + dy * dy;
337
+ }
338
+ /**
339
+ * How many points a CURVED segment is sampled into. A Wardley map is ~1600
340
+ * units wide and the tightest tolerance any rule declares is 24, so 32 chords
341
+ * put the sampling error two orders of magnitude below the thing being
342
+ * measured. Paid only by an element that actually carries tangents.
343
+ */
344
+ const CURVE_SAMPLES = 32;
345
+ /** A point on the cubic Bézier `p0 → p1` with control points `c0`, `c1`. */
346
+ function cubicAt(p0, c0, c1, p1, t) {
347
+ const u = 1 - t;
348
+ const a = u * u * u;
349
+ const b = 3 * u * u * t;
350
+ const c = 3 * u * t * t;
351
+ const d = t * t * t;
352
+ return [
353
+ a * p0[0] + b * c0[0] + c * c1[0] + d * p1[0],
354
+ a * p0[1] + b * c0[1] + c * c1[1] + d * p1[1],
355
+ ];
356
+ }
357
+ /** Anything but a rounding difference away from `p`. */
358
+ function departsFrom(candidate, p) {
359
+ return (isPoint(candidate) &&
360
+ (Math.abs(candidate[0] - p[0]) > 1e-6 || Math.abs(candidate[1] - p[1]) > 1e-6));
361
+ }
362
+ /**
363
+ * The POLYLINE a directional element exposes, in absolute model coordinates —
364
+ * `null` for anything that is not one.
365
+ *
366
+ * Duck-typed on purpose. The engine must not import a connector model: it knows
367
+ * roles and geometry, and "an element that runs from here to there" is
368
+ * geometry. Two sources, in order:
369
+ *
370
+ * 1. `absolutePath` — the ROUTED path, which is what the user sees and what a
371
+ * right-angled connector actually traces;
372
+ * 2. the free endpoints of an unattached edge, for an element the layout has
373
+ * not routed yet (a freshly pasted connector, a fixture in a unit test).
374
+ *
375
+ * An edge attached at both ends and never laid out has neither, and gets no
376
+ * verdict at all — silence, not a guess.
377
+ *
378
+ * ## Curved edges
379
+ *
380
+ * A routed path is not always a polyline. A curved connector stores its whole
381
+ * shape in TWO points carrying tangents (`absOut` on the first, `absIn` on the
382
+ * second) — so reading the bare coordinates gives the CHORD, and a bar sitting
383
+ * exactly on the drawn curve reads as 150 units off it. Wardley draws straight
384
+ * connectors, but the connector toolbar lets the mode be changed, and a rule
385
+ * that is confidently wrong about what the user can plainly see is worse than
386
+ * one that says nothing.
387
+ *
388
+ * Detected by geometry, not by mode: a point whose tangent departs from itself
389
+ * is a curve, whatever the element type calls it. The segment is then sampled
390
+ * ({@link CURVE_SAMPLES}) so every downstream test — distance, intersection —
391
+ * measures the drawn shape.
392
+ */
393
+ function elementPath(el) {
394
+ const edge = el;
395
+ const raw = edge.absolutePath;
396
+ if (Array.isArray(raw) && raw.length >= 2) {
397
+ const anchors = [];
398
+ for (const point of raw) {
399
+ const x = point?.[0];
400
+ const y = point?.[1];
401
+ if (typeof x !== 'number' || typeof y !== 'number')
402
+ return null;
403
+ anchors.push([x, y]);
404
+ }
405
+ const points = [anchors[0]];
406
+ for (let i = 1; i < anchors.length; i++) {
407
+ const from = anchors[i - 1];
408
+ const to = anchors[i];
409
+ const out = raw[i - 1]?.absOut;
410
+ const into = raw[i]?.absIn;
411
+ // Straight unless a tangent says otherwise — the common case, and the
412
+ // one that must not pay for the other.
413
+ if (!departsFrom(out, from) && !departsFrom(into, to)) {
414
+ points.push(to);
415
+ continue;
416
+ }
417
+ const c0 = isPoint(out) ? out : from;
418
+ const c1 = isPoint(into) ? into : to;
419
+ for (let s = 1; s <= CURVE_SAMPLES; s++) {
420
+ points.push(cubicAt(from, c0, c1, to, s / CURVE_SAMPLES));
421
+ }
422
+ }
423
+ return points;
424
+ }
425
+ const from = edge.source?.position;
426
+ const to = edge.target?.position;
427
+ if (isPoint(from) && isPoint(to))
428
+ return [from, to];
429
+ return null;
430
+ }
431
+ function isPoint(value) {
432
+ return (Array.isArray(value) &&
433
+ value.length >= 2 &&
434
+ typeof value[0] === 'number' &&
435
+ typeof value[1] === 'number');
436
+ }
437
+ /**
438
+ * Which way a directional element RUNS: the unit vector from where it starts to
439
+ * where it ends.
440
+ *
441
+ * End to end, not segment by segment: an elbowed arrow wanders, but what it
442
+ * says is where it came from and where it points. `null` for an element with no
443
+ * path, or one whose ends coincide — a zero-length arrow means nothing and is
444
+ * not a mistake worth a message.
445
+ */
446
+ function elementDirection(el) {
447
+ const path = elementPath(el);
448
+ if (path === null)
449
+ return null;
450
+ const [sx, sy] = path[0];
451
+ const [ex, ey] = path[path.length - 1];
452
+ const dx = ex - sx;
453
+ const dy = ey - sy;
454
+ const length = Math.hypot(dx, dy);
455
+ if (length === 0)
456
+ return null;
457
+ return [dx / length, dy / length];
458
+ }
459
+ /** Squared distance from `p` to the segment `a…b`. */
460
+ function pointSegmentDistanceSquared(p, a, b) {
461
+ const vx = b[0] - a[0];
462
+ const vy = b[1] - a[1];
463
+ const lengthSquared = vx * vx + vy * vy;
464
+ let t = 0;
465
+ if (lengthSquared > 0) {
466
+ t = ((p[0] - a[0]) * vx + (p[1] - a[1]) * vy) / lengthSquared;
467
+ t = t < 0 ? 0 : t > 1 ? 1 : t;
468
+ }
469
+ const dx = p[0] - (a[0] + t * vx);
470
+ const dy = p[1] - (a[1] + t * vy);
471
+ return dx * dx + dy * dy;
472
+ }
473
+ /** Squared distance from `p` to the nearest point of the polyline `path`. */
474
+ function pointPathDistanceSquared(p, path) {
475
+ let best = Infinity;
476
+ for (let i = 1; i < path.length; i++) {
477
+ const d = pointSegmentDistanceSquared(p, path[i - 1], path[i]);
478
+ if (d < best)
479
+ best = d;
480
+ }
481
+ return best;
482
+ }
483
+ /** The centre of a bound, as a point. */
484
+ function centreOf(bound) {
485
+ return [bound.x + bound.w / 2, bound.y + bound.h / 2];
486
+ }
487
+ /**
488
+ * "Does this arrow run against the axis?"
489
+ *
490
+ * The subject's direction is confronted with the declared FORWARD sense of one
491
+ * axis of the frame it sits on (PF5.15). The verdict is an angle and nothing
492
+ * else: no shape type, no colour, no element type — an arrow is a subject
493
+ * because it carries the role, and it is wrong because of where it points.
494
+ *
495
+ * A subject with no direction ({@link elementDirection}) and a frame that
496
+ * declares no such axis both yield silence rather than a guess.
497
+ *
498
+ * Cost: one `elementBound` and one path read per subject, plus the frame
499
+ * attribution. Constant per element — the frames are counted in units.
500
+ */
501
+ function evaluateOrientationAgainstAxis(rule, elements) {
502
+ const subjectRole = rule.appliesTo;
503
+ const against = rule.against;
504
+ const def = rule.background;
505
+ if (subjectRole === undefined || against === undefined || def === undefined) {
506
+ return [];
507
+ }
508
+ const axis = backgroundAxisFact(def, against.axis);
509
+ if (axis === undefined)
510
+ return [];
511
+ const backgrounds = backgroundsOf(rule, elements);
512
+ // No frame on the board, no sense to run against: a lone arrow is a sketch.
513
+ if (backgrounds.length === 0)
514
+ return [];
515
+ // Beyond this angle from the axis' forward sense, the subject runs against
516
+ // it. Perpendicular (90°) plus the declared dead zone.
517
+ const limit = Math.cos(((90 + against.toleranceDeg) * Math.PI) / 180);
518
+ const violations = [];
519
+ for (const el of elements) {
520
+ if (el.role === undefined)
521
+ continue;
522
+ if (!roleIsA(el.role, subjectRole, rule.roles))
523
+ continue;
524
+ const direction = elementDirection(el);
525
+ if (direction === null)
526
+ continue;
527
+ const alignment = direction[0] * axis.forward[0] + direction[1] * axis.forward[1];
528
+ if (alignment >= limit)
529
+ continue;
530
+ violations.push(raise(rule, [el.id], attributeBackground(el.elementBound, backgrounds)?.id));
531
+ }
532
+ return violations;
533
+ }
534
+ /**
535
+ * "Is this straddling what it is supposed to be straddling — and, if the rule
536
+ * asks, posed on what it is supposed to be posed on?"
537
+ *
538
+ * The subject must STRADDLE one of the frame's declared transitions: its own
539
+ * extent along `boundaryAxis` has to intersect the transition band
540
+ * ({@link backgroundTransitionBands}), which is what "superimposed on the
541
+ * dashed line" is as geometry (see {@link AttachmentDef.boundaryAxis}).
542
+ *
543
+ * A rule may ALSO name a carrier (PF5.16), and then the subject must sit within
544
+ * `tolerance` of an element carrying that role — an edge, measured along its
545
+ * path. That half is optional, and Wardley's W2 stopped declaring it on the PO
546
+ * recette of 02/08/2026: a bar on a divider with no dependency under it is a
547
+ * legitimate map, not a mistake.
548
+ *
549
+ * One finding, and it says which half failed. Two requirements are still a
550
+ * single badge on a single symbol — two would be pedantry on a bar eight units
551
+ * wide — but they are two different mistakes with two different gestures to fix
552
+ * them, so the finding carries the words of the one that actually failed. When
553
+ * BOTH fail it reports the carrier: a symbol attached to nothing has to find
554
+ * something to be about before where it sits can mean anything.
555
+ *
556
+ * A rule that names a carrier stays silent while the board carries none: a map
557
+ * with no dependency yet is a map being drawn, not a map with a misplaced
558
+ * symbol.
559
+ *
560
+ * ## The boundary half asks nothing while the boundary is hidden
561
+ *
562
+ * The frontiers are read off the INSTANCE, not off the declaration alone
563
+ * ({@link backgroundTransitionBands}): a framework that lets the user switch its
564
+ * graduations off names the prop that shows them, and a frame with them off
565
+ * yields no band, so every subject on it passes.
566
+ *
567
+ * Proportionality, and the PO's own wording (25/08/2026): a user who has hidden
568
+ * the dashed columns is looking at a plain card, and telling them a bar is not
569
+ * astride a line they cannot see is a finding they cannot act on — the gesture
570
+ * it asks for would aim at nothing. Judge the drawing that is on the canvas,
571
+ * not the one the declaration could have painted. The rule is not weakened, it
572
+ * is scoped: turn the columns back on and every verdict comes back, on the same
573
+ * bars, in the same places.
574
+ */
575
+ function evaluateAttachment(rule, elements) {
576
+ const subjectRole = rule.appliesTo;
577
+ const attachment = rule.attachment;
578
+ if (subjectRole === undefined || attachment === undefined)
579
+ return [];
580
+ const carrierRole = attachment.carrierRole;
581
+ // A carrier is something with a PATH. Declaring anything else here produces a
582
+ // rule that can never fire; say so once instead of shrugging.
583
+ const carrierKind = carrierRole === undefined ? undefined : rule.roles[carrierRole]?.kind;
584
+ if (carrierKind !== undefined && carrierKind !== 'edge') {
585
+ warnOnce(`attachment rule "${rule.id}" names a "${carrierKind}" role ` +
586
+ `("${carrierRole}") as its carrier — only an "edge" role ` +
587
+ `has a path, and "posed on" is a distance to one, so this rule can ` +
588
+ `never fire.`);
589
+ return [];
590
+ }
591
+ const carriers = [];
592
+ const subjects = [];
593
+ for (const el of elements) {
594
+ if (el.role === undefined)
595
+ continue;
596
+ if (carrierRole !== undefined && roleIsA(el.role, carrierRole, rule.roles)) {
597
+ const path = elementPath(el);
598
+ if (path !== null)
599
+ carriers.push(path);
600
+ }
601
+ else if (roleIsA(el.role, subjectRole, rule.roles)) {
602
+ subjects.push(el);
603
+ }
604
+ }
605
+ if (subjects.length === 0)
606
+ return [];
607
+ if (carrierRole !== undefined && carriers.length === 0)
608
+ return [];
609
+ const backgrounds = backgroundsOf(rule, elements);
610
+ const tolerance = attachment.tolerance ?? 0;
611
+ const toleranceSquared = tolerance * tolerance;
612
+ // The transition test needs a frame to read the bands off, so a rule asking
613
+ // for one on a board with no frame simply does not ask.
614
+ const def = rule.background;
615
+ let axis = attachment.boundaryAxis !== undefined && def !== undefined
616
+ ? backgroundAxisFact(def, attachment.boundaryAxis)
617
+ : undefined;
618
+ if (axis !== undefined && def?.transitionBandWidth === undefined) {
619
+ // A width the frame never declared is not a width the engine may invent:
620
+ // guessing one would indict every subject on the board over a number
621
+ // nobody wrote down.
622
+ warnOnce(`attachment rule "${rule.id}" asks for the "${attachment.boundaryAxis}" ` +
623
+ `transitions, but its background declares no "transitionBandWidth" — ` +
624
+ `the boundary half of this rule is not evaluated.`);
625
+ axis = undefined;
626
+ }
627
+ // Bands are a function of the frame's bounds, and a board carries units of
628
+ // frames against hundreds of subjects: read once per frame, not per bar.
629
+ const bandsOf = new Map();
630
+ const violations = [];
631
+ for (const el of subjects) {
632
+ const bound = el.elementBound;
633
+ const centre = centreOf(bound);
634
+ let carried = carrierRole === undefined;
635
+ for (const path of carriers) {
636
+ if (pointPathDistanceSquared(centre, path) <= toleranceSquared) {
637
+ carried = true;
638
+ break;
639
+ }
640
+ }
641
+ const frame = attributeBackground(bound, backgrounds);
642
+ let inBand = true;
643
+ // The frontier the subject missed: the nearest one, which is the one the
644
+ // user was aiming at and the only one a suggestion can point back to.
645
+ let missed;
646
+ if (carried && axis !== undefined && frame !== null && def) {
647
+ let bands = bandsOf.get(frame.id);
648
+ if (bands === undefined) {
649
+ // `frame.props` is what makes the bands the ones the user can SEE: a
650
+ // frame whose graduations are switched off yields none, and the
651
+ // "no transition at all" branch below turns that into silence.
652
+ const all = backgroundTransitionBands(def, frame.bound, frame.props);
653
+ // A transition ACROSS a horizontal axis is a vertical band, i.e. an `x`.
654
+ bands = axis.orientation === 'horizontal' ? all.x : all.y;
655
+ bandsOf.set(frame.id, bands);
656
+ }
657
+ // The subject's own EXTENT across the axis, not a point inside it: the
658
+ // question is whether the frontier passes THROUGH the subject, and a
659
+ // centre cannot answer it for anything wider than a hair.
660
+ const horizontal = axis.orientation === 'horizontal';
661
+ const from = horizontal ? bound.x : bound.y;
662
+ const to = horizontal ? bound.maxX : bound.maxY;
663
+ const value = horizontal ? centre[0] : centre[1];
664
+ inBand = false;
665
+ let nearest = Infinity;
666
+ for (const band of bands) {
667
+ // Two intervals overlapping: the subject covers the frontier, or the
668
+ // frame's declared band covers the subject.
669
+ if (from <= band.max && to >= band.min) {
670
+ inBand = true;
671
+ break;
672
+ }
673
+ const away = Math.abs(value - band.at);
674
+ if (away < nearest) {
675
+ nearest = away;
676
+ missed = band;
677
+ }
678
+ }
679
+ // A frame with no transition at all asks nothing of the subject — and
680
+ // since the bands are read through the instance, "none" covers both a
681
+ // frame that declares no frontier and one whose frontiers the user has
682
+ // hidden.
683
+ if (bands.length === 0)
684
+ inBand = true;
685
+ }
686
+ if (carried && inBand)
687
+ continue;
688
+ // The words of the half that failed. Carrier first when a rule declares
689
+ // one: it is the more actionable of the two, and the only one that makes
690
+ // the other mean anything. A boundary-only rule has `carried` true and no
691
+ // `offBoundary`, so it says its own sentence — the only one it has.
692
+ const violation = raise(rule, [el.id], frame?.id, carried ? attachment.offBoundary : rule);
693
+ if (carried && missed !== undefined)
694
+ violation.boundaryId = missed.id;
695
+ violations.push(violation);
696
+ }
697
+ return violations;
698
+ }
699
+ /**
700
+ * How much shared extent counts as an overlap rather than a shared edge.
701
+ *
702
+ * Two boxes that TOUCH — a label ending exactly where a node begins — share no
703
+ * area and are perfectly readable, which is precisely what alignment and snap
704
+ * produce all day. The house `Bound.isOverlapWithBound` answers `true` for them
705
+ * (it compares against `+EPSILON`, so equality passes), so this asks for real
706
+ * shared extent instead. Small enough that a pixel of genuine overlap still
707
+ * counts, large enough that float noise on a snapped edge does not.
708
+ */
709
+ const OVERLAP_EPSILON = 1e-6;
710
+ /** Do these two bounds share AREA, as opposed to an edge? */
711
+ function boundsOverlap(a, b) {
712
+ return (b.maxX - a.minX > OVERLAP_EPSILON &&
713
+ a.maxX - b.minX > OVERLAP_EPSILON &&
714
+ b.maxY - a.minY > OVERLAP_EPSILON &&
715
+ a.maxY - b.minY > OVERLAP_EPSILON);
716
+ }
717
+ /**
718
+ * Do these two subjects actually collide, given each one's geometry?
719
+ *
720
+ * ## Known limit: a ROTATED node is measured by its bounding box
721
+ *
722
+ * `elementBound` is the axis-aligned box of a rotated element, so a 120×26
723
+ * label at 45° presents as roughly 103×103 — the same inflation this family
724
+ * refuses to accept for an edge, where the fix was to measure along the path.
725
+ * A rotated NODE has no equivalent cheap answer: it needs an oriented box and a
726
+ * separating-axis test on both sides of every pair, in the inner loop of the
727
+ * only super-linear family here.
728
+ *
729
+ * Deliberately not built, and deliberately written down. Rotating a Wardley
730
+ * node is not a gesture the framework has; the day a framework rotates its
731
+ * artefacts is the day this is worth the inner loop, and {@link OverlapSubject}
732
+ * is where the oriented box would go. Until then the failure mode is a
733
+ * false POSITIVE on a rotated pair — a warning too many, never a miss — which
734
+ * is the right way round for a readability rule that is only ever a warning.
735
+ * A test pins the behaviour so a change to it cannot be silent.
736
+ *
737
+ * A rotated `text` role holds the same line, and has to be MADE to: narrowing
738
+ * a rotated box to the band an unrotated renderer would draw in is how a miss
739
+ * gets built (at 180° the words are at the other end of it). So
740
+ * {@link textInkBound} hands a rotated element its whole box back.
741
+ */
742
+ function subjectsCollide(a, b, minPenetration) {
743
+ // Bounding boxes first: on a dense map almost every pair dies here, and this
744
+ // is the test the O(n²) sweep is really made of.
745
+ if (!boundsOverlap(a.bound, b.bound))
746
+ return false;
747
+ if (a.path === null && b.path === null) {
748
+ return (minPenetration <= 0 || boundsPenetration(a.bound, b.bound) > minPenetration);
749
+ }
750
+ // Two paths: zero-width lines, so there is no depth to measure and a declared
751
+ // crossing is reported as it always was.
752
+ if (a.path !== null && b.path !== null)
753
+ return pathsCross(a.path, b.path);
754
+ const path = (a.path ?? b.path);
755
+ const bound = a.path === null ? a.bound : b.bound;
756
+ if (!pathHitsBound(path, bound))
757
+ return false;
758
+ return (minPenetration <= 0 || pathPenetration(path, bound) > minPenetration);
759
+ }
760
+ /**
761
+ * How far two overlapping boxes reach INTO each other: the smaller of the two
762
+ * axis overlaps, which is the distance one of them would have to move to come
763
+ * free. Corner-grazing gives a small number on both axes; a name written across
764
+ * a node gives the height of the letters.
765
+ */
766
+ function boundsPenetration(a, b) {
767
+ return Math.min(Math.min(a.maxX - b.minX, b.maxX - a.minX), Math.min(a.maxY - b.minY, b.maxY - a.minY));
768
+ }
769
+ /**
770
+ * The deepest any point of `path` gets under an edge of `bound` — 0 when the
771
+ * path only touches it, negative when it misses entirely.
772
+ *
773
+ * "Depth" is the distance to the NEAREST edge, so a link crossing a label
774
+ * lengthwise through the middle scores half the line height, and one clipping a
775
+ * corner scores almost nothing. Exact, not sampled: that distance is the
776
+ * minimum of four linear functions of the position along the segment, hence
777
+ * concave, so its maximum is attained either at an end of the segment or where
778
+ * two of the four swap places — at most eight candidates, all of them tested.
779
+ *
780
+ * Only ever reached by a pair that already collides, i.e. off the hot path of
781
+ * the sweep.
782
+ */
783
+ function pathPenetration(path, bound) {
784
+ const edges = (p) => [
785
+ p[0] - bound.minX,
786
+ bound.maxX - p[0],
787
+ p[1] - bound.minY,
788
+ bound.maxY - p[1],
789
+ ];
790
+ const depthAt = (from, to, t) => {
791
+ let depth = Infinity;
792
+ for (let k = 0; k < 4; k++) {
793
+ depth = Math.min(depth, from[k] + t * (to[k] - from[k]));
794
+ }
795
+ return depth;
796
+ };
797
+ let deepest = -Infinity;
798
+ for (let i = 1; i < path.length; i++) {
799
+ const from = edges(path[i - 1]);
800
+ const to = edges(path[i]);
801
+ deepest = Math.max(deepest, depthAt(from, to, 0), depthAt(from, to, 1));
802
+ for (let m = 0; m < 4; m++) {
803
+ for (let n = m + 1; n < 4; n++) {
804
+ const slope = to[m] - from[m] - (to[n] - from[n]);
805
+ if (slope === 0)
806
+ continue;
807
+ const t = (from[n] - from[m]) / slope;
808
+ if (t <= 0 || t >= 1)
809
+ continue;
810
+ deepest = Math.max(deepest, depthAt(from, to, t));
811
+ }
812
+ }
813
+ }
814
+ return deepest;
815
+ }
816
+ /**
817
+ * Advance width of one character, as a fraction of the font size, by CLASS.
818
+ *
819
+ * The engine measures no text: a canvas in the evaluation path would cost a
820
+ * `measureText` per label per pass, and would make the verdict depend on which
821
+ * fonts a host happens to have loaded — the same map would validate differently
822
+ * in a headless report and in a browser. So the width is DECLARED, and read off
823
+ * this table.
824
+ *
825
+ * ## Why a table and not one mean advance
826
+ *
827
+ * A single ratio was the first answer and it was wrong in the direction that
828
+ * matters. Letters differ by a factor of FOUR — `i` is 0.24 em and `W` is 0.9 —
829
+ * so an average tuned on `Customer` reads `utility` half as wide again as it is
830
+ * drawn, and a link 15 units past the last letter of a lowercase name lands
831
+ * inside the ghost. That is the very false positive this geometry exists to
832
+ * remove, and it survived the first version for every narrow-lettered name.
833
+ *
834
+ * Five classes, still constant per character, still no canvas, still the same
835
+ * answer on every host:
836
+ *
837
+ * | class | em | characters |
838
+ * | --- | --- | --- |
839
+ * | thin | 0.26 | `i l I j` and the punctuation, brackets and the SPACE |
840
+ * | narrow | 0.35 | `f t r " *` |
841
+ * | wide | 0.85 | `m w M W @ %` |
842
+ * | capital | 0.62 | `A`–`Z`, the rest of them |
843
+ * | full-width | 1.00 | anything from U+2E80 up: CJK, kana, hangul |
844
+ * | nothing | 0.00 | combining marks, zero-width joiners, variation selectors |
845
+ * | default | 0.53 | everything else, lowercase and digits |
846
+ *
847
+ * ## The precision, measured rather than claimed
848
+ *
849
+ * Against the real renderer at Inter 18, over the 28-name bench in
850
+ * `integration-test/.../wardley-validation.spec.ts` — which prints every line
851
+ * and fails outside ±15 % on ANY of them:
852
+ *
853
+ * | name | drawn | declared |
854
+ * | --- | --- | --- |
855
+ * | `utility` | 46.4 | 45.7 (−1 %) |
856
+ * | `little` | 36.6 | 36.2 (−1 %) |
857
+ * | `ERP` | 33.7 | 33.5 (−1 %) |
858
+ * | `Customer` | 83.2 | 77.2 (−7 %) |
859
+ * | `Payment gateway` | 152.0 | 144.9 (−5 %) |
860
+ * | `Cloud` | 49.7 | 44.5 (**−11 %**, the worst of the 28) |
861
+ * | `WWWWWWWWWW` | 170.8 | 153.0 (−10 %) |
862
+ * | `付款` | 36.0 | 36.0 (0 %) |
863
+ *
864
+ * **Never wide, on any of the 28** — which is the property that matters: an
865
+ * over-estimate is a ghost past the last letter, and a link crossing it is
866
+ * precisely the false positive this geometry exists to remove. What is left is
867
+ * a few units of silence at the end of a name, on the scale
868
+ * {@link ValidationRule.minPenetration} is calibrated on.
869
+ *
870
+ * (The drawn figure itself moves by a few percent with the web font's loading
871
+ * state — the other half of why the engine does not try to measure exactly.)
872
+ */
873
+ const TEXT_ADVANCE = /* @__PURE__ */ (() => {
874
+ const table = Object.create(null);
875
+ for (const char of " .,:;!|'`()[]{}/\\-ilIj")
876
+ table[char] = 0.26;
877
+ for (const char of 'ftr"*')
878
+ table[char] = 0.35;
879
+ for (const char of 'mwMW@%')
880
+ table[char] = 0.85;
881
+ return table;
882
+ })();
883
+ /** Width of one character, in em. See {@link TEXT_ADVANCE}. */
884
+ function charAdvance(char) {
885
+ const declared = TEXT_ADVANCE[char];
886
+ if (declared !== undefined)
887
+ return declared;
888
+ const code = char.codePointAt(0) ?? 0;
889
+ // Code points that draw nothing OF THEIR OWN, and so advance nothing: a
890
+ // combining mark sits on the letter before it, a zero-width space or joiner
891
+ // is not there at all, and a variation selector only says how to draw its
892
+ // neighbour. Billed at full price they turn "élément" pasted from a Mac
893
+ // (decomposed: nine code points, seven glyphs) into a fifth of a name's worth
894
+ // of white paper — a false positive, which is the one thing this must not
895
+ // manufacture. Before the full-width test, since U+FE0F is above it.
896
+ if ((code >= 0x0300 && code <= 0x036f) ||
897
+ (code >= 0x200b && code <= 0x200d) ||
898
+ code === 0xfe0f) {
899
+ return 0;
900
+ }
901
+ // Full-width scripts, where one character IS one em.
902
+ if (code >= 0x2e80)
903
+ return 1;
904
+ // The rest of the capitals: wider than lowercase, narrower than `M`.
905
+ if (code >= 65 && code <= 90)
906
+ return 0.62;
907
+ return 0.53;
908
+ }
909
+ /** Width of one line of text, in model units. */
910
+ function lineWidth(line, fontSize) {
911
+ let em = 0;
912
+ for (const char of line)
913
+ em += charAdvance(char);
914
+ return em * fontSize;
915
+ }
916
+ /**
917
+ * The box the TEXT of an element actually occupies, inside the box the element
918
+ * was created with.
919
+ *
920
+ * A text element is created at a width that says nothing about its content — a
921
+ * Wardley label is 120 to 200 units wide whether it reads "ERP" or "Customer
922
+ * relationship management" — so its box carries empty margin on the side its
923
+ * `textAlign` runs away from. Measuring that box makes the rule report things
924
+ * the user cannot see, which is exactly the report they stop believing.
925
+ *
926
+ * Narrowed on the horizontal only, and never widened: the height stored on the
927
+ * element IS the rendered block (the editor writes `lineHeight × lines` back on
928
+ * every edit), and the renderer lays the lines out from the TOP of the box.
929
+ * An element exposing no text is left exactly as it was, so a fixture or a
930
+ * host element that carries none is measured by its box as before; one whose
931
+ * text reads EMPTY gets a width of zero, and {@link evaluateNoOverlap} drops it
932
+ * from the pass entirely — it draws nothing, so it collides with nothing.
933
+ *
934
+ * ## A ROTATED text is measured by its whole box
935
+ *
936
+ * `elementBound` is the axis-aligned box of a rotated element, and this cuts a
937
+ * band out of it where an UNROTATED renderer would put the ink. At 180° the
938
+ * words are at the other end of the box, so the band would be white paper and
939
+ * the letters outside it: a MISS, not a warning too many. A rotated text
940
+ * therefore keeps its whole box — over-reporting, which is the failure mode the
941
+ * family already accepts for a rotated node, and the one a readability warning
942
+ * can afford.
943
+ */
944
+ function textInkBound(el, bound) {
945
+ const text = el;
946
+ if (typeof text.fontSize !== 'number' || text.text == null)
947
+ return bound;
948
+ if (typeof text.rotate === 'number' && text.rotate !== 0)
949
+ return bound;
950
+ let longest = 0;
951
+ // NFC first: the same name typed here and pasted from a Mac can arrive as the
952
+ // same glyphs in a different number of code points, and a width that depends
953
+ // on which one would be a rule that answers differently on identical text.
954
+ for (const line of String(text.text).normalize('NFC').split('\n')) {
955
+ longest = Math.max(longest, lineWidth(line, text.fontSize));
956
+ }
957
+ const w = Math.min(bound.w, longest);
958
+ const x = text.textAlign === 'center'
959
+ ? bound.x + (bound.w - w) / 2
960
+ : text.textAlign === 'right'
961
+ ? bound.maxX - w
962
+ : bound.x;
963
+ return new Bound(x, bound.y, w, bound.h);
964
+ }
965
+ function pathHitsBound(path, bound) {
966
+ for (let i = 1; i < path.length; i++) {
967
+ if (bound.containsPoint(path[i - 1]) || bound.containsPoint(path[i])) {
968
+ return true;
969
+ }
970
+ if (bound.intersectLine(path[i - 1], path[i]) !== null)
971
+ return true;
972
+ }
973
+ return false;
974
+ }
975
+ function pathsCross(a, b) {
976
+ for (let i = 1; i < a.length; i++) {
977
+ for (let j = 1; j < b.length; j++) {
978
+ if (lineIntersects(a[i - 1], a[i], b[j - 1], b[j]))
979
+ return true;
980
+ }
981
+ }
982
+ return false;
983
+ }
984
+ /**
985
+ * "Do these two things sit on top of each other?" (PF5.13)
986
+ *
987
+ * The first family that is not element-local: it evaluates PAIRS, so a finding
988
+ * names two elements and neither of them alone is at fault. Which pairs is
989
+ * declared data — `[label, node]`, `[label, link]` — and the geometry each side
990
+ * is measured with follows the role's own `kind`, so an edge is measured along
991
+ * its path and never by the bounding box of its diagonal.
992
+ *
993
+ * ## Cost, and the dirty set
994
+ *
995
+ * Naive it is O(p²) over the PARTICIPANTS — the elements carrying one of the
996
+ * declared roles, never the whole surface — with every bound read exactly once
997
+ * and a rectangle test as the inner loop. That is what the bench measures on
998
+ * the reference map, and it fits.
999
+ *
1000
+ * With an {@link IncrementalContext} it is O(|dirty| × p): findings that name
1001
+ * only untouched elements are carried over from the previous evaluation, and
1002
+ * only the couples involving something that MOVED are re-tested. A drag on a
1003
+ * dense map is one element against the participants, not the whole grid against
1004
+ * itself.
1005
+ */
1006
+ function evaluateNoOverlap(rule, elements, incremental) {
1007
+ const pairs = rule.overlap;
1008
+ if (pairs === undefined || pairs.length === 0)
1009
+ return [];
1010
+ // The distinct roles the pairs mention, and the pairs re-expressed as index
1011
+ // couples into that list — so the inner loop compares booleans, not strings.
1012
+ const slots = [];
1013
+ const couples = [];
1014
+ for (const [a, b] of pairs) {
1015
+ const ia = slots.indexOf(a) >= 0 ? slots.indexOf(a) : slots.push(a) - 1;
1016
+ const ib = slots.indexOf(b) >= 0 ? slots.indexOf(b) : slots.push(b) - 1;
1017
+ couples.push([ia, ib]);
1018
+ }
1019
+ const subjects = [];
1020
+ for (const el of elements) {
1021
+ // Neutral element: never a participant, whatever it sits on.
1022
+ if (el.role === undefined)
1023
+ continue;
1024
+ let matched = false;
1025
+ const filled = slots.map(slot => {
1026
+ const isA = roleIsA(el.role, slot, rule.roles);
1027
+ matched ||= isA;
1028
+ return isA;
1029
+ });
1030
+ if (!matched)
1031
+ continue;
1032
+ const kind = rule.roles[el.role]?.kind;
1033
+ // A `text` role is measured by the ink of its text, not by the box it was
1034
+ // created at; everything else by its bounds.
1035
+ const bound = kind === 'text' ? textInkBound(el, el.elementBound) : el.elementBound;
1036
+ // A text emptied of its words draws NOTHING, and a zero-width box is still
1037
+ // a vertical line that a wider box can be said to contain. Out of the pass:
1038
+ // it cannot make anything unreadable. Only a TEXT — a perfectly vertical
1039
+ // connector has a flat box too, and is measured along its path.
1040
+ if (kind === 'text' && bound.w === 0)
1041
+ continue;
1042
+ subjects.push({
1043
+ id: el.id,
1044
+ bound,
1045
+ // An `edge` role is measured along its path.
1046
+ path: kind === 'edge' ? elementPath(el) : null,
1047
+ slots: filled,
1048
+ });
1049
+ }
1050
+ if (subjects.length < 2)
1051
+ return [];
1052
+ const backgrounds = backgroundsOf(rule, elements);
1053
+ const declared = (a, b) => couples.some(([i, j]) => (a.slots[i] && b.slots[j]) || (a.slots[j] && b.slots[i]));
1054
+ const minPenetration = rule.minPenetration ?? 0;
1055
+ const found = [];
1056
+ const test = (a, b) => {
1057
+ if (!declared(a, b) || !subjectsCollide(a, b, minPenetration))
1058
+ return;
1059
+ // Sorted, so the same collision reads the same way every time — and the
1060
+ // frame is read off the SAME half whichever end the pair was reached from,
1061
+ // or a full pass and an incremental one could attribute one collision to
1062
+ // two different maps.
1063
+ const [first, second] = a.id < b.id ? [a, b] : [b, a];
1064
+ found.push(raise(rule, [first.id, second.id], attributeBackground(first.bound, backgrounds)?.id));
1065
+ };
1066
+ const sweep = () => {
1067
+ for (let i = 0; i < subjects.length; i++) {
1068
+ for (let j = i + 1; j < subjects.length; j++)
1069
+ test(subjects[i], subjects[j]);
1070
+ }
1071
+ return found;
1072
+ };
1073
+ if (incremental === undefined)
1074
+ return sweep();
1075
+ const { dirty } = incremental;
1076
+ /**
1077
+ * Anything that IS or WAS a frame sends the whole surface back through the
1078
+ * sweep.
1079
+ *
1080
+ * A frame is not a participant, so no pair-wise re-test ever reaches the
1081
+ * findings attributed to it — and moving, resizing or DELETING one changes
1082
+ * every one of those attributions, which is what decides the map-wide
1083
+ * arbitration and the profile in force. Deletion is the case that made this
1084
+ * necessary and the one a "is a current background dirty" test cannot see:
1085
+ * once the map is gone `backgrounds` is empty, nothing looks dirty, and the
1086
+ * findings measured against it would vanish from a live board.
1087
+ */
1088
+ const frameTouched = backgrounds.some(background => dirty.has(background.id)) ||
1089
+ incremental.previous.some(violation => violation.backgroundId !== undefined &&
1090
+ dirty.has(violation.backgroundId));
1091
+ if (frameTouched)
1092
+ return sweep();
1093
+ /**
1094
+ * ...and so does a change big enough that the shortcut is no longer one.
1095
+ *
1096
+ * The dirty path costs `dirtyParticipants × p`; the sweep costs `p²/2`. Past
1097
+ * the crossover the "optimisation" is several times the price of the thing it
1098
+ * replaces — measured at 8× and OUT of the 16 ms budget for a lasso drag over
1099
+ * a third of a 500-element map, against 2.2 ms for the sweep it was avoiding.
1100
+ * One comparison buys back the worst case entirely.
1101
+ */
1102
+ let dirtyParticipants = 0;
1103
+ const isDirty = subjects.map(subject => {
1104
+ const changed = dirty.has(subject.id);
1105
+ if (changed)
1106
+ dirtyParticipants++;
1107
+ return changed;
1108
+ });
1109
+ if (dirtyParticipants * 2 >= subjects.length)
1110
+ return sweep();
1111
+ // Only the couples involving something that changed. Everything else keeps
1112
+ // the verdict it already had.
1113
+ for (let i = 0; i < subjects.length; i++) {
1114
+ if (!isDirty[i])
1115
+ continue;
1116
+ for (let j = 0; j < subjects.length; j++) {
1117
+ if (j === i)
1118
+ continue;
1119
+ // A dirty–dirty couple is reachable from both ends and must be tested
1120
+ // once. The earlier index already tested it against everything, so only
1121
+ // look forward — no per-couple key, and nothing allocated in the loop
1122
+ // that is the whole cost of this path.
1123
+ if (j < i && isDirty[j])
1124
+ continue;
1125
+ test(subjects[i], subjects[j]);
1126
+ }
1127
+ }
1128
+ const alive = new Set(subjects.map(subject => subject.id));
1129
+ // The `backgroundId` is deliberately NOT filtered on here: a pair-wise
1130
+ // finding depends on its frame only for ATTRIBUTION, and any change to that
1131
+ // frame already took the whole surface through the sweep above. Dropping
1132
+ // findings on it as well is how a live overlap disappeared when its map was
1133
+ // deleted.
1134
+ const carried = incremental.previous.filter(violation => violation.ruleId === rule.id &&
1135
+ violation.elementIds.every(id => !dirty.has(id) && alive.has(id)));
1136
+ return [...carried, ...found];
1137
+ }
1138
+ /**
1139
+ * Hue windows, in degrees, in ascending order. The last one wraps back onto
1140
+ * red, which is why the table is read as "first upper bound above the hue".
1141
+ */
1142
+ const TONE_HUES = [
1143
+ [15, 'red'],
1144
+ [45, 'orange'],
1145
+ [70, 'yellow'],
1146
+ [160, 'green'],
1147
+ [200, 'teal'],
1148
+ [260, 'blue'],
1149
+ [300, 'purple'],
1150
+ [345, 'magenta'],
1151
+ [360, 'red'],
1152
+ ];
1153
+ /**
1154
+ * Tone words, longest first so a substring can never win over the name that
1155
+ * contains it. This is how a THEME TOKEN is classified — `--affine-palette-shape-red`
1156
+ * carries its own answer in its name, and reading it is the only way to classify
1157
+ * a variable the engine has no stylesheet to resolve.
1158
+ */
1159
+ const TONE_WORDS = [
1160
+ ['magenta', 'magenta'],
1161
+ ['purple', 'purple'],
1162
+ ['orange', 'orange'],
1163
+ ['yellow', 'yellow'],
1164
+ ['green', 'green'],
1165
+ ['white', 'grey'],
1166
+ ['black', 'grey'],
1167
+ ['blue', 'blue'],
1168
+ ['teal', 'teal'],
1169
+ ['grey', 'grey'],
1170
+ ['gray', 'grey'],
1171
+ ['red', 'red'],
1172
+ ];
1173
+ /** Below this saturation a colour reads as a grey whatever its hue says. */
1174
+ const GREY_SATURATION = 0.15;
1175
+ /** Below this alpha nothing is on screen, so there is nothing to judge. */
1176
+ const INVISIBLE_ALPHA = 0.08;
1177
+ /** `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa` → `[r, g, b, a]` in 0..1. */
1178
+ function parseHex(value) {
1179
+ const hex = value.slice(1);
1180
+ const short = hex.length === 3 || hex.length === 4;
1181
+ if (!short && hex.length !== 6 && hex.length !== 8)
1182
+ return null;
1183
+ const size = short ? 1 : 2;
1184
+ const channels = [];
1185
+ for (let i = 0; i < hex.length; i += size) {
1186
+ const chunk = hex.slice(i, i + size);
1187
+ const parsed = Number.parseInt(short ? chunk + chunk : chunk, 16);
1188
+ if (Number.isNaN(parsed))
1189
+ return null;
1190
+ channels.push(parsed / 255);
1191
+ }
1192
+ return [channels[0], channels[1], channels[2], channels[3] ?? 1];
1193
+ }
1194
+ /**
1195
+ * Which tone family a colour belongs to, or `undefined` when the engine cannot
1196
+ * honestly say.
1197
+ *
1198
+ * Two readings, in order: the NUMBER when the colour is a hex the process can
1199
+ * actually see, and the NAME when it is a theme token it cannot. Anything else —
1200
+ * `transparent`, a gradient, an `rgb()` triple, a variable with no tone in its
1201
+ * name — is unclassifiable, and unclassifiable means silence: a convention rule
1202
+ * that guessed would indict a user over a colour nobody here can see.
1203
+ */
1204
+ export function toneOf(value) {
1205
+ if (!value)
1206
+ return undefined;
1207
+ const raw = value.trim().toLowerCase();
1208
+ if (raw === '' || raw === 'transparent')
1209
+ return undefined;
1210
+ if (raw.startsWith('#')) {
1211
+ const rgba = parseHex(raw);
1212
+ if (rgba === null)
1213
+ return undefined;
1214
+ const [r, g, b, a] = rgba;
1215
+ if (a < INVISIBLE_ALPHA)
1216
+ return undefined;
1217
+ const max = Math.max(r, g, b);
1218
+ const min = Math.min(r, g, b);
1219
+ const lightness = (max + min) / 2;
1220
+ const span = max - min;
1221
+ // Saturation of the HSL model, and the two ends of the lightness scale
1222
+ // where hue stops meaning anything at all.
1223
+ const saturation = span === 0 ? 0 : span / (1 - Math.abs(2 * lightness - 1));
1224
+ if (saturation < GREY_SATURATION || lightness < 0.06 || lightness > 0.96) {
1225
+ return 'grey';
1226
+ }
1227
+ let hue;
1228
+ if (max === r)
1229
+ hue = ((g - b) / span) % 6;
1230
+ else if (max === g)
1231
+ hue = (b - r) / span + 2;
1232
+ else
1233
+ hue = (r - g) / span + 4;
1234
+ hue = (hue * 60 + 360) % 360;
1235
+ for (const [limit, tone] of TONE_HUES)
1236
+ if (hue < limit)
1237
+ return tone;
1238
+ return 'red';
1239
+ }
1240
+ for (const [word, tone] of TONE_WORDS)
1241
+ if (raw.includes(word))
1242
+ return tone;
1243
+ return undefined;
1244
+ }
1245
+ /**
1246
+ * The colour-bearing props the engine reads, duck-typed.
1247
+ *
1248
+ * Duck-typed for the same reason {@link elementPath} is: the engine must not
1249
+ * import a shape model, a text model or a connector model to answer a question
1250
+ * that is about pixels. An element that carries none of these has no colour to
1251
+ * be judged on and is skipped on the cheapest possible test.
1252
+ */
1253
+ const COLOUR_PROPS = ['fillColor', 'strokeColor', 'color'];
1254
+ /**
1255
+ * Every string a `Color` value can present. A theme PAIR (`{ light, dark }`)
1256
+ * yields both members: they are one decision seen under two themes, and the
1257
+ * caller treats them as alternatives rather than as two separate colours.
1258
+ */
1259
+ function colourStrings(value) {
1260
+ if (typeof value === 'string')
1261
+ return [value];
1262
+ if (value === null || typeof value !== 'object')
1263
+ return [];
1264
+ const out = [];
1265
+ for (const key of ['normal', 'light', 'dark']) {
1266
+ const member = value[key];
1267
+ if (typeof member === 'string')
1268
+ out.push(member);
1269
+ }
1270
+ return out;
1271
+ }
1272
+ /**
1273
+ * Whether this element is drawn in a tone the convention does not sanction.
1274
+ *
1275
+ * Per PROP, not per string: a `{ light, dark }` pair is ONE colour, so it
1276
+ * offends only when neither of its two members lands on a sanctioned tone. A
1277
+ * prop whose colour cannot be classified is skipped, and an element whose props
1278
+ * are all unclassifiable is never indicted.
1279
+ *
1280
+ * An unfilled shape's `fillColor` is deliberately ignored: it is stored, it is
1281
+ * not painted, and a rule about what the map LOOKS like has no business reading
1282
+ * a colour that is not on screen.
1283
+ */
1284
+ function departsFromTones(el, allowed) {
1285
+ const record = el;
1286
+ const filled = record['filled'];
1287
+ for (const prop of COLOUR_PROPS) {
1288
+ if (prop === 'fillColor' && filled === false)
1289
+ continue;
1290
+ let classified = false;
1291
+ let sanctioned = false;
1292
+ for (const raw of colourStrings(record[prop])) {
1293
+ const tone = toneOf(raw);
1294
+ if (tone === undefined)
1295
+ continue;
1296
+ classified = true;
1297
+ if (allowed.has(tone)) {
1298
+ sanctioned = true;
1299
+ break;
1300
+ }
1301
+ }
1302
+ if (classified && !sanctioned)
1303
+ return true;
1304
+ }
1305
+ return false;
1306
+ }
1307
+ /**
1308
+ * "Is this drawn in one of the tones the frame sanctions for it?" (PF13.8 / Q5)
1309
+ *
1310
+ * The sanctioned tones come from the FRAME's own palette, resolved through the
1311
+ * `@name` indirection every background label already uses — so the rule restates
1312
+ * no colour the background owns, and a host restyling the frame restyles the
1313
+ * convention with it. See {@link ToneConventionDef}.
1314
+ *
1315
+ * A rule whose named entries resolve to nothing classifiable warns once and
1316
+ * evaluates nothing: a convention with no reference colour is not a convention,
1317
+ * and indicting the whole board over a broken declaration is the one outcome
1318
+ * worse than silence.
1319
+ */
1320
+ function evaluateToneConvention(rule, elements) {
1321
+ const subjectRole = rule.appliesTo;
1322
+ const tone = rule.tone;
1323
+ const def = rule.background;
1324
+ if (subjectRole === undefined || tone === undefined || def === undefined) {
1325
+ return [];
1326
+ }
1327
+ const palette = def.chrome?.palette;
1328
+ const allowed = new Set();
1329
+ for (const name of tone.palette) {
1330
+ // Straight at the entry rather than through `backgroundColor('@name')`,
1331
+ // which answers a broken magenta for a name the palette does not carry —
1332
+ // and a magenta the rule never asked for would silently become a sanctioned
1333
+ // tone.
1334
+ const entry = palette?.[name];
1335
+ const classified = entry === undefined ? undefined : toneOf(entry);
1336
+ if (classified !== undefined)
1337
+ allowed.add(classified);
1338
+ }
1339
+ if (allowed.size === 0) {
1340
+ warnOnce(`tone-convention rule "${rule.id}" names no palette entry that resolves ` +
1341
+ `to a classifiable tone — it is not evaluated.`);
1342
+ return [];
1343
+ }
1344
+ const backgrounds = backgroundsOf(rule, elements);
1345
+ const violations = [];
1346
+ for (const el of elements) {
1347
+ if (el.role === undefined)
1348
+ continue;
1349
+ if (!roleIsA(el.role, subjectRole, rule.roles))
1350
+ continue;
1351
+ if (!departsFromTones(el, allowed))
1352
+ continue;
1353
+ violations.push(raise(rule, [el.id], attributeBackground(el.elementBound, backgrounds)?.id));
1354
+ }
1355
+ return violations;
1356
+ }
1357
+ /**
1358
+ * "Is most of what is on this map of one declared nature?" (PF13.8 / Q6)
1359
+ *
1360
+ * A remark about the MAP, so the finding names the BACKGROUND and nothing else:
1361
+ * no single element is at fault, and drawing a badge on one of two hundred
1362
+ * components would be an accusation aimed at a bystander.
1363
+ *
1364
+ * Counted PER MAP. A board carrying a map of activities beside a map of
1365
+ * practices holds two different answers, and a tally over the whole surface
1366
+ * would give both of them the wrong one.
1367
+ *
1368
+ * ## The gate
1369
+ *
1370
+ * A surface where NOT ONE subject carries the fact yields nothing, silently —
1371
+ * see {@link MajorityDef}. That is the state the Wardley `nature` is in today,
1372
+ * and the rule ships inert rather than waiting in a branch somewhere for
1373
+ * somebody to remember it.
1374
+ */
1375
+ function evaluateMajorityFact(rule, elements) {
1376
+ const subjectRole = rule.appliesTo;
1377
+ const majority = rule.majority;
1378
+ if (subjectRole === undefined || majority === undefined)
1379
+ return [];
1380
+ const backgrounds = backgroundsOf(rule, elements);
1381
+ // No frame on the board: no map for the remark to be about.
1382
+ if (backgrounds.length === 0)
1383
+ return [];
1384
+ const tally = new Map();
1385
+ for (const el of elements) {
1386
+ if (el.role === undefined)
1387
+ continue;
1388
+ if (!roleIsA(el.role, subjectRole, rule.roles))
1389
+ continue;
1390
+ const frame = attributeBackground(el.elementBound, backgrounds);
1391
+ if (frame === null)
1392
+ continue;
1393
+ let counts = tally.get(frame.id);
1394
+ if (counts === undefined) {
1395
+ counts = { subjects: 0, known: 0, matching: 0 };
1396
+ tally.set(frame.id, counts);
1397
+ }
1398
+ counts.subjects += 1;
1399
+ const fact = el[majority.fact];
1400
+ // Whatever a peer wrote, and whatever a framework has not written yet.
1401
+ if (typeof fact !== 'string' || fact === '')
1402
+ continue;
1403
+ counts.known += 1;
1404
+ if (fact === majority.value)
1405
+ counts.matching += 1;
1406
+ }
1407
+ const violations = [];
1408
+ // Sorted, so a board with two qualifying maps always reports them the same
1409
+ // way whichever order the surface happened to be walked in.
1410
+ for (const id of [...tally.keys()].sort()) {
1411
+ const counts = tally.get(id);
1412
+ // THE GATE: nothing here carries the fact, so there is no majority to be in.
1413
+ if (counts.known === 0)
1414
+ continue;
1415
+ // A strict majority of the SUBJECTS, not of the ones that happen to be
1416
+ // classified: half a map left unclassified is not a mandate.
1417
+ if (counts.matching * 2 <= counts.subjects)
1418
+ continue;
1419
+ violations.push(raise(rule, [id], id));
1420
+ }
1421
+ return violations;
1422
+ }
1423
+ /**
1424
+ * The two ids a typed edge BINDS, or `null`.
1425
+ *
1426
+ * Duck-typed exactly like {@link elementPath}, and for the same reason: the
1427
+ * engine knows roles and geometry, and must not import a connector model. An
1428
+ * end holding a bare `position` and no `id` is bound to nothing.
1429
+ *
1430
+ * `null` is the ADR's own guard, and it is not marginal — releasing the link
1431
+ * tool over empty canvas produces such an edge at any time, and a palette
1432
+ * sample of a stroke style is one by construction. An edge that relates nothing
1433
+ * is never evaluated.
1434
+ */
1435
+ function boundEnds(el) {
1436
+ const edge = el;
1437
+ const source = edge.source?.id;
1438
+ const target = edge.target?.id;
1439
+ if (typeof source !== 'string' || typeof target !== 'string')
1440
+ return null;
1441
+ // A self-loop states nothing about an order: it is one element, compared with
1442
+ // itself, and no move can ever resolve it.
1443
+ if (source === target)
1444
+ return null;
1445
+ return [source, target];
1446
+ }
1447
+ /**
1448
+ * "Are these two in the order their relation states?" — W4 (`docs/adr/0010`).
1449
+ *
1450
+ * The first family whose subject is a RELATION. For every element carrying the
1451
+ * declared edge role, the two elements it binds are projected onto one axis of
1452
+ * the frame and compared in the order the edge states: for
1453
+ * `wardley:dependency`, the source is the consumer and must sit higher on the
1454
+ * visibility axis than what it needs.
1455
+ *
1456
+ * The order comes from the persisted `source → target` pair, never from the
1457
+ * geometry — that is the whole point, and the reason this rule can fire at all.
1458
+ * Every other input is declaration: which axis, which way it runs, how much
1459
+ * slack, all read off the frame the framework already declares.
1460
+ *
1461
+ * ## What it stays silent about, and why each one matters
1462
+ *
1463
+ * - **no frame on the board** — nothing declares an axis, so there is no order
1464
+ * to be in. A chain drawn on blank canvas is a sketch;
1465
+ * - **an edge with a free end** — it relates nothing (see {@link boundEnds});
1466
+ * - **an end whose element is gone** — a dangling id says nothing about a
1467
+ * layout;
1468
+ * - **a pair straddling TWO frames** — "higher than" is a question inside one
1469
+ * frame of reference, and two maps have two;
1470
+ * - **an edge carrying no role, or another role** — proportionality (PRD
1471
+ * principle 8). A change arrow is oriented too and is not a dependency.
1472
+ *
1473
+ * ## Cost
1474
+ *
1475
+ * Linear in the EDGES: one pass to index the elements by id, one pass over the
1476
+ * edges, and two constant-time frame attributions per edge. There is no graph
1477
+ * closure and no pair-wise sweep — the rule is about a relation somebody drew,
1478
+ * never about every couple of nodes that could have had one. A cycle A→B→A is
1479
+ * two edges and is judged as two edges: at least one of them is against the
1480
+ * order, and that one is reported.
1481
+ */
1482
+ function evaluateRelativeOrder(rule, elements) {
1483
+ const order = rule.relativeOrder;
1484
+ const def = rule.background;
1485
+ if (order === undefined || def === undefined)
1486
+ return [];
1487
+ const axis = backgroundAxisFact(def, order.axis);
1488
+ if (axis === undefined) {
1489
+ warnOnce(`relative-order rule "${rule.id}" measures along "${order.axis}", which ` +
1490
+ `its background does not declare — the rule is not evaluated.`);
1491
+ return [];
1492
+ }
1493
+ const backgrounds = backgroundsOf(rule, elements);
1494
+ // No frame, no frame of reference: a chain on blank canvas is a sketch.
1495
+ if (backgrounds.length === 0)
1496
+ return [];
1497
+ const byId = new Map();
1498
+ const edges = [];
1499
+ for (const el of elements) {
1500
+ byId.set(el.id, el);
1501
+ // Cheapest possible exit for a neutral element: no role, no evaluation.
1502
+ if (el.role === undefined)
1503
+ continue;
1504
+ if (roleIsA(el.role, order.edgeRole, rule.roles))
1505
+ edges.push(el);
1506
+ }
1507
+ if (edges.length === 0)
1508
+ return [];
1509
+ const horizontal = axis.orientation === 'horizontal';
1510
+ // How far along the axis' FORWARD sense a bound sits. One number, so the
1511
+ // comparison below reads the same whichever way the axis runs.
1512
+ const along = (bound) => {
1513
+ const centre = centreOf(bound);
1514
+ return centre[0] * axis.forward[0] + centre[1] * axis.forward[1];
1515
+ };
1516
+ const violations = [];
1517
+ for (const edge of edges) {
1518
+ const ends = boundEnds(edge);
1519
+ if (ends === null)
1520
+ continue;
1521
+ const [sourceId, targetId] = ends;
1522
+ const source = byId.get(sourceId);
1523
+ const target = byId.get(targetId);
1524
+ if (source === undefined || target === undefined)
1525
+ continue;
1526
+ const sourceBound = source.elementBound;
1527
+ const targetBound = target.elementBound;
1528
+ const frame = attributeBackground(sourceBound, backgrounds);
1529
+ // Two maps, two frames of reference: the question does not arise.
1530
+ if (frame === null)
1531
+ continue;
1532
+ if (attributeBackground(targetBound, backgrounds)?.id !== frame.id)
1533
+ continue;
1534
+ const ahead = along(sourceBound) - along(targetBound);
1535
+ const expected = order.expect === 'source-ahead' ? ahead : -ahead;
1536
+ const span = horizontal ? frame.bound.w : frame.bound.h;
1537
+ const tolerance = (order.toleranceRatio ?? 0) * span;
1538
+ if (expected >= -tolerance)
1539
+ continue;
1540
+ violations.push(raise(rule, [edge.id, sourceId, targetId].sort(), frame.id));
1541
+ }
1542
+ return violations;
1543
+ }
1544
+ const RULE_FAMILIES = {
1545
+ 'element-in-background': evaluateElementInBackground,
1546
+ 'orientation-against-axis': evaluateOrientationAgainstAxis,
1547
+ attachment: evaluateAttachment,
1548
+ 'no-overlap': evaluateNoOverlap,
1549
+ 'tone-convention': evaluateToneConvention,
1550
+ 'majority-fact': evaluateMajorityFact,
1551
+ 'relative-order-along-axis': evaluateRelativeOrder,
1552
+ };
1553
+ /** A rule the drawing path evaluates. `moment` absent means `'realtime'`. */
1554
+ function isRealtime(rule) {
1555
+ return (rule.moment ?? 'realtime') === 'realtime';
1556
+ }
1557
+ /** The exceptions an element carries, always an array, never a copy to keep. */
1558
+ export function elementExceptions(element) {
1559
+ const stored = element.validationExceptions;
1560
+ // The value comes out of a Y.Map, so it is whatever a peer wrote: a client
1561
+ // that got it wrong must not break evaluation on this one.
1562
+ return Array.isArray(stored) ? stored : [];
1563
+ }
1564
+ /** Whether `element` is excused from `ruleId`. */
1565
+ export function hasException(element, ruleId) {
1566
+ return elementExceptions(element).some(exception => exception?.ruleId === ruleId);
1567
+ }
1568
+ /**
1569
+ * Stamp `exemption` on the findings a user exception already covers.
1570
+ *
1571
+ * Called only when a rule actually raised something, so a conformant board — the
1572
+ * common case, and the one the 16 ms budget is measured on — pays nothing at
1573
+ * all for the feature.
1574
+ */
1575
+ function applyExceptions(rule, raised, elements) {
1576
+ const byId = new Map();
1577
+ for (const el of elements)
1578
+ byId.set(el.id, el);
1579
+ const excused = (id) => {
1580
+ if (id === undefined)
1581
+ return false;
1582
+ const el = byId.get(id);
1583
+ return el !== undefined && hasException(el, rule.id);
1584
+ };
1585
+ for (const violation of raised) {
1586
+ // EVERY indicted element must be excused: a rule indicting two elements of
1587
+ // which only one is excused is still a live finding.
1588
+ const perElement = violation.elementIds.every(excused);
1589
+ // Narrower scope first, so revoking walks from the local decision outward
1590
+ // and each click visibly changes the state instead of doing nothing.
1591
+ if (perElement)
1592
+ violation.exemption = 'element';
1593
+ // ...and the map scope reads THIS finding's own background, never "some
1594
+ // background on the board carries the exception". An arbitration made on
1595
+ // one map says nothing about the map next to it.
1596
+ else if (excused(violation.backgroundId))
1597
+ violation.exemption = 'map';
1598
+ // Nothing covers it (any more). Stamped rather than left alone because an
1599
+ // incremental pass CARRIES OVER finding objects from the last evaluation
1600
+ // (see {@link IncrementalContext}), and one that kept a stale `exemption`
1601
+ // would read as excused on a board where the exception has been revoked.
1602
+ else
1603
+ delete violation.exemption;
1604
+ }
1605
+ }
1606
+ /**
1607
+ * Run every rule over every element. Pure and synchronous — the unit of the
1608
+ * 16 ms budget (PF5.12), and the only thing the bench measures.
1609
+ *
1610
+ * `profiles` is the level of requirement in force (PF9). Omitted, every rule is
1611
+ * judged by its own declared severity, which is exactly what a framework
1612
+ * shipping no profile gets.
1613
+ *
1614
+ * `incremental` is what the caller knows about what CHANGED. Omitted — the
1615
+ * default, and what every test and the bench's full-evaluation case pass — the
1616
+ * whole surface is re-judged from scratch. Handed in, the pair-wise family
1617
+ * re-tests only the couples a change can have affected; the answer is the same,
1618
+ * it is just reached without walking the grid against itself.
1619
+ */
1620
+ export function evaluateRules(rules, elements, profiles = [], incremental) {
1621
+ return runRules(rules, elements, profiles, 'realtime', incremental);
1622
+ }
1623
+ /**
1624
+ * Run every rule of one MOMENT over every element. The body both
1625
+ * {@link evaluateRules} and {@link evaluateCheckup} are made of — one pipeline,
1626
+ * so a profile, an exception and a background attribution mean exactly the same
1627
+ * thing whichever moment a rule is declared at.
1628
+ *
1629
+ * The moment is tested INSIDE the loop rather than by filtering the array: one
1630
+ * property read per rule, no allocation, and the "an on-demand rule costs the
1631
+ * drawing path nothing" claim is true of every evaluation and not just of the
1632
+ * ones that bothered to filter.
1633
+ */
1634
+ function runRules(rules, elements, profiles, moment, incremental) {
1635
+ if (rules.length === 0)
1636
+ return [];
1637
+ // No profile registered => not a single extra read on the whole surface.
1638
+ const index = profiles.length > 0 ? indexProfiles(profiles) : null;
1639
+ const chosen = index ? readChosenProfiles(elements) : null;
1640
+ const violations = [];
1641
+ for (const rule of rules) {
1642
+ // The other moment's rules are not this pass's business — checked FIRST,
1643
+ // so an on-demand rule never reaches a profile lookup, let alone an
1644
+ // element (PF5.14).
1645
+ if ((isRealtime(rule) ? 'realtime' : 'on-demand') !== moment)
1646
+ continue;
1647
+ // `'off'` everywhere means never walked: the cheapest exit there is, taken
1648
+ // before a single element is touched.
1649
+ if (index && chosen && isRuleSilent(rule, chosen, index))
1650
+ continue;
1651
+ let raised = RULE_FAMILIES[rule.family](rule, elements, incremental);
1652
+ if (index && chosen && raised.length > 0) {
1653
+ raised = applyProfiles(rule, raised, chosen, index);
1654
+ }
1655
+ if (raised.length === 0)
1656
+ continue;
1657
+ // Exceptions are read LAST, and independently of the profile: an
1658
+ // arbitration a user made is theirs, and changing the level of requirement
1659
+ // never revokes one (PF9.3).
1660
+ applyExceptions(rule, raised, elements);
1661
+ violations.push(...raised);
1662
+ }
1663
+ return violations;
1664
+ }
1665
+ /** The registered rules a check-up runs — and nothing else ever evaluates. */
1666
+ export function onDemandRules(rules) {
1667
+ return rules.filter(rule => !isRealtime(rule));
1668
+ }
1669
+ /**
1670
+ * Run the ON-DEMAND rules over the surface (PF5.14). The exact mirror of
1671
+ * {@link evaluateRules}, for the moment the drawing path never touches.
1672
+ *
1673
+ * Synchronous and total: this is what a test, a host and a report call. The
1674
+ * manager drives the same pipeline one rule at a time so a long check-up can
1675
+ * yield the thread — see {@link ValidationManager.runCheckup} — and both reach
1676
+ * the same answer, because both are {@link runRules}.
1677
+ *
1678
+ * The findings are ordinary {@link Violation} objects and go through the same
1679
+ * profile and exception passes as any other. What makes them REMARKS rather
1680
+ * than verdicts is where they land: they never reach `violations$`, so they
1681
+ * never reach the timeline, the bracket or the badge. A framework declares them
1682
+ * `audit` on top of that, which is the severity for "collected, invisible to the
1683
+ * drawing user" — belt and braces, and the braces are structural.
1684
+ */
1685
+ export function evaluateCheckup(rules, elements, profiles = []) {
1686
+ return runRules(rules, elements, profiles, 'on-demand');
1687
+ }
1688
+ /**
1689
+ * How long a check-up may hold the thread before yielding it back.
1690
+ *
1691
+ * One frame, so a check-up that turns out to be expensive costs a dropped frame
1692
+ * at worst instead of a locked tab. A rule is the unit of interruption — the
1693
+ * smallest slice that has a meaning — so a single rule slower than this still
1694
+ * runs to completion and the next one waits for the following task.
1695
+ *
1696
+ * The rules shipped today cost well under a millisecond between them, so the
1697
+ * yield never fires in the delivered configuration — which would leave the
1698
+ * generation counter and the supersession path as code nothing reaches. It is
1699
+ * therefore a DEFAULT rather than a constant: {@link ValidationManager.checkupSliceMs}
1700
+ * is settable, and lowering it makes every rule a slice, which is how a test
1701
+ * exercises the yield and the race with the real rules instead of a stub.
1702
+ */
1703
+ export const CHECKUP_SLICE_MS = 16;
1704
+ /**
1705
+ * Grant `ruleId` an exception on `element`, unless it already has one.
1706
+ *
1707
+ * The write goes through the declared `@field()` accessor, so it lands in the
1708
+ * Y.Map, syncs to every peer, joins the undo stack and survives a copy — see the
1709
+ * note on `GfxPrimitiveElementModel.validationExceptions`.
1710
+ */
1711
+ export function grantException(element, ruleId, author, now = Date.now()) {
1712
+ if (hasException(element, ruleId))
1713
+ return;
1714
+ element.validationExceptions = [
1715
+ ...elementExceptions(element),
1716
+ // The timestamp is taken at the moment of the gesture, and an absent author
1717
+ // stays ABSENT rather than becoming an empty string.
1718
+ { ruleId, at: now, ...(author !== undefined ? { author } : {}) },
1719
+ ];
1720
+ }
1721
+ /** Remove `ruleId`'s exception from `element`, if it has one. */
1722
+ export function revokeException(element, ruleId) {
1723
+ const current = elementExceptions(element);
1724
+ const next = current.filter(exception => exception?.ruleId !== ruleId);
1725
+ if (next.length === current.length)
1726
+ return;
1727
+ if (next.length > 0) {
1728
+ element.validationExceptions = next;
1729
+ return;
1730
+ }
1731
+ // The last one goes, so the KEY goes. Assigning `undefined` through the
1732
+ // accessor would leave the key in the Y.Map holding an undefined value —
1733
+ // invisible through the getter, but synced to every peer and shipped in every
1734
+ // snapshot. `clearField` removes it, so an element whose exceptions were all
1735
+ // revoked really is indistinguishable from one that never had any, in the
1736
+ // document and not just in this tab.
1737
+ element.clearField('validationExceptions');
1738
+ }
1739
+ /**
1740
+ * The exceptions `element` ANSWERS FOR, i.e. the ones a menu on it should be
1741
+ * able to revoke.
1742
+ *
1743
+ * Same rule the canvas mark follows ({@link anchorOf}): an exception is
1744
+ * answered for by the outermost canvas group containing the element that
1745
+ * carries it, or by that element when it is not grouped. So the entry lives on
1746
+ * the whole Wardley component rather than on the bare node inside it, moves
1747
+ * down to the element the moment the group is dissolved, and appears on a
1748
+ * framework background for the map-wide arbitration it carries.
1749
+ *
1750
+ * Reads STORED exceptions, not current violations: an exception outlives the
1751
+ * violation that prompted it (move the component back onto the map and the
1752
+ * finding goes, the arbitration stays), and it is the arbitration the user is
1753
+ * asking to take back.
1754
+ */
1755
+ export function exceptionsAnchoredOn(element, surface) {
1756
+ const candidates = [element];
1757
+ if (element instanceof GfxGroupLikeElementModel) {
1758
+ for (const descendant of element.descendantElements) {
1759
+ if (descendant instanceof GfxPrimitiveElementModel) {
1760
+ candidates.push(descendant);
1761
+ }
1762
+ }
1763
+ }
1764
+ const anchored = [];
1765
+ for (const candidate of candidates) {
1766
+ const exceptions = elementExceptions(candidate);
1767
+ if (exceptions.length === 0)
1768
+ continue;
1769
+ // Anything whose anchor is somebody else belongs to somebody else's menu —
1770
+ // that is what keeps the entry on the outermost group and off its members.
1771
+ if (anchorOf(candidate.id, surface)?.id !== element.id)
1772
+ continue;
1773
+ for (const exception of exceptions) {
1774
+ if (exception?.ruleId) {
1775
+ anchored.push({ element: candidate, ruleId: exception.ruleId });
1776
+ }
1777
+ }
1778
+ }
1779
+ return anchored;
1780
+ }
1781
+ /** A framework registers its rules here; nothing else registers rules. */
1782
+ export const ValidationRuleIdentifier = createIdentifier('ValidationRule');
1783
+ /**
1784
+ * Register a framework's rules. Call it from the FLAG-GATED view extension —
1785
+ * validation is tooling, so a disabled framework must contribute nothing.
1786
+ *
1787
+ * ```ts
1788
+ * context.register(ValidationRuleExtension(WARDLEY_RULES));
1789
+ * ```
1790
+ */
1791
+ export function ValidationRuleExtension(rules) {
1792
+ return {
1793
+ setup: di => {
1794
+ for (const rule of rules) {
1795
+ di.addImpl(ValidationRuleIdentifier(rule.id), () => rule);
1796
+ }
1797
+ },
1798
+ };
1799
+ }
1800
+ /** A framework registers its profiles here; nothing else registers profiles. */
1801
+ export const ValidationProfileIdentifier = createIdentifier('ValidationProfile');
1802
+ /**
1803
+ * Register a framework's profiles. Call it from the FLAG-GATED view extension,
1804
+ * beside {@link ValidationRuleExtension}: a level of requirement is tooling, so
1805
+ * a disabled framework offers none — and the id already written on a background
1806
+ * simply goes unread until the flag comes back.
1807
+ *
1808
+ * ```ts
1809
+ * context.register(ValidationProfileExtension(WARDLEY_PROFILES));
1810
+ * ```
1811
+ */
1812
+ export function ValidationProfileExtension(profiles) {
1813
+ return {
1814
+ setup: di => {
1815
+ for (const profile of profiles) {
1816
+ di.addImpl(ValidationProfileIdentifier(profile.id), () => profile);
1817
+ }
1818
+ },
1819
+ };
1820
+ }
1821
+ /** Recompute delay, so a drag re-evaluates once instead of once per frame. */
1822
+ const VALIDATION_DELAY_MS = 120;
1823
+ /**
1824
+ * Element props that can change a verdict, WHATEVER the registered rules are.
1825
+ * Everything else — `opacity` and the other `@local()` fields, colours,
1826
+ * labels — is ignored, so brushing a canvas (`SpotlightManager` writes
1827
+ * `opacity` on every element it dims) neither re-evaluates the surface nor
1828
+ * pushes the pending evaluation further away.
1829
+ *
1830
+ * A framework can add to this list without the engine learning its name: see
1831
+ * {@link verdictPropsOf}.
1832
+ */
1833
+ export const VERDICT_PROPS = [
1834
+ 'xywh',
1835
+ 'rotate',
1836
+ 'role',
1837
+ // The two ends of a typed edge ARE the relation's orientation since
1838
+ // `docs/adr/0010`, so re-pointing one — or reversing the edge, which writes
1839
+ // both — changes a verdict exactly as much as moving a node does. Without
1840
+ // these, an inversion would leave the finding on screen until the next
1841
+ // unrelated drag woke the engine.
1842
+ 'source',
1843
+ 'target',
1844
+ // A user exception changes a verdict as much as a move does: without this,
1845
+ // granting one on a peer's tab would leave the mark up until the next drag.
1846
+ 'validationExceptions',
1847
+ // Changing the level of requirement re-judges everything measured against
1848
+ // this background (PF9). Written on a background, read for every finding
1849
+ // attributed to it — and, like an exception, it can be DELETED (choosing the
1850
+ // default back removes the key), which is why `touchesVerdict` reads
1851
+ // `oldValues` too.
1852
+ 'validationProfile',
1853
+ ];
1854
+ /**
1855
+ * {@link VERDICT_PROPS} plus what the REGISTERED rules make verdict-bearing.
1856
+ *
1857
+ * A framework background can carry visibility toggles, and one of them may be
1858
+ * the toggle that shows the frontiers a rule measures against — hide the
1859
+ * dashed columns of a Wardley map and the findings about them stop being
1860
+ * findings ({@link backgroundTransitionBands}). Without this the toggle would be
1861
+ * as inert as a colour: the marks would stay on screen until some unrelated
1862
+ * drag happened to wake the engine, which is a board that lies about a document
1863
+ * that has already changed.
1864
+ *
1865
+ * Derived from the DECLARATIONS the engine was handed, never from a name it
1866
+ * knows: `backgroundTransitionVisibleProps` reads the prop each framework
1867
+ * writes beside the line it gates. A framework whose background declares no
1868
+ * toggle contributes nothing and the set is the constant.
1869
+ */
1870
+ export function verdictPropsOf(rules) {
1871
+ const props = new Set(VERDICT_PROPS);
1872
+ for (const rule of rules) {
1873
+ if (rule.background === undefined)
1874
+ continue;
1875
+ for (const prop of backgroundTransitionVisibleProps(rule.background)) {
1876
+ props.add(prop);
1877
+ }
1878
+ }
1879
+ return props;
1880
+ }
1881
+ /**
1882
+ * Whether an `elementUpdated` payload can have changed a verdict.
1883
+ *
1884
+ * It has to read `oldValues` as well as `props`, because a Y.Map **delete**
1885
+ * fills only the former (`syncElementFromY` puts a key in `props` for `add` and
1886
+ * `update` only). `validationExceptions` is the first {@link VERDICT_PROPS}
1887
+ * entry whose normal life includes being removed — `xywh` and `rotate` are
1888
+ * never deleted, `role` is only ever added — so this branch had never been
1889
+ * exercised. Reading `props` alone means an UNDO of an exception, which deletes
1890
+ * the key, wakes nothing: the board keeps showing a finding as excused when the
1891
+ * document no longer says so, and its Revoke button becomes a no-op.
1892
+ *
1893
+ * A payload carrying neither is unreadable, so it counts as "might have": the
1894
+ * cost of a spurious re-evaluation is a debounce tick, the cost of a missed one
1895
+ * is a board that lies.
1896
+ */
1897
+ export function touchesVerdict(payload, watched = VERDICT_PROPS) {
1898
+ const { props, oldValues } = payload;
1899
+ if (!props && !oldValues)
1900
+ return true;
1901
+ for (const prop of watched) {
1902
+ if (props !== undefined && prop in props)
1903
+ return true;
1904
+ if (oldValues !== undefined && prop in oldValues)
1905
+ return true;
1906
+ }
1907
+ return false;
1908
+ }
1909
+ /** Shared answer for "this element is nobody's frame", allocated once. */
1910
+ const EMPTY_FRAMEWORKS = new Set();
1911
+ /**
1912
+ * Owns the evaluation and the reactive violation list. No-op until a framework
1913
+ * registers a rule.
1914
+ */
1915
+ export class ValidationManager extends InteractivityExtension {
1916
+ constructor() {
1917
+ super(...arguments);
1918
+ /**
1919
+ * Current violations. The reactive seam a host panel (wave 2) subscribes to;
1920
+ * wave 1 ships only the canvas affordance.
1921
+ */
1922
+ this.violations$ = signal([]);
1923
+ /**
1924
+ * Level 3 — what an AI audit reported, and ONLY that (PF14.1).
1925
+ *
1926
+ * A second signal rather than a marked subset of {@link violations$}, and the
1927
+ * reason is the invariant the whole audit seam exists to protect: **the
1928
+ * deterministic engine never depends on the AI**. Nothing in `evaluate()`
1929
+ * reads this signal, writes it or is slowed by it; nothing here is ever
1930
+ * recomputed, carried over by an incremental pass, or judged by a profile.
1931
+ * The 16 ms budget is measured on a function that cannot see this field.
1932
+ *
1933
+ * Merging the two lists and discriminating by severity would have been
1934
+ * cheaper to type and much more expensive to reason about: every consumer of
1935
+ * `violations$` — the overlay, the timeline, the bubble, the exception
1936
+ * toolbar, `userFacingViolations`, the incremental carry-over — would have
1937
+ * had to learn to skip entries it must never touch, and forgetting one of
1938
+ * them in some future slice would put a model's opinion behind a canvas
1939
+ * bracket. Two signals make that a type error instead of a review.
1940
+ *
1941
+ * The findings ARE violation objects (`AuditFinding` is assignable to
1942
+ * {@link Violation}, pinned by a test), so a host panel renders them with the
1943
+ * code it already has. They are session state, never persisted: an audit is
1944
+ * an opinion at a moment, and the document says what it contains, not what a
1945
+ * model thought of it on Tuesday.
1946
+ */
1947
+ this.auditFindings$ = signal([]);
1948
+ /**
1949
+ * Which violations are still FRESH, i.e. still owed the loud bracket.
1950
+ *
1951
+ * It lives here rather than inside the overlay because the affordance has two
1952
+ * halves on two different technologies — a canvas bracket that fades, and a
1953
+ * DOM badge that takes over — and they are mutually exclusive: the badge
1954
+ * appears only once the bracket is fully gone. Two copies of "when did I
1955
+ * first see this" would drift, and the handover would show both at once or
1956
+ * neither.
1957
+ *
1958
+ * Session state, never persisted: a document says which rules it breaks, not
1959
+ * when you happened to look at them.
1960
+ */
1961
+ this.timeline = new ViolationTimeline();
1962
+ /**
1963
+ * The last check-up (PF5.14), or `null` while none has been asked for.
1964
+ *
1965
+ * A SEPARATE signal from {@link violations$}, and that separation is the whole
1966
+ * design: a remark never reaches the timeline, the bracket or the badge,
1967
+ * because it never reaches the list those three read. "Outside the canvas
1968
+ * affordance" is therefore a property of the wiring, not a filter somebody has
1969
+ * to remember to apply.
1970
+ *
1971
+ * Reset to `null` on unmount and never persisted: a document says what it
1972
+ * contains, not when somebody last checked it.
1973
+ */
1974
+ this.checkup$ = signal(null);
1975
+ /**
1976
+ * The element whose Map quality panel is OPEN, or `null` — the seam between
1977
+ * the toolbar entry that asks for it (`validation-toolbar.ts`, PF7.11) and the
1978
+ * widget that draws it.
1979
+ *
1980
+ * A signal rather than a DOM call because the two live in different trees: the
1981
+ * entry is a lit template inside `editor-menu-button`'s shadow root, and the
1982
+ * panel is a widget on the root block. Session state, like every other "what
1983
+ * is open" in this file.
1984
+ */
1985
+ this.mapQualityFor$ = signal(null);
1986
+ this._pending = null;
1987
+ /**
1988
+ * Bumped by every {@link runCheckup}, so a run that yielded the thread can
1989
+ * tell it has been superseded and drop its partial results on the floor
1990
+ * instead of overwriting a newer answer.
1991
+ */
1992
+ this._checkupGeneration = 0;
1993
+ /**
1994
+ * How long {@link runCheckup} may hold the thread between two rules.
1995
+ *
1996
+ * Settable rather than fixed so the interruption and the supersession are
1997
+ * TESTABLE with the rules a framework actually ships: at the shipped default
1998
+ * the Wardley check-up costs half a millisecond and never yields, which would
1999
+ * leave the whole race path unreached by anything. Dropped to zero, every rule
2000
+ * becomes a slice.
2001
+ */
2002
+ this.checkupSliceMs = CHECKUP_SLICE_MS;
2003
+ /**
2004
+ * Element ids added, removed or updated since the last verdict — the dirty
2005
+ * set of PF5.13. Filled by the subscriptions, drained by every evaluation.
2006
+ */
2007
+ this._dirty = new Set();
2008
+ /** Whether {@link violations$} holds a verdict a dirty set can build on. */
2009
+ this._evaluated = false;
2010
+ /**
2011
+ * The ids that were framework BACKGROUNDS at the last evaluation.
2012
+ *
2013
+ * `no-overlap` already sends the surface back through a full sweep when a
2014
+ * frame is touched, and it finds a DELETED one by looking for its id in the
2015
+ * previous findings — the only trace a departed element leaves. That trace
2016
+ * does not exist when the frame's profile put the rule on `'off'`: the
2017
+ * findings measured against it were dropped by `applyProfiles`, so `previous`
2018
+ * is empty, the deletion looks like an ordinary one, and the overlap that
2019
+ * comes back to life under the default profile is never reported. No family
2020
+ * can close that hole — an element that is gone from the surface AND absent
2021
+ * from the findings is invisible to everything except a memory of it.
2022
+ *
2023
+ * So the manager keeps that memory. A dirty id that used to be a background
2024
+ * forces the full pass, whatever the previous findings say or fail to say —
2025
+ * which makes `incremental === full` true by construction rather than by the
2026
+ * luck of no shipped profile using `'off'` yet.
2027
+ */
2028
+ this._backgrounds = new Set();
2029
+ this._rules = null;
2030
+ this._verdictProps = null;
2031
+ this._nudges = null;
2032
+ this._profiles = null;
2033
+ this._subscriptions = [];
2034
+ this._disposeSurfaceEffect = null;
2035
+ }
2036
+ static { this.key = 'validation-manager'; }
2037
+ /**
2038
+ * Publish an audit's results. The ONLY writer of {@link auditFindings$}, and
2039
+ * deliberately dumb: it stores, it does not evaluate, re-judge or merge.
2040
+ *
2041
+ * Replaces rather than appends — a run's findings are a whole answer about
2042
+ * the board as it was, so accumulating two runs would show a user a
2043
+ * contradiction and call it a list.
2044
+ */
2045
+ setAuditFindings(findings) {
2046
+ this.auditFindings$.value = findings;
2047
+ }
2048
+ /** Registered rules, resolved once. Empty when every framework is flagged off. */
2049
+ get _activeRules() {
2050
+ this._rules ??= Array.from(this.std.provider.getAll(ValidationRuleIdentifier).values());
2051
+ return this._rules;
2052
+ }
2053
+ /**
2054
+ * The props worth re-evaluating for, given what is registered. Resolved once,
2055
+ * beside the rules it is derived from — see {@link verdictPropsOf}.
2056
+ */
2057
+ get _watchedProps() {
2058
+ this._verdictProps ??= verdictPropsOf(this._activeRules);
2059
+ return this._verdictProps;
2060
+ }
2061
+ /**
2062
+ * Registered nudges, resolved once. Empty when no framework ships one, or
2063
+ * when every framework is flagged off.
2064
+ */
2065
+ get _activeNudges() {
2066
+ this._nudges ??= Array.from(this.std.provider.getAll(QualityNudgeIdentifier).values());
2067
+ return this._nudges;
2068
+ }
2069
+ /**
2070
+ * Registered profiles, resolved once. Empty when no framework ships one, in
2071
+ * which case every rule is judged by its own declared severity.
2072
+ */
2073
+ get _activeProfiles() {
2074
+ this._profiles ??= Array.from(this.std.provider.getAll(ValidationProfileIdentifier).values());
2075
+ return this._profiles;
2076
+ }
2077
+ get _overlay() {
2078
+ return this.std.getOptional(OverlayIdentifier(ValidationOverlay.overlayName));
2079
+ }
2080
+ mounted() {
2081
+ // Flag off (no rule registered) => never subscribe, never evaluate.
2082
+ // The cost of validation on a board with no framework enabled is this
2083
+ // single length check, once.
2084
+ if (this._activeRules.length === 0)
2085
+ return;
2086
+ // The surface is a SIGNAL, not a fact: it can legitimately be null at
2087
+ // mount and arrive later, and it is replaced if the surface block is. A
2088
+ // one-shot peek here would leave validation silently dead for the whole
2089
+ // session — hence tracking it rather than reading it once.
2090
+ this._disposeSurfaceEffect = effect(() => {
2091
+ this._resubscribe(this.gfx.surface$.value);
2092
+ });
2093
+ }
2094
+ unmounted() {
2095
+ if (this._pending)
2096
+ clearTimeout(this._pending);
2097
+ this._pending = null;
2098
+ this._disposeSurfaceEffect?.();
2099
+ this._disposeSurfaceEffect = null;
2100
+ this._unsubscribe();
2101
+ this._dirty.clear();
2102
+ this._evaluated = false;
2103
+ this._backgrounds = new Set();
2104
+ this.timeline.clear();
2105
+ // Supersede any check-up still yielding between two rules, and forget the
2106
+ // last one: both are session state, and a stale answer outliving the
2107
+ // surface it was measured on is a panel that lies.
2108
+ this._checkupGeneration += 1;
2109
+ this.checkup$.value = null;
2110
+ this.mapQualityFor$.value = null;
2111
+ // Session state, and the session is over. An audit describes a board at a
2112
+ // moment; carrying one across a remount would show it against a different
2113
+ // surface (PF14.1).
2114
+ this.auditFindings$.value = [];
2115
+ super.unmounted();
2116
+ }
2117
+ _unsubscribe() {
2118
+ for (const subscription of this._subscriptions)
2119
+ subscription.unsubscribe();
2120
+ this._subscriptions = [];
2121
+ }
2122
+ _resubscribe(surface) {
2123
+ this._unsubscribe();
2124
+ if (!surface)
2125
+ return;
2126
+ this._subscriptions.push(surface.elementAdded.subscribe(({ id }) => this._schedule(id)));
2127
+ this._subscriptions.push(surface.elementRemoved.subscribe(({ id }) => {
2128
+ // A check-up measured on a map that no longer exists is an answer about
2129
+ // nothing. The panel is closed by then (the widget watches the same
2130
+ // signal), but the result would otherwise sit in memory waiting to be
2131
+ // shown against whatever the user opens next.
2132
+ this._forgetCheckupOf(id);
2133
+ this._schedule(id);
2134
+ }));
2135
+ this._subscriptions.push(surface.elementUpdated.subscribe(payload => {
2136
+ // A prop that cannot change a verdict must not even rearm the timer.
2137
+ if (!touchesVerdict(payload, this._watchedProps))
2138
+ return;
2139
+ this._schedule(payload.id);
2140
+ }));
2141
+ // A new surface knows nothing about the last one: judge it in full.
2142
+ this.evaluate();
2143
+ }
2144
+ _schedule(id) {
2145
+ this._dirty.add(id);
2146
+ if (this._pending)
2147
+ clearTimeout(this._pending);
2148
+ this._pending = setTimeout(() => {
2149
+ this._pending = null;
2150
+ this.evaluate(true);
2151
+ }, VALIDATION_DELAY_MS);
2152
+ }
2153
+ /**
2154
+ * Evaluate now. Exposed so a host (and the bench) can drive it directly.
2155
+ *
2156
+ * `incremental` is the DEBOUNCED path's privilege and nobody else's: it is
2157
+ * the only caller that knows exactly which elements moved since the last
2158
+ * verdict. Everything else — a gesture that must land on the spot, a surface
2159
+ * arriving, a host asking — pays for a full pass, because a full pass is
2160
+ * always right and a wrong dirty set is a board that lies.
2161
+ */
2162
+ evaluate(incremental = false) {
2163
+ const rules = this._activeRules;
2164
+ const surface = this.gfx.surface;
2165
+ // Whatever happens below, what accumulated is now accounted for: a dirty
2166
+ // set left behind would be replayed against a later, unrelated snapshot.
2167
+ const dirty = this._dirty;
2168
+ this._dirty = new Set();
2169
+ if (rules.length === 0 || !surface)
2170
+ return;
2171
+ const previous = this.violations$.peek();
2172
+ // A frame that WAS there and is now dirty invalidates attributions no
2173
+ // pair-wise re-test would reach — including, when its profile silenced the
2174
+ // rule, findings that were never recorded for the family to find.
2175
+ const frameTouched = [...dirty].some(id => this._backgrounds.has(id));
2176
+ const violations = evaluateRules(rules, surface.elementModels, this._activeProfiles, incremental && this._evaluated && dirty.size > 0 && !frameTouched
2177
+ ? { dirty, previous }
2178
+ : undefined);
2179
+ this._evaluated = true;
2180
+ this._backgrounds = backgroundElementIds(rules, surface.elementModels);
2181
+ // Stay silent when nothing changed: `violations$` is the seam a host panel
2182
+ // subscribes to, and a clean board must not wake it on every debounce tick.
2183
+ if (violations.length === 0 && this.violations$.peek().length === 0)
2184
+ return;
2185
+ // Age the marks BEFORE waking anybody, so the canvas half and the DOM half
2186
+ // read the same clock within one evaluation and never disagree about which
2187
+ // of the two markers is due.
2188
+ //
2189
+ // Exempted findings are dropped HERE and only here: `violations$` keeps
2190
+ // them, so the bubble and a host panel see the full picture, while the
2191
+ // flash and the bracket see only what is still asking for something.
2192
+ const shown = liveViolations(userFacingViolations(violations));
2193
+ this.timeline.sync(shown, performance.now());
2194
+ this.violations$.value = violations;
2195
+ this._overlay?.setViolations(shown, this.timeline);
2196
+ }
2197
+ /** The registered rule with this id, if its framework is enabled. */
2198
+ ruleOf(ruleId) {
2199
+ return this._activeRules.find(rule => rule.id === ruleId);
2200
+ }
2201
+ /**
2202
+ * The profiles selectable on `element`, i.e. those of every framework that
2203
+ * measures against a background carrying this element's ROLE.
2204
+ *
2205
+ * Derived from the registered rules rather than from a second registry: a
2206
+ * rule already names the role of the frame it measures against
2207
+ * (`backgroundRole`), which is the only thing that makes an element a root
2208
+ * instance in the engine's eyes. So the answer is gated for free — flag off,
2209
+ * no rule, no profile offered — and a framework that ships profiles but no
2210
+ * rule offers nothing to choose between, correctly.
2211
+ *
2212
+ * Empty for a neutral element, and for a background authored before its role
2213
+ * existed: no role, no framework, no profile.
2214
+ */
2215
+ profilesFor(element) {
2216
+ const frameworks = this.frameworksOf(element);
2217
+ if (frameworks.size === 0)
2218
+ return [];
2219
+ return this._activeProfiles.filter(profile => frameworks.has(profile.framework));
2220
+ }
2221
+ /**
2222
+ * The frameworks `element` is a ROOT INSTANCE of, i.e. those with a registered
2223
+ * rule measuring against a background carrying this element's role.
2224
+ *
2225
+ * The one test that decides whether an element is a framework's frame, and the
2226
+ * only one: it is derived from the registered rules rather than from a second
2227
+ * registry, so it is gated for free — flag off, no rule, no framework — and
2228
+ * every per-instance surface (the profile picker, the nudge checklist, the
2229
+ * check-up) agrees about what a root instance is without any of them saying so
2230
+ * twice.
2231
+ *
2232
+ * Empty for a neutral element, and for a background authored before its role
2233
+ * existed.
2234
+ */
2235
+ frameworksOf(element) {
2236
+ const role = element.role;
2237
+ if (role === undefined)
2238
+ return EMPTY_FRAMEWORKS;
2239
+ const frameworks = new Set();
2240
+ for (const rule of this._activeRules) {
2241
+ if (rule.backgroundRole === undefined)
2242
+ continue;
2243
+ if (roleIsA(role, rule.backgroundRole, rule.roles)) {
2244
+ frameworks.add(rule.framework);
2245
+ }
2246
+ }
2247
+ return frameworks;
2248
+ }
2249
+ /**
2250
+ * The nudges offered on `element` (PF7.10), in declared order.
2251
+ *
2252
+ * Same derivation as {@link profilesFor} and gated the same way: a framework
2253
+ * that registers no nudge — or whose flag is off — offers none, and the ids
2254
+ * already ticked on the element stay written, unread, until it comes back.
2255
+ */
2256
+ nudgesFor(element) {
2257
+ const frameworks = this.frameworksOf(element);
2258
+ if (frameworks.size === 0)
2259
+ return [];
2260
+ return this._activeNudges
2261
+ .filter(nudge => frameworks.has(nudge.framework))
2262
+ .sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
2263
+ }
2264
+ /** The on-demand rules a check-up on `element` would walk (PF5.14). */
2265
+ checkupRulesFor(element) {
2266
+ const frameworks = this.frameworksOf(element);
2267
+ if (frameworks.size === 0)
2268
+ return [];
2269
+ return onDemandRules(this._activeRules).filter(rule => frameworks.has(rule.framework));
2270
+ }
2271
+ /**
2272
+ * Whether `element` has a Map quality panel to open at all: a framework of
2273
+ * its own declaring a NUDGE.
2274
+ *
2275
+ * The panel is the checklist and nothing else since the PO recette of
2276
+ * 02/08/2026, so this asks for the checklist and nothing else — a framework
2277
+ * that declared only on-demand rules would otherwise be offered a panel with
2278
+ * nothing in it. {@link checkupRulesFor} and {@link runCheckup} are unchanged
2279
+ * and unrelated: a host can still ask for a check-up, it simply is not what
2280
+ * this panel is.
2281
+ *
2282
+ * Generic by construction (PF7.11) — nothing here names a framework. A second
2283
+ * framework shipping nudges gets the entry and the panel with no code written
2284
+ * anywhere.
2285
+ */
2286
+ hasMapQuality(element) {
2287
+ return this.nudgesFor(element).length > 0;
2288
+ }
2289
+ /**
2290
+ * Open the Map quality panel on `element`; the widget draws it.
2291
+ *
2292
+ * Opening on a DIFFERENT instance forgets the last check-up. The panel already
2293
+ * refuses to render a run belonging to another map ({@link CheckupRun.backgroundId}),
2294
+ * so this is not what makes the display correct — it is what stops a result
2295
+ * measured on a map the user has moved on from (or deleted) sitting in memory
2296
+ * waiting to be shown again. Reopening on the SAME map keeps its stamp, which
2297
+ * is the one case where "last check-up: 01:51" is worth reading.
2298
+ */
2299
+ openMapQuality(element) {
2300
+ this._forgetCheckupOf(element.id, true);
2301
+ this.mapQualityFor$.value = element.id;
2302
+ }
2303
+ closeMapQuality() {
2304
+ this.mapQualityFor$.value = null;
2305
+ }
2306
+ /**
2307
+ * Drop the current check-up when it is about `backgroundId` — or, with
2308
+ * `unless`, when it is about anything else.
2309
+ *
2310
+ * The two callers are the two ways a run stops being about something the user
2311
+ * can look at: the instance it measured was deleted, and the panel moved to a
2312
+ * different instance.
2313
+ */
2314
+ _forgetCheckupOf(backgroundId, unless = false) {
2315
+ const run = this.checkup$.peek();
2316
+ if (run === null)
2317
+ return;
2318
+ if ((run.backgroundId === backgroundId) !== unless) {
2319
+ this.checkup$.value = null;
2320
+ }
2321
+ }
2322
+ /**
2323
+ * Run the check-up on `element` (PF5.14).
2324
+ *
2325
+ * ## It is about ONE instance
2326
+ *
2327
+ * The walk covers the whole surface — that is where the elements are — but the
2328
+ * ANSWER is narrowed to the map that was asked about, on
2329
+ * {@link Violation.backgroundId}, which every family measuring against a frame
2330
+ * already records. A board carrying two Wardley maps holds two independent
2331
+ * answers, and handing the panel a tally over both would be exactly the
2332
+ * whole-surface count {@link evaluateMajorityFact} goes out of its way not to
2333
+ * compute. Narrowed HERE and not at the rendering: a run that reaches a host,
2334
+ * a report or the agent has to already be about one map.
2335
+ *
2336
+ * ## Why this is async, for two rules that take a microsecond
2337
+ *
2338
+ * Because the moment exists precisely so that a framework may ship a control
2339
+ * that does NOT fit a frame. A check-up walks the surface with no dirty set
2340
+ * and no budget over its head, and the honest way to offer that is to be
2341
+ * interruptible: the driver evaluates ONE RULE at a time — the smallest slice
2342
+ * that has a meaning — and yields the thread whenever the slice has held it
2343
+ * for {@link checkupSliceMs}. The panel sees `done` climb towards `total`, so
2344
+ * progress is the same value the results arrive in and there is no second
2345
+ * signal to keep in step.
2346
+ *
2347
+ * A run started while another is still yielding SUPERSEDES it: the older one
2348
+ * notices its generation is stale on its next slice and drops what it had.
2349
+ * Clicking twice therefore gives the second answer, never a race between two.
2350
+ *
2351
+ * ## A rule that throws
2352
+ *
2353
+ * ...ends the run, visibly. The failure must not leave the panel believing a
2354
+ * check-up is in flight — that reads as "Checking…" for ever and disables the
2355
+ * one button that could try again, on every map, until the editor is
2356
+ * remounted. So the run is finished with {@link CheckupRun.error} and whatever
2357
+ * it had managed to collect.
2358
+ *
2359
+ * @returns the finished run, or `null` if it was superseded or there was
2360
+ * nothing to run.
2361
+ */
2362
+ async runCheckup(element) {
2363
+ const rules = this.checkupRulesFor(element);
2364
+ const generation = ++this._checkupGeneration;
2365
+ const backgroundId = element.id;
2366
+ if (rules.length === 0) {
2367
+ this.checkup$.value = null;
2368
+ return null;
2369
+ }
2370
+ const at = Date.now();
2371
+ const results = [];
2372
+ const total = rules.length;
2373
+ let sliceStart = performance.now();
2374
+ this.checkup$.value = { backgroundId, at, results: [], done: 0, total };
2375
+ for (let i = 0; i < total; i++) {
2376
+ // The surface is re-read on every slice: the user can keep drawing while
2377
+ // this runs, and a rule must be judged against the board as it is now,
2378
+ // not against a snapshot taken before the last yield.
2379
+ const surface = this.gfx.surface;
2380
+ if (!surface || generation !== this._checkupGeneration)
2381
+ return null;
2382
+ try {
2383
+ for (const remark of evaluateCheckup([rules[i]], surface.elementModels, this._activeProfiles)) {
2384
+ // This map's remarks, and only this map's. A family that measures
2385
+ // against no frame records no `backgroundId` and is dropped: it has
2386
+ // said nothing about the instance the user is looking at.
2387
+ if (remark.backgroundId === backgroundId)
2388
+ results.push(remark);
2389
+ }
2390
+ }
2391
+ catch (error) {
2392
+ console.error(`[validation] check-up rule "${rules[i].id}" threw`, error);
2393
+ const failed = {
2394
+ backgroundId,
2395
+ at,
2396
+ results: [...results],
2397
+ // Finished, deliberately: see the note above. A locked button is a
2398
+ // worse failure than a reported one.
2399
+ done: total,
2400
+ total,
2401
+ error: true,
2402
+ };
2403
+ this.checkup$.value = failed;
2404
+ return failed;
2405
+ }
2406
+ this.checkup$.value = {
2407
+ backgroundId,
2408
+ at,
2409
+ results: [...results],
2410
+ done: i + 1,
2411
+ total,
2412
+ };
2413
+ if (i + 1 < total &&
2414
+ performance.now() - sliceStart > this.checkupSliceMs) {
2415
+ // Back of the queue, so paint and input go first. `setTimeout(0)` and
2416
+ // not a microtask: a microtask would yield the call stack and nothing
2417
+ // else, which is exactly the freeze this exists to avoid.
2418
+ await new Promise(resolve => setTimeout(resolve, 0));
2419
+ sliceStart = performance.now();
2420
+ }
2421
+ }
2422
+ return generation === this._checkupGeneration
2423
+ ? this.checkup$.peek()
2424
+ : null;
2425
+ }
2426
+ /**
2427
+ * The profile `element` is actually checked against — the one it names, or
2428
+ * its framework's default. `undefined` when it is not a root instance of any
2429
+ * enabled framework, or when its framework ships no profile.
2430
+ */
2431
+ profileOf(element) {
2432
+ const available = this.profilesFor(element);
2433
+ const named = available.find(profile => profile.id === element.validationProfile);
2434
+ if (named)
2435
+ return named;
2436
+ const framework = available[0]?.framework;
2437
+ return framework === undefined
2438
+ ? undefined
2439
+ : defaultProfileOf(available, framework);
2440
+ }
2441
+ /**
2442
+ * Put `element` on `profileId`, and re-judge the board on the spot.
2443
+ *
2444
+ * Choosing the DEFAULT clears the key rather than writing it, so a background
2445
+ * that never left the default stays byte-identical to one created before
2446
+ * profiles existed — and a user who tries strict and comes back leaves no
2447
+ * trace. Everything else is one flat string in the same `@field()` the role
2448
+ * and the exceptions live in: it syncs, it undoes, it survives a copy and an
2449
+ * export, with no block schema change.
2450
+ *
2451
+ * Exceptions are deliberately left alone. A level of requirement is not an
2452
+ * amnesty: raising it must not resurrect decisions the user made, and
2453
+ * lowering it must not quietly delete them (PF9.3).
2454
+ *
2455
+ * @returns whether the document actually changed.
2456
+ */
2457
+ setProfile(element, profileId) {
2458
+ // The write below goes through a `@field()` accessor, i.e. `store.transact`,
2459
+ // which has no readonly guard of its own. A readonly board arbitrates
2460
+ // nothing — and `false` keeps the caller's telemetry silent too.
2461
+ if (this.std.store.readonly)
2462
+ return false;
2463
+ const available = this.profilesFor(element);
2464
+ const target = available.find(profile => profile.id === profileId);
2465
+ // Never write a profile nobody registered, nor one belonging to a framework
2466
+ // this element is not a background of.
2467
+ if (!target)
2468
+ return false;
2469
+ const isDefault = defaultProfileOf(available, target.framework)?.id === target.id;
2470
+ const next = isDefault ? undefined : target.id;
2471
+ if ((element.validationProfile ?? undefined) === next)
2472
+ return false;
2473
+ if (next === undefined)
2474
+ element.clearField('validationProfile');
2475
+ else
2476
+ element.validationProfile = next;
2477
+ // The 120 ms debounce would get there on its own; the gesture has to land
2478
+ // immediately, exactly like an exception does.
2479
+ this.evaluate();
2480
+ return true;
2481
+ }
2482
+ /**
2483
+ * Which elements an exception of the given scope is written on.
2484
+ *
2485
+ * `element` — the elements the rule actually indicts, which is what makes the
2486
+ * exception local (PF8.2): excusing one component says nothing about the next.
2487
+ *
2488
+ * `map` — the background element these findings were MEASURED AGAINST, and
2489
+ * only that one. It comes from {@link Violation.backgroundId}, recorded by the
2490
+ * rule family when it picked the frame, so a board carrying three maps holds
2491
+ * three independent arbitrations and a gesture made on one writes on one.
2492
+ * That choice is what lets a map-wide arbitration exist with NO block schema
2493
+ * change and no hidden per-document store: it is one more value in the same
2494
+ * `@field()`, on an element that is selected, copied, exported and deleted
2495
+ * like any other. A family that measures against no background records no
2496
+ * `backgroundId`, has no map to hang an exception on, and offers no map scope.
2497
+ */
2498
+ _targetsOf(violations, scope, surface) {
2499
+ const ids = scope === 'map'
2500
+ ? new Set(violations
2501
+ .map(violation => violation.backgroundId)
2502
+ .filter((id) => id !== undefined))
2503
+ : new Set(violations.flatMap(violation => violation.elementIds));
2504
+ return Array.from(ids)
2505
+ .map(id => surface.getElementById(id))
2506
+ .filter((el) => el !== null);
2507
+ }
2508
+ /**
2509
+ * Grant or revoke `ruleId`'s exception at `scope`, over the elements the given
2510
+ * violations indict.
2511
+ *
2512
+ * Takes the whole set rather than one violation on purpose: a Wardley
2513
+ * component is a GROUP, so one bubble line can stand for several indicted
2514
+ * members, and one click has to settle all of them.
2515
+ *
2516
+ * Re-evaluates on the spot instead of waiting for the 120 ms debounce the
2517
+ * field write would trigger anyway — the gesture must apply immediately
2518
+ * (PF8.1).
2519
+ *
2520
+ * @returns the elements actually written to, so the caller can tell a real
2521
+ * arbitration from a no-op and only report the former.
2522
+ */
2523
+ setException(violations, scope, granted, author) {
2524
+ // Same transact hole as `setProfile`; an empty return is what keeps the
2525
+ // bubble from emitting Granted/Revoked telemetry for a write that never
2526
+ // happened.
2527
+ if (this.std.store.readonly)
2528
+ return [];
2529
+ const ruleId = violations[0]?.ruleId;
2530
+ const surface = this.gfx.surface;
2531
+ if (ruleId === undefined || !surface)
2532
+ return [];
2533
+ // Never write an exception for a rule nobody registered: with the framework
2534
+ // flagged off there is no rule to arbitrate on.
2535
+ if (!this.ruleOf(ruleId))
2536
+ return [];
2537
+ const targets = this._targetsOf(violations, scope, surface).filter(element => hasException(element, ruleId) !== granted);
2538
+ if (targets.length === 0)
2539
+ return [];
2540
+ // One timestamp for the whole gesture: a single click is a single decision,
2541
+ // whatever number of elements it happens to touch.
2542
+ const at = Date.now();
2543
+ for (const element of targets) {
2544
+ if (granted)
2545
+ grantException(element, ruleId, author, at);
2546
+ else
2547
+ revokeException(element, ruleId);
2548
+ }
2549
+ this.evaluate();
2550
+ return targets;
2551
+ }
2552
+ /**
2553
+ * The exceptions on `element` that can still be taken back — those it answers
2554
+ * for ({@link exceptionsAnchoredOn}) whose rule is actually REGISTERED.
2555
+ *
2556
+ * The rule filter is what makes the menu entry disappear with the framework:
2557
+ * flag off, no rule reaches the container, so there is nothing to arbitrate
2558
+ * on and nothing to offer. The exceptions themselves are untouched — they are
2559
+ * document data and outlive the tooling (PF8.6).
2560
+ */
2561
+ revocableExceptionsOn(element) {
2562
+ const surface = this.gfx.surface;
2563
+ if (!surface)
2564
+ return [];
2565
+ return exceptionsAnchoredOn(element, surface).filter(({ ruleId }) => this.ruleOf(ruleId) !== undefined);
2566
+ }
2567
+ /**
2568
+ * Take back every exception `element` answers for, in one gesture.
2569
+ *
2570
+ * @returns one entry per (rule, scope) actually written, so the caller can
2571
+ * report the arbitration and tell a real one from a no-op.
2572
+ */
2573
+ revokeExceptionsOn(element) {
2574
+ // Same transact hole as `setProfile`, same telemetry contract: no entry
2575
+ // returned, nothing reported.
2576
+ if (this.std.store.readonly)
2577
+ return [];
2578
+ const anchored = this.revocableExceptionsOn(element);
2579
+ if (anchored.length === 0)
2580
+ return [];
2581
+ const reported = new Map();
2582
+ for (const { element: target, ruleId } of anchored) {
2583
+ const rule = this.ruleOf(ruleId);
2584
+ if (!rule)
2585
+ continue;
2586
+ revokeException(target, ruleId);
2587
+ // An exception written on the framework's own background IS the map-wide
2588
+ // one — the same test `applyExceptions` uses to read it back.
2589
+ const scope = rule.backgroundRole !== undefined &&
2590
+ target.role !== undefined &&
2591
+ roleIsA(target.role, rule.backgroundRole, rule.roles)
2592
+ ? 'map'
2593
+ : 'element';
2594
+ const key = `${ruleId}|${scope}`;
2595
+ const entry = reported.get(key);
2596
+ if (entry)
2597
+ entry.elementCount += 1;
2598
+ else {
2599
+ reported.set(key, {
2600
+ ruleId,
2601
+ framework: rule.framework,
2602
+ scope,
2603
+ elementCount: 1,
2604
+ });
2605
+ }
2606
+ }
2607
+ this.evaluate();
2608
+ return Array.from(reported.values());
2609
+ }
2610
+ }
2611
+ /**
2612
+ * Where a mark is DRAWN for one violating element: the outermost canvas group
2613
+ * containing it, or the element itself when it is not grouped.
2614
+ *
2615
+ * A RENDERING decision, not a semantic one. The violation object keeps naming
2616
+ * the elements actually at fault (`elementIds`); the group never appears in it,
2617
+ * is never evaluated, and carries no role. This exists because a mark drawn on
2618
+ * a lone member — a Wardley component is a group of {node, label} — collides
2619
+ * with the group's own selection rect and becomes unreadable.
2620
+ *
2621
+ * Frames are deliberately skipped: they are group-compatible BLOCKS and appear
2622
+ * in the group chain, but framing a whole frame would be a wild over-reach.
2623
+ */
2624
+ function anchorOf(elementId, surface) {
2625
+ const element = surface.getElementById(elementId);
2626
+ if (!element)
2627
+ return null;
2628
+ // `getGroups` walks outward, so the last CANVAS group is the outermost one.
2629
+ const canvasGroups = surface
2630
+ .getGroups(elementId)
2631
+ .filter(group => group instanceof GfxGroupLikeElementModel);
2632
+ return canvasGroups[canvasGroups.length - 1] ?? element;
2633
+ }
2634
+ /**
2635
+ * The MIDDLE of a drawn path, by arc length — not the middle point of the
2636
+ * array, which on an elbowed connector is wherever the router happened to put a
2637
+ * bend.
2638
+ */
2639
+ function pathMidpoint(path) {
2640
+ if (path.length === 0)
2641
+ return null;
2642
+ if (path.length === 1)
2643
+ return [path[0][0], path[0][1]];
2644
+ let total = 0;
2645
+ for (let i = 1; i < path.length; i++) {
2646
+ total += Math.hypot(path[i][0] - path[i - 1][0], path[i][1] - path[i - 1][1]);
2647
+ }
2648
+ // A degenerate path (every point on top of the last) has no middle to find;
2649
+ // any of its points is the answer.
2650
+ if (total === 0)
2651
+ return [path[0][0], path[0][1]];
2652
+ let walked = 0;
2653
+ for (let i = 1; i < path.length; i++) {
2654
+ const from = path[i - 1];
2655
+ const to = path[i];
2656
+ const length = Math.hypot(to[0] - from[0], to[1] - from[1]);
2657
+ if (walked + length >= total / 2) {
2658
+ const t = length === 0 ? 0 : (total / 2 - walked) / length;
2659
+ return [from[0] + (to[0] - from[0]) * t, from[1] + (to[1] - from[1]) * t];
2660
+ }
2661
+ walked += length;
2662
+ }
2663
+ return [path[path.length - 1][0], path[path.length - 1][1]];
2664
+ }
2665
+ /**
2666
+ * Whether the mark on this element belongs on the MIDDLE of a link rather than
2667
+ * on a corner of a box.
2668
+ *
2669
+ * Two independent answers, either of which is enough, because they are the two
2670
+ * ways an edge can be one:
2671
+ *
2672
+ * 1. its ROLE says so — a declared role of `kind: 'edge'`, which is the
2673
+ * semantic answer and the one a framework owns;
2674
+ * 2. its GEOMETRY says so — the element exposes a path ({@link elementPath}),
2675
+ * which covers a generalist connector carrying no role at all, and an
2676
+ * element whose framework is not loaded.
2677
+ *
2678
+ * Vocabularies are optional: a caller that has none (the canvas overlay, which
2679
+ * only ever reads `bound`) still gets the geometric answer.
2680
+ */
2681
+ function anchorKindOf(element, vocabularies) {
2682
+ // A GROUP is never an edge, whatever it contains: the mark frames the group's
2683
+ // box, and the middle of that box is not on any of its members.
2684
+ if (element instanceof GfxGroupLikeElementModel)
2685
+ return 'node';
2686
+ const role = element.role;
2687
+ for (const defs of vocabularies) {
2688
+ const def = role === undefined ? undefined : defs[role];
2689
+ if (def?.kind === 'edge')
2690
+ return 'edge';
2691
+ }
2692
+ return elementPath(element) !== null ? 'edge' : 'node';
2693
+ }
2694
+ /**
2695
+ * Where the badge for `element` is pinned, in model space.
2696
+ *
2697
+ * Exported so a test can assert the coordinate rather than the rendering, and
2698
+ * so the widget converts ONE point instead of reproducing the offsets.
2699
+ */
2700
+ function markPointOf(element, kind, bound) {
2701
+ if (kind === 'edge') {
2702
+ const middle = pathMidpoint(elementPath(element) ?? []);
2703
+ // The intersection of the rectangle's diagonals — its centre — for an edge
2704
+ // the layout has not routed yet.
2705
+ return middle ?? [bound.x + bound.w / 2, bound.y + bound.h / 2];
2706
+ }
2707
+ // Outside the corner by the mark's gap plus half a badge, so it does not land
2708
+ // under the selected-rect north-east resize handle.
2709
+ const offset = VIOLATION_MARK_PADDING + VIOLATION_BADGE_SIZE / 2;
2710
+ return [bound.maxX + offset, bound.y - offset];
2711
+ }
2712
+ /**
2713
+ * Group violations by the anchor their mark is drawn on — one entry per
2714
+ * anchor, so several violating members of the same group share a single
2715
+ * bracket, a single badge and a single bubble.
2716
+ *
2717
+ * Pure and stateless, and called at paint time by both halves of the
2718
+ * affordance, so the mark follows a group that moves and falls back onto the
2719
+ * element the moment the group is dissolved, with nothing to invalidate.
2720
+ *
2721
+ * @param vocabularies the registered role vocabularies, for the SEMANTIC half
2722
+ * of "is this an edge" ({@link anchorKindOf}). Optional: a caller that only
2723
+ * reads `bound` — the canvas overlay draws the same bracket either way — needs
2724
+ * none, and still gets the geometric answer.
2725
+ */
2726
+ export function resolveViolationAnchors(violations, surface, vocabularies = []) {
2727
+ const anchors = new Map();
2728
+ for (const violation of violations) {
2729
+ for (const id of violation.elementIds) {
2730
+ const anchor = anchorOf(id, surface);
2731
+ if (!anchor)
2732
+ continue;
2733
+ let entry = anchors.get(anchor.id);
2734
+ if (!entry) {
2735
+ const bound = anchor.elementBound;
2736
+ const kind = anchorKindOf(anchor, vocabularies);
2737
+ entry = {
2738
+ id: anchor.id,
2739
+ bound,
2740
+ kind,
2741
+ markAt: markPointOf(anchor, kind, bound),
2742
+ violations: [],
2743
+ };
2744
+ anchors.set(anchor.id, entry);
2745
+ }
2746
+ // A single violation naming two members of one group resolves to that
2747
+ // group twice and must be recorded once. Two SEPARATE violations of the
2748
+ // same rule stay separate here — collapsing what is DISPLAYED is the
2749
+ // bubble's call, not this function's.
2750
+ if (!entry.violations.includes(violation)) {
2751
+ entry.violations.push(violation);
2752
+ }
2753
+ }
2754
+ }
2755
+ return Array.from(anchors.values());
2756
+ }
2757
+ /**
2758
+ * Violations the drawing user is meant to SEE. `audit` is collected for
2759
+ * reporting and stays invisible on the canvas — that is what the severity
2760
+ * means (see {@link ViolationSeverity}), and the UI is the only place that can
2761
+ * honour it: the engine reports everything, unfiltered, to the host panel.
2762
+ */
2763
+ export function userFacingViolations(violations) {
2764
+ return violations.filter(violation => violation.severity !== 'audit');
2765
+ }
2766
+ /**
2767
+ * Violations still asking for something, i.e. not covered by an exception.
2768
+ *
2769
+ * This is the whole of PF8's canvas behaviour: an exempted finding is dropped
2770
+ * from the timeline, so it never flashes, and from the overlay, so it carries no
2771
+ * bracket. It keeps its badge and its line in the bubble, where it reads as
2772
+ * "exception" and can be revoked — the state changed, the finding did not
2773
+ * disappear.
2774
+ */
2775
+ export function liveViolations(violations) {
2776
+ return violations.filter(violation => violation.exemption === undefined);
2777
+ }
2778
+ /**
2779
+ * One violation per RULE, first occurrence wins.
2780
+ *
2781
+ * What a detail bubble renders is rule-level — a label, a severity, a hint —
2782
+ * so two components of the same group both drawn off the map, which are two
2783
+ * violations, would produce the same sentence twice. The engine is right to
2784
+ * report both (it names the elements); a list that repeats itself is just
2785
+ * noise. Which member moved is on the canvas, not in a list.
2786
+ */
2787
+ export function distinctByRule(violations) {
2788
+ const byRule = new Map();
2789
+ for (const violation of violations) {
2790
+ const kept = byRule.get(violation.ruleId);
2791
+ // A LIVE finding always wins the slot: two members of one group, one
2792
+ // excused and one not, must read as "this rule still applies here" rather
2793
+ // than as an exception that quietly covers both.
2794
+ if (kept === undefined || (kept.exemption && !violation.exemption)) {
2795
+ byRule.set(violation.ruleId, violation);
2796
+ }
2797
+ }
2798
+ return Array.from(byRule.values());
2799
+ }
2800
+ /**
2801
+ * How loudly an ANCHOR is drawn: the loudest of the violations sharing it, so
2802
+ * a fresh finding is not muted by an older one on the same group.
2803
+ *
2804
+ * The single arbiter between the two markers. `> 0` means the bracket still has
2805
+ * the anchor; exactly `0` means it is done and the badge takes over. Both
2806
+ * halves call this, on the same timeline, so they can never both claim it.
2807
+ */
2808
+ export function anchorEmphasis(anchor, timeline, now) {
2809
+ let emphasis = 0;
2810
+ for (const violation of anchor.violations) {
2811
+ emphasis = Math.max(emphasis, timeline.emphasis(violation, now));
2812
+ }
2813
+ return emphasis;
2814
+ }
2815
+ /**
2816
+ * Mark geometry in MODEL units, deliberately — the affordance zooms with the
2817
+ * board like any element on it.
2818
+ *
2819
+ * The house convention for annotation overlays is the opposite (divide by the
2820
+ * zoom, keep a constant size on screen), and that is right for a snap guide,
2821
+ * which is transient and never numerous. It is wrong here. The dimensioning
2822
+ * case is a hundred-component map, zoomed out: screen-constant marks grow
2823
+ * relative to the content they annotate until the brackets are all you can see
2824
+ * and the map underneath is unreadable. Zoomed out far enough these become
2825
+ * small along with everything else, and that is the point — a board you cannot
2826
+ * read is a worse answer than a mark you have to zoom in to inspect.
2827
+ *
2828
+ * The one exception is the CLICK target, which lives in DOM and keeps a screen
2829
+ * minimum so it stays reachable by thumb (see the widget). Invisible padding
2830
+ * around a model-sized visual: the pattern `edgeless-auto-complete` already
2831
+ * uses on this canvas.
2832
+ */
2833
+ const MARK_CORNER = 10;
2834
+ const MARK_LINE_WIDTH = 2;
2835
+ /**
2836
+ * Gap between the anchor's bounds and the mark, in MODEL units. Exported
2837
+ * because the badge sits ON the bracket's top-right corner and has to use the
2838
+ * same gap to land there.
2839
+ */
2840
+ export const VIOLATION_MARK_PADDING = 6;
2841
+ /** Diameter of the persistent badge, in MODEL units. */
2842
+ export const VIOLATION_BADGE_SIZE = 16;
2843
+ /** The one amber the affordance is drawn in, canvas and DOM alike. */
2844
+ export const VIOLATION_MARK_COLOR = '#f5a623';
2845
+ /**
2846
+ * The EPHEMERAL half of the PF7 affordance: amber corner brackets that flash on
2847
+ * the anchor of a violation the moment it appears, hold for a few seconds, then
2848
+ * fade out (see {@link ViolationTimeline}). What stays behind afterwards is the
2849
+ * badge — `affine-violation-detail-widget`, a DOM sibling of this overlay.
2850
+ *
2851
+ * The handover is strict: an anchor whose emphasis has reached zero is skipped
2852
+ * here and picked up by the badge, so the two markers are never on screen at
2853
+ * the same time. {@link anchorEmphasis} is the single arbiter, read from the
2854
+ * one timeline the manager owns.
2855
+ *
2856
+ * An overlay rather than a renderer change: it touches no element model, writes
2857
+ * nothing to the document, creates no undo entry, and disappears with the rule
2858
+ * that produced it. The fade is session state too — nothing about "when did I
2859
+ * first see this" ever reaches the document.
2860
+ *
2861
+ * The mark is anchored on the outermost enclosing canvas group rather than on
2862
+ * the bare element — see {@link resolveMarkAnchors}. Evaluation is untouched by
2863
+ * this: it stays on the element carrying the role, because its position is what
2864
+ * the rule is about.
2865
+ */
2866
+ export class ValidationOverlay extends Overlay {
2867
+ constructor() {
2868
+ super(...arguments);
2869
+ /**
2870
+ * The violations themselves — NOT their bounds, and not their groups either.
2871
+ * Both are resolved at paint time so the mark tracks what it accuses:
2872
+ * evaluation is debounced, the renderer is not, so a frozen snapshot would
2873
+ * leave the bracket behind at the drag's starting point until 120 ms after
2874
+ * the user let go — and a dissolved group would keep framing nothing.
2875
+ */
2876
+ this._violations = [];
2877
+ /**
2878
+ * Handed in with the violations by {@link ValidationManager}, which owns it —
2879
+ * the DOM badge reads the same instance, and the two markers must never
2880
+ * disagree about whether the bracket is done.
2881
+ */
2882
+ this._timeline = null;
2883
+ /** Armed only while something is still fading. */
2884
+ this._frame = null;
2885
+ /**
2886
+ * Whether the renderer this overlay paints into is gone.
2887
+ *
2888
+ * It matters because the two ends of the affordance are on different
2889
+ * lifecycles: the overlay dies with the surface COMPONENT (the renderer
2890
+ * disposes it), while {@link ValidationManager} lives on the gfx scope and
2891
+ * keeps its subscriptions on the surface MODEL, which outlives the component.
2892
+ * So an evaluation can perfectly well land here after teardown — switch to
2893
+ * page mode, edit the doc — and without this flag it would arm an animation
2894
+ * loop repainting a dead renderer sixty times a second for three seconds.
2895
+ *
2896
+ * Not permanent: the overlay is a DI singleton and is handed a new renderer
2897
+ * if the surface comes back.
2898
+ */
2899
+ this._detached = false;
2900
+ this._onFrame = () => {
2901
+ this._frame = null;
2902
+ this._schedule();
2903
+ };
2904
+ }
2905
+ static { this.overlayName = 'validation'; }
2906
+ /**
2907
+ * Repaint, and keep repainting for as long as a mark is inside its window.
2908
+ *
2909
+ * This is the only clock the affordance runs, and it stops on its own: a
2910
+ * board with no violation, or whose violations have all settled, requests no
2911
+ * animation frame at all.
2912
+ */
2913
+ _schedule() {
2914
+ if (this._detached)
2915
+ return;
2916
+ this.refresh();
2917
+ if (this._frame !== null)
2918
+ return;
2919
+ if (!this._timeline?.isAnimating(performance.now()))
2920
+ return;
2921
+ this._frame = requestAnimationFrame(this._onFrame);
2922
+ }
2923
+ _cancelFrame() {
2924
+ if (this._frame === null)
2925
+ return;
2926
+ cancelAnimationFrame(this._frame);
2927
+ this._frame = null;
2928
+ }
2929
+ _forget() {
2930
+ this._cancelFrame();
2931
+ this._violations = [];
2932
+ // The timeline belongs to the manager and is shared with the badge: drop
2933
+ // the reference, never the contents.
2934
+ this._timeline = null;
2935
+ }
2936
+ /**
2937
+ * @param violations already filtered to what the user is meant to see.
2938
+ * @param timeline the manager's, already synced against these violations.
2939
+ */
2940
+ setViolations(violations, timeline) {
2941
+ if (this._detached)
2942
+ return;
2943
+ this._violations = violations;
2944
+ this._timeline = timeline;
2945
+ this._schedule();
2946
+ }
2947
+ setRenderer(renderer) {
2948
+ this._detached = renderer === null;
2949
+ super.setRenderer(renderer);
2950
+ }
2951
+ clear() {
2952
+ this._forget();
2953
+ super.clear();
2954
+ }
2955
+ dispose() {
2956
+ this._detached = true;
2957
+ this._forget();
2958
+ super.dispose();
2959
+ }
2960
+ render(ctx, _rc) {
2961
+ const timeline = this._timeline;
2962
+ if (!timeline || this._violations.length === 0)
2963
+ return;
2964
+ const now = performance.now();
2965
+ // Everything has settled and the badges have it: the cheapest possible
2966
+ // exit, before a single group chain is walked. The canvas repaints on
2967
+ // every pan, zoom and edit — this overlay must cost nothing between the
2968
+ // rare moments it has something to say.
2969
+ if (!timeline.isAnimating(now))
2970
+ return;
2971
+ const surface = this.gfx.surface;
2972
+ if (!surface)
2973
+ return;
2974
+ // Model units, NOT divided by the zoom: the mark scales with the board.
2975
+ const corner = MARK_CORNER;
2976
+ const padding = VIOLATION_MARK_PADDING;
2977
+ ctx.save();
2978
+ ctx.strokeStyle = VIOLATION_MARK_COLOR;
2979
+ ctx.lineWidth = MARK_LINE_WIDTH;
2980
+ for (const anchor of resolveViolationAnchors(this._violations, surface)) {
2981
+ const emphasis = anchorEmphasis(anchor, timeline, now);
2982
+ // Done fading: the badge has this anchor now, and the two never overlap.
2983
+ if (emphasis <= 0)
2984
+ continue;
2985
+ const { bound } = anchor;
2986
+ const x = bound.x - padding;
2987
+ const y = bound.y - padding;
2988
+ const maxX = bound.maxX + padding;
2989
+ const maxY = bound.maxY + padding;
2990
+ ctx.globalAlpha = emphasis;
2991
+ ctx.beginPath();
2992
+ // Four corner brackets: reads as an annotation, not as a selection box.
2993
+ ctx.moveTo(x + corner, y);
2994
+ ctx.lineTo(x, y);
2995
+ ctx.lineTo(x, y + corner);
2996
+ ctx.moveTo(maxX - corner, y);
2997
+ ctx.lineTo(maxX, y);
2998
+ ctx.lineTo(maxX, y + corner);
2999
+ ctx.moveTo(x, maxY - corner);
3000
+ ctx.lineTo(x, maxY);
3001
+ ctx.lineTo(x + corner, maxY);
3002
+ ctx.moveTo(maxX, maxY - corner);
3003
+ ctx.lineTo(maxX, maxY);
3004
+ ctx.lineTo(maxX - corner, maxY);
3005
+ ctx.stroke();
3006
+ }
3007
+ ctx.restore();
3008
+ }
3009
+ }