@formicoidea/labre-core 0.32.0 → 0.33.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 (227) hide show
  1. package/dist/_pkgs/affine-block-attachment/attachment-block.js +1 -1
  2. package/dist/_pkgs/affine-block-callout/callout-block.js +1 -1
  3. package/dist/_pkgs/affine-block-code/code-block-inline.js +1 -1
  4. package/dist/_pkgs/affine-block-code/code-toolbar/config.js +3 -1
  5. package/dist/_pkgs/affine-block-code/code-toolbar/index.js +1 -1
  6. package/dist/_pkgs/affine-block-database/data-source.js +1 -2
  7. package/dist/_pkgs/affine-block-database/external-data-source.js +1 -1
  8. package/dist/_pkgs/affine-block-edgeless-text/edgeless-text-block.js +1 -1
  9. package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/config.js +1 -1
  10. package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-service.js +1 -1
  11. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +2 -2
  12. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/toolbar.js +1 -1
  13. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/utils.js +1 -1
  14. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +1 -1
  15. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-synced-doc-block.js +1 -1
  16. package/dist/_pkgs/affine-block-frame/frame-manager.js +3 -1
  17. package/dist/_pkgs/affine-block-frame/frame-tool.js +1 -1
  18. package/dist/_pkgs/affine-block-image/components/page-image-block.js +1 -1
  19. package/dist/_pkgs/affine-block-image/image-edgeless-block.js +1 -1
  20. package/dist/_pkgs/affine-block-list/utils/forward-delete.js +1 -1
  21. package/dist/_pkgs/affine-block-note/commands/select-block.js +1 -1
  22. package/dist/_pkgs/affine-block-note/commands/select-blocks-between.js +1 -1
  23. package/dist/_pkgs/affine-block-note/move-block.js +1 -1
  24. package/dist/_pkgs/affine-block-paragraph/utils/forward-delete.js +2 -2
  25. package/dist/_pkgs/affine-block-paragraph/utils/merge-with-prev.js +1 -1
  26. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +1 -1
  27. package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.js +1 -1
  28. package/dist/_pkgs/affine-block-root/edgeless/utils/clipboard-utils.js +1 -1
  29. package/dist/_pkgs/affine-block-surface/adapters/markdown/element-adapter/type.js +1 -1
  30. package/dist/_pkgs/affine-block-surface/adapters/plain-text/element-adapter/type.js +1 -1
  31. package/dist/_pkgs/affine-block-surface/element-model/index.d.ts +18 -3
  32. package/dist/_pkgs/affine-block-surface/element-model/index.js +12 -2
  33. package/dist/_pkgs/affine-block-surface/extensions/audit.js +23 -6
  34. package/dist/_pkgs/affine-block-surface/extensions/crud-extension.js +10 -5
  35. package/dist/_pkgs/affine-block-surface/extensions/editor-anchored-panel.js +2 -1
  36. package/dist/_pkgs/affine-block-surface/extensions/element-renderer.js +1 -1
  37. package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +1 -1
  38. package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +4 -0
  39. package/dist/_pkgs/affine-block-surface/extensions/index.js +4 -0
  40. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.d.ts +172 -0
  41. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +393 -0
  42. package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +306 -0
  43. package/dist/_pkgs/affine-block-surface/extensions/interchange.js +64 -0
  44. package/dist/_pkgs/affine-block-surface/extensions/map-quality-widget.js +61 -61
  45. package/dist/_pkgs/affine-block-surface/extensions/morph-toolbar.d.ts +248 -0
  46. package/dist/_pkgs/affine-block-surface/extensions/morph-toolbar.js +248 -0
  47. package/dist/_pkgs/affine-block-surface/extensions/reading-widget.js +78 -80
  48. package/dist/_pkgs/affine-block-surface/extensions/reading.js +2 -2
  49. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +107 -0
  50. package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +958 -0
  51. package/dist/_pkgs/affine-block-surface/extensions/validation.d.ts +809 -20
  52. package/dist/_pkgs/affine-block-surface/extensions/validation.js +1661 -50
  53. package/dist/_pkgs/affine-block-surface/extensions/violation-detail-widget.d.ts +50 -6
  54. package/dist/_pkgs/affine-block-surface/extensions/violation-detail-widget.js +129 -32
  55. package/dist/_pkgs/affine-block-surface/framework-background/def.d.ts +289 -0
  56. package/dist/_pkgs/affine-block-surface/framework-background/def.js +126 -1
  57. package/dist/_pkgs/affine-block-surface/framework-background/labels.d.ts +42 -2
  58. package/dist/_pkgs/affine-block-surface/framework-background/labels.js +80 -4
  59. package/dist/_pkgs/affine-block-surface/framework-background/renderer.js +172 -11
  60. package/dist/_pkgs/affine-block-surface/renderer/overlay.js +1 -1
  61. package/dist/_pkgs/affine-block-surface-ref/portal/note.js +2 -2
  62. package/dist/_pkgs/affine-block-table/commands.js +1 -1
  63. package/dist/_pkgs/affine-components/edgeless-shape-color-picker/color-picker.js +1 -1
  64. package/dist/_pkgs/affine-components/embed-card-modal/embed-card-edit-modal.js +1 -1
  65. package/dist/_pkgs/affine-components/filterable-list/styles.js +1 -1
  66. package/dist/_pkgs/affine-components/peek/peekable.js +1 -1
  67. package/dist/_pkgs/affine-components/toolbar/index.d.ts +1 -0
  68. package/dist/_pkgs/affine-components/toolbar/index.js +1 -0
  69. package/dist/_pkgs/affine-components/toolbar/menu-button.js +1 -1
  70. package/dist/_pkgs/affine-components/toolbar/picker-menu.d.ts +55 -0
  71. package/dist/_pkgs/affine-components/toolbar/picker-menu.js +47 -0
  72. package/dist/_pkgs/affine-fragment-frame-panel/body/frame-panel-body.js +2 -2
  73. package/dist/_pkgs/affine-fragment-frame-panel/header/frame-panel-header.js +2 -2
  74. package/dist/_pkgs/affine-fragment-frame-panel/header/frames-setting-menu.js +1 -1
  75. package/dist/_pkgs/affine-fragment-outline/mobile-outline-panel.js +1 -1
  76. package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/pen-tool-button.js +1 -1
  77. package/dist/_pkgs/affine-gfx-connector/connector-manager.js +7 -10
  78. package/dist/_pkgs/affine-gfx-connector/connector-tool.d.ts +19 -1
  79. package/dist/_pkgs/affine-gfx-connector/connector-tool.js +10 -2
  80. package/dist/_pkgs/affine-gfx-connector/connector-watcher.js +1 -4
  81. package/dist/_pkgs/affine-gfx-connector/direction/direction-reveal.js +2 -1
  82. package/dist/_pkgs/affine-gfx-connector/direction/invert-direction.js +1 -3
  83. package/dist/_pkgs/affine-gfx-connector/element-renderer/index.js +1 -1
  84. package/dist/_pkgs/affine-gfx-connector/element-renderer/utils.js +1 -1
  85. package/dist/_pkgs/affine-gfx-connector/toolbar/config.js +1 -1
  86. package/dist/_pkgs/affine-gfx-mindmap/adapter/markdown/index.js +1 -1
  87. package/dist/_pkgs/affine-gfx-mindmap/adapter/utils.js +1 -1
  88. package/dist/_pkgs/affine-gfx-mindmap/templates/index.js +583 -4
  89. package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-menu.js +23 -23
  90. package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.js +5 -1
  91. package/dist/_pkgs/affine-gfx-mindmap/view/utils.js +1 -1
  92. package/dist/_pkgs/affine-gfx-note/overlay/overlay.js +1 -1
  93. package/dist/_pkgs/affine-gfx-note/toolbar/note-senior-button.js +1 -1
  94. package/dist/_pkgs/affine-gfx-shape/consts.js +1 -1
  95. package/dist/_pkgs/affine-gfx-shape/draggable/shape-draggable.js +1 -1
  96. package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/polygon.js +17 -9
  97. package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/utils.js +8 -3
  98. package/dist/_pkgs/affine-gfx-shape/element-view.js +19 -12
  99. package/dist/_pkgs/affine-gfx-shape/overlay/polygon-drawing-overlay.js +1 -1
  100. package/dist/_pkgs/affine-gfx-shape/overlay/polygon-vertex-editing-overlay.js +12 -7
  101. package/dist/_pkgs/affine-gfx-shape/overlay/shape-overlay.js +1 -1
  102. package/dist/_pkgs/affine-gfx-shape/polygon-tool.js +4 -2
  103. package/dist/_pkgs/affine-gfx-shape/shape-tool.js +1 -1
  104. package/dist/_pkgs/affine-gfx-shape/text/edgeless-shape-text-editor.js +1 -3
  105. package/dist/_pkgs/affine-gfx-template/builtin/other.js +66 -17
  106. package/dist/_pkgs/affine-gfx-template/toolbar/overlay-scrollbar.js +1 -1
  107. package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +1 -1
  108. package/dist/_pkgs/affine-gfx-text/toolbar/actions.js +1 -1
  109. package/dist/_pkgs/affine-inline-link/command.js +1 -1
  110. package/dist/_pkgs/affine-inline-preset/command/format-block.js +1 -1
  111. package/dist/_pkgs/affine-inline-preset/command/format-native.js +2 -2
  112. package/dist/_pkgs/affine-inline-preset/command/format-text.js +1 -1
  113. package/dist/_pkgs/affine-inline-preset/inline-spec.js +1 -1
  114. package/dist/_pkgs/affine-inline-reference/reference-node/reference-node.js +1 -1
  115. package/dist/_pkgs/affine-model/elements/bpmn/node.d.ts +40 -7
  116. package/dist/_pkgs/affine-model/elements/bpmn/node.js +4 -2
  117. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +74 -17
  118. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +55 -26
  119. package/dist/_pkgs/affine-model/elements/c4/board.d.ts +88 -0
  120. package/dist/_pkgs/affine-model/elements/c4/board.js +128 -0
  121. package/dist/_pkgs/affine-model/elements/c4/boundary.d.ts +63 -0
  122. package/dist/_pkgs/affine-model/elements/c4/boundary.js +134 -0
  123. package/dist/_pkgs/affine-model/elements/c4/index.d.ts +3 -0
  124. package/dist/_pkgs/affine-model/elements/c4/index.js +3 -0
  125. package/dist/_pkgs/affine-model/elements/c4/node.d.ts +97 -0
  126. package/dist/_pkgs/affine-model/elements/c4/node.js +130 -0
  127. package/dist/_pkgs/affine-model/elements/connector/connector.js +1 -1
  128. package/dist/_pkgs/affine-model/elements/context-map/context-map.d.ts +27 -0
  129. package/dist/_pkgs/affine-model/elements/context-map/context-map.js +94 -0
  130. package/dist/_pkgs/affine-model/elements/context-map/index.d.ts +1 -0
  131. package/dist/_pkgs/affine-model/elements/context-map/index.js +1 -0
  132. package/dist/_pkgs/affine-model/elements/core-domain/core-domain.d.ts +34 -17
  133. package/dist/_pkgs/affine-model/elements/core-domain/core-domain.js +28 -26
  134. package/dist/_pkgs/affine-model/elements/event-storming/event-storming.d.ts +36 -0
  135. package/dist/_pkgs/affine-model/elements/event-storming/event-storming.js +103 -0
  136. package/dist/_pkgs/affine-model/elements/event-storming/index.d.ts +1 -0
  137. package/dist/_pkgs/affine-model/elements/event-storming/index.js +1 -0
  138. package/dist/_pkgs/affine-model/elements/index.d.ts +11 -0
  139. package/dist/_pkgs/affine-model/elements/index.js +3 -0
  140. package/dist/_pkgs/affine-model/elements/shape/api/polygon.js +2 -8
  141. package/dist/_pkgs/affine-shared/adapters/html/block-adapter.js +1 -1
  142. package/dist/_pkgs/affine-shared/adapters/markdown/block-adapter.js +1 -1
  143. package/dist/_pkgs/affine-shared/adapters/markdown/delta-converter.js +1 -1
  144. package/dist/_pkgs/affine-shared/adapters/notion-html/block-adapter.js +1 -1
  145. package/dist/_pkgs/affine-shared/adapters/notion-html/delta-converter.js +1 -1
  146. package/dist/_pkgs/affine-shared/adapters/plain-text/block-adapter.js +1 -1
  147. package/dist/_pkgs/affine-shared/adapters/plain-text/delta-converter.js +1 -1
  148. package/dist/_pkgs/affine-shared/adapters/utils/color.js +5 -2
  149. package/dist/_pkgs/affine-shared/commands/model-crud/draft-selected-models.js +1 -1
  150. package/dist/_pkgs/affine-shared/commands/selection/focus-block-end.js +1 -1
  151. package/dist/_pkgs/affine-shared/commands/selection/focus-block-start.js +1 -1
  152. package/dist/_pkgs/affine-shared/consts/index.d.ts +10 -0
  153. package/dist/_pkgs/affine-shared/consts/index.js +10 -0
  154. package/dist/_pkgs/affine-shared/services/artefact-catalogue-service.d.ts +55 -0
  155. package/dist/_pkgs/affine-shared/services/artefact-catalogue-service.js +35 -0
  156. package/dist/_pkgs/affine-shared/services/audit-service.d.ts +11 -0
  157. package/dist/_pkgs/affine-shared/services/command-usage-service.d.ts +31 -0
  158. package/dist/_pkgs/affine-shared/services/command-usage-service.js +109 -0
  159. package/dist/_pkgs/affine-shared/services/comment-service/block-comment-manager.js +1 -1
  160. package/dist/_pkgs/affine-shared/services/doc-display-meta-service.js +1 -1
  161. package/dist/_pkgs/affine-shared/services/embed-iframe/embed-iframe-config.js +1 -1
  162. package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
  163. package/dist/_pkgs/affine-shared/services/index.js +2 -0
  164. package/dist/_pkgs/affine-shared/services/pivot-materiality-publisher.js +2 -1
  165. package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +73 -1
  166. package/dist/_pkgs/affine-shared/services/toolbar-service/registry.d.ts +44 -0
  167. package/dist/_pkgs/affine-shared/services/toolbar-service/registry.js +161 -3
  168. package/dist/_pkgs/affine-shared/services/universe-tag-defs-service.js +5 -3
  169. package/dist/_pkgs/affine-shared/test-utils/affine-template.js +1 -1
  170. package/dist/_pkgs/affine-shared/utils/edgeless.js +1 -1
  171. package/dist/_pkgs/affine-shared/utils/file/filesys.d.ts +34 -0
  172. package/dist/_pkgs/affine-shared/utils/file/filesys.js +48 -7
  173. package/dist/_pkgs/affine-shared/utils/zod-schema.js +1 -3
  174. package/dist/_pkgs/affine-widget-drag-handle/utils.js +1 -1
  175. package/dist/_pkgs/affine-widget-edgeless-auto-connect/index.js +1 -1
  176. package/dist/_pkgs/affine-widget-edgeless-dragging-area/edgeless-dragging-area-rect.js +1 -1
  177. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-default.d.ts +14 -0
  178. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-default.js +40 -0
  179. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.d.ts +158 -0
  180. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.js +572 -0
  181. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.d.ts +46 -0
  182. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.js +66 -0
  183. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/chord-format.d.ts +13 -0
  184. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/chord-format.js +50 -0
  185. package/dist/_pkgs/affine-widget-edgeless-toolbar/edgeless-toolbar.d.ts +6 -0
  186. package/dist/_pkgs/affine-widget-edgeless-toolbar/edgeless-toolbar.js +15 -5
  187. package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.d.ts +2 -0
  188. package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.js +2 -0
  189. package/dist/_pkgs/affine-widget-edgeless-toolbar/extension/index.d.ts +7 -0
  190. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.d.ts +4 -0
  191. package/dist/_pkgs/affine-widget-edgeless-toolbar/index.js +4 -0
  192. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.d.ts +19 -0
  193. package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.js +51 -10
  194. package/dist/_pkgs/affine-widget-edgeless-toolbar/view.js +9 -0
  195. package/dist/_pkgs/affine-widget-keyboard-toolbar/keyboard-tool-panel.js +1 -1
  196. package/dist/_pkgs/affine-widget-linked-doc/import-doc/import-doc.js +1 -1
  197. package/dist/_pkgs/affine-widget-linked-doc/linked-doc-popover.js +1 -1
  198. package/dist/_pkgs/affine-widget-page-dragging-area/index.js +1 -1
  199. package/dist/_pkgs/affine-widget-remote-selection/doc/doc-remote-selection.js +1 -1
  200. package/dist/_pkgs/affine-widget-toolbar/toolbar.js +6 -2
  201. package/dist/_pkgs/affine-widget-toolbar/utils.js +6 -2
  202. package/dist/_pkgs/data-view/view-presets/calendar/pc/actions.js +8 -12
  203. package/dist/_pkgs/data-view/view-presets/calendar/pc/view.js +52 -40
  204. package/dist/_pkgs/data-view/view-presets/table/mobile/group.js +3 -1
  205. package/dist/_pkgs/data-view/view-presets/table/pc/group.js +3 -1
  206. package/dist/_pkgs/std/clipboard/utils.js +4 -1
  207. package/dist/_pkgs/std/extension/command-registry.d.ts +159 -5
  208. package/dist/_pkgs/std/extension/command-registry.js +202 -4
  209. package/dist/_pkgs/std/extension/shortcut.js +2 -1
  210. package/dist/_pkgs/std/gfx/grid.js +1 -1
  211. package/dist/_pkgs/std/gfx/index.d.ts +1 -1
  212. package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +160 -0
  213. package/dist/_pkgs/std/gfx/model/surface/element-model.js +75 -1
  214. package/dist/_pkgs/std/gfx/model/surface/role.js +5 -3
  215. package/dist/_pkgs/std/gfx/model/surface/surface-model.js +2 -1
  216. package/dist/_pkgs/std/gfx/selection.js +1 -1
  217. package/dist/_pkgs/std/gfx/viewport.js +1 -1
  218. package/dist/_pkgs/std/inline/extensions/inline-manager.js +1 -1
  219. package/dist/_pkgs/std/inline/extensions/markdown-matcher.js +1 -1
  220. package/dist/_pkgs/store/model/store/crud.js +4 -1
  221. package/dist/commands.js +1 -1
  222. package/dist/extensions/command-usage.d.ts +23 -0
  223. package/dist/extensions/command-usage.js +37 -0
  224. package/dist/extensions/view.js +12 -0
  225. package/dist/frameworks.js +32 -0
  226. package/dist/translations.js +90 -2
  227. package/package.json +1 -1
@@ -0,0 +1,109 @@
1
+ import { CommandUsageIdentifier, } from '../../std/index.js';
2
+ /**
3
+ * Where `createLocalCommandUsageStore` persists. Namespaced like the rest of
4
+ * Labre's browser-local state, and versionless on purpose: the shape is two
5
+ * numbers per command, and a reader that does not recognise an entry drops it.
6
+ */
7
+ export const COMMAND_USAGE_KEY = 'labre:command-usage';
8
+ /**
9
+ * Enough for every command of every framework several times over (the registry
10
+ * holds 76 today), small enough that the JSON stays a few kilobytes. Overflow
11
+ * evicts the least recently used entry, so the measure a ranking cares about —
12
+ * what this user reached for lately — is the last thing lost.
13
+ */
14
+ const MAX_ENTRIES = 200;
15
+ /**
16
+ * `localStorage` throws outright in a few environments (private mode quotas,
17
+ * sandboxed frames, some test runners), and a usage measure is never important
18
+ * enough to take a command down with it — every access is therefore
19
+ * best-effort. Same rule and same shape as the icon picker's recent store.
20
+ */
21
+ const storage = () => {
22
+ try {
23
+ return globalThis.localStorage ?? null;
24
+ }
25
+ catch {
26
+ return null;
27
+ }
28
+ };
29
+ const readUsage = () => {
30
+ try {
31
+ const raw = storage()?.getItem(COMMAND_USAGE_KEY);
32
+ if (!raw)
33
+ return {};
34
+ const parsed = JSON.parse(raw);
35
+ if (!parsed || typeof parsed !== 'object')
36
+ return {};
37
+ // Entry by entry: one corrupted record must not discard the whole table.
38
+ const usage = {};
39
+ for (const [id, value] of Object.entries(parsed)) {
40
+ const { c, t } = (value ?? {});
41
+ if (typeof c === 'number' && typeof t === 'number')
42
+ usage[id] = { c, t };
43
+ }
44
+ return usage;
45
+ }
46
+ catch {
47
+ return {};
48
+ }
49
+ };
50
+ const writeUsage = (usage) => {
51
+ try {
52
+ storage()?.setItem(COMMAND_USAGE_KEY, JSON.stringify(usage));
53
+ }
54
+ catch {
55
+ // best-effort: this session keeps working, the measure just misses a run
56
+ }
57
+ };
58
+ /** Keeps the {@link MAX_ENTRIES} most recently used entries, nothing else. */
59
+ const evictOverflow = (usage) => {
60
+ const ids = Object.keys(usage);
61
+ if (ids.length <= MAX_ENTRIES)
62
+ return usage;
63
+ const kept = ids
64
+ .sort((a, b) => usage[b].t - usage[a].t)
65
+ .slice(0, MAX_ENTRIES);
66
+ return Object.fromEntries(kept.map(id => [id, usage[id]]));
67
+ };
68
+ /**
69
+ * The default {@link CommandUsageStore}: recency and frequency in this
70
+ * browser's `localStorage`, per command id.
71
+ *
72
+ * It measures only. Nothing ranks anything here — PF6's "seven most-recent +
73
+ * six most-used" sub-menu reads these numbers, and a host that
74
+ * wants the measure to follow the user across devices replaces the whole store
75
+ * through {@link CommandUsageExtension}.
76
+ */
77
+ export function createLocalCommandUsageStore() {
78
+ return {
79
+ record: (command) => {
80
+ const usage = readUsage();
81
+ const previous = usage[command.id];
82
+ usage[command.id] = { c: (previous?.c ?? 0) + 1, t: Date.now() };
83
+ writeUsage(evictOverflow(usage));
84
+ },
85
+ statsOf: (commandId) => {
86
+ const entry = readUsage()[commandId];
87
+ return entry ? { count: entry.c, lastUsedAt: entry.t } : undefined;
88
+ },
89
+ };
90
+ }
91
+ /**
92
+ * Host override seam: replace the browser-local measure with the host's own.
93
+ *
94
+ * This is where a host persists command usage in ITS database — so the ranking
95
+ * follows the user from laptop to browser to tablet instead of restarting at
96
+ * zero, which is exactly what a "most-used" sub-menu is judged on. The library
97
+ * ships the local store as the default (`CommandUsageViewExtension`) and never
98
+ * requires a host to care.
99
+ *
100
+ * Mirrors `EditorSettingExtension` / `KeymapOverrideExtension`: `di.override`,
101
+ * so a host registering after the library's own extensions always wins.
102
+ */
103
+ export function CommandUsageExtension(store) {
104
+ return {
105
+ setup: di => {
106
+ di.override(CommandUsageIdentifier, () => store);
107
+ },
108
+ };
109
+ }
@@ -1,6 +1,6 @@
1
1
  import { DividerBlockModel } from '../../../affine-model/index.js';
2
2
  import { DisposableGroup } from '../../../global/disposable/index.js';
3
- import { BlockSelection, LifeCycleWatcher, TextSelection, } from '../../../std/index.js';
3
+ import { BlockSelection, LifeCycleWatcher, TextSelection } from '../../../std/index.js';
4
4
  import { signal } from '@preact/signals-core';
5
5
  import { getSelectedBlocksCommand } from '../../commands/index.js';
6
6
  import { ImageSelection } from '../../selection/index.js';
@@ -1,5 +1,5 @@
1
1
  import { createIdentifier } from '../../global/di/index.js';
2
- import { disposeMember, } from '../../global/disposable/index.js';
2
+ import { disposeMember } from '../../global/disposable/index.js';
3
3
  import { AliasIcon, BlockLinkIcon, DeleteIcon, EdgelessIcon, LinkedEdgelessIcon, LinkedPageIcon, PageIcon, } from '@blocksuite/icons/lit';
4
4
  import { LifeCycleWatcher, StdIdentifier } from '../../std/index.js';
5
5
  import { computed, signal, } from '@preact/signals-core';
@@ -1,4 +1,4 @@
1
- import { createIdentifier, } from '../../../global/di/index.js';
1
+ import { createIdentifier } from '../../../global/di/index.js';
2
2
  export const EmbedIframeConfigIdentifier = createIdentifier('EmbedIframeConfig');
3
3
  export function EmbedIframeConfigExtension(config) {
4
4
  const identifier = EmbedIframeConfigIdentifier(config.name);
@@ -1,7 +1,9 @@
1
+ export * from './artefact-catalogue-service.js';
1
2
  export * from './audit-service.js';
2
3
  export * from './auto-clear-selection-service.js';
3
4
  export * from './block-meta-service.js';
4
5
  export * from './citation-service/index.js';
6
+ export * from './command-usage-service.js';
5
7
  export * from './comment-service/index.js';
6
8
  export * from './doc-display-meta-service.js';
7
9
  export * from './doc-mode-service.js';
@@ -1,7 +1,9 @@
1
+ export * from './artefact-catalogue-service.js';
1
2
  export * from './audit-service.js';
2
3
  export * from './auto-clear-selection-service.js';
3
4
  export * from './block-meta-service.js';
4
5
  export * from './citation-service/index.js';
6
+ export * from './command-usage-service.js';
5
7
  export * from './comment-service/index.js';
6
8
  export * from './doc-display-meta-service.js';
7
9
  export * from './doc-mode-service.js';
@@ -110,7 +110,8 @@ export class PivotMaterialityPublisher extends LifeCycleWatcher {
110
110
  // there is nobody to announce to, so nothing is subscribed and the whole
111
111
  // watcher costs one lookup. Binding and qualification keep working: they
112
112
  // are element-local writes and never needed the host.
113
- if (!this.std.getOptional(PivotPropertiesProvider)?.publishOccurrenceMaterialities) {
113
+ if (!this.std.getOptional(PivotPropertiesProvider)
114
+ ?.publishOccurrenceMaterialities) {
114
115
  return;
115
116
  }
116
117
  // The surface is a SIGNAL, not a fact: it can legitimately be null at mount
@@ -34,7 +34,7 @@ export type BlockLifecycleEvents = {
34
34
  * manipulated ('background:classic', 'node:market', 'connector:link'…).
35
35
  */
36
36
  export interface FrameworkElementEvent extends TelemetryEvent {
37
- framework: 'wardley' | 'edgy' | 'cynefin' | 'bpmn' | 'event-storming' | 'core-domain' | 'context-map';
37
+ framework: 'wardley' | 'edgy' | 'cynefin' | 'bpmn' | 'c4' | 'event-storming' | 'core-domain' | 'context-map';
38
38
  element: string;
39
39
  }
40
40
  /**
@@ -101,11 +101,83 @@ export interface EdgeDirectionEvent extends TelemetryEvent {
101
101
  /** How many edges the single gesture reversed. */
102
102
  elementCount: number;
103
103
  }
104
+ /**
105
+ * A framework element became a NEARBY kind of itself — a task said more
106
+ * precisely as a user task, a start event as a timer start — from its own
107
+ * contextual toolbar, in one atomic write.
108
+ *
109
+ * **A new event name, for the reason {@link FrameworkPromotionEvent} gives.**
110
+ * ADR 0003 § 2 defines the creation event as UI intent emitted at INSERTION
111
+ * sites, and a morph inserts nothing: no element is created, destroyed or
112
+ * swapped, no id changes, no geometry moves and no connector is re-pointed.
113
+ * Reusing `FrameworkElementAdded` would count a drawn-then-refined artefact
114
+ * twice and inflate "elements added per framework" permanently.
115
+ *
116
+ * Worth measuring on its own, too. The set of reachable kinds is DATA a
117
+ * framework declares by hand, and the only evidence that a declared family is
118
+ * the right family is how often it is actually crossed — a pair nobody ever
119
+ * morphs between did not need to be offered, and a morph users reach for
120
+ * constantly says the palette led them to the wrong artefact first.
121
+ *
122
+ * Ids only: the two roles, and their framework. Never a kind's board content,
123
+ * never the element ids, never the label anybody typed.
124
+ */
125
+ export interface FrameworkElementMorphedEvent extends TelemetryEvent {
126
+ page?: 'whiteboard editor';
127
+ /**
128
+ * Owning framework — the declaration's own wire key, matching what
129
+ * `reportCommandTelemetry` sends for that framework's commands.
130
+ *
131
+ * OPTIONAL for the same reason it is on {@link FrameworkPromotionEvent}: the
132
+ * capability is generic, and an element type may one day declare a morph
133
+ * without belonging to a framework the union names. Absent rather than
134
+ * `'unknown'`, per the repo convention.
135
+ */
136
+ framework?: FrameworkId;
137
+ /** Role id the selection carried, when they all carried the same one. */
138
+ fromRole?: string;
139
+ /** Role id it now carries. */
140
+ toRole?: string;
141
+ /** How many elements the single gesture actually rewrote. */
142
+ elementCount: number;
143
+ }
144
+ /**
145
+ * A framework VIEW was told which level it draws — a C4 board set to Context,
146
+ * Container or Component, or put back to a free sketch.
147
+ *
148
+ * **A new event name, for the reason {@link FrameworkPromotionEvent} gives.**
149
+ * Nothing is inserted, nothing is morphed and no level of requirement changes:
150
+ * the author is stating what the sheet IS, which is a fact none of the existing
151
+ * events describes. Reusing one of them would corrupt the funnel it belongs to.
152
+ *
153
+ * Worth measuring on its own, and for the same reason a profile change is: this
154
+ * is an OPTIONAL declaration nothing forces, so how often it is made is the only
155
+ * evidence that asking for it was worth the toolbar entry — and a level users
156
+ * set and then clear is a rule pack that argued with a drawing they meant.
157
+ *
158
+ * Ids only: the framework and the level's own closed vocabulary. Never the
159
+ * board's title, never what is drawn on it.
160
+ */
161
+ export interface FrameworkViewLevelEvent extends TelemetryEvent {
162
+ page?: 'whiteboard editor';
163
+ /** Owning framework of the view, e.g. `c4`. */
164
+ framework: FrameworkId;
165
+ /**
166
+ * The level now in force, from the framework's own closed vocabulary —
167
+ * `'none'` when the view has been put back to declaring nothing, which is a
168
+ * value the dashboard needs as much as the others.
169
+ */
170
+ level: string;
171
+ /** The one it replaces, when the view declared one. */
172
+ previousLevel?: string;
173
+ }
104
174
  export type FrameworkDiagramEvents = {
105
175
  FrameworkElementAdded: FrameworkElementEvent;
106
176
  FrameworkToolPicked: FrameworkElementEvent;
107
177
  FrameworkLegendCreated: FrameworkElementEvent;
108
178
  FrameworkElementPromoted: FrameworkPromotionEvent;
179
+ FrameworkElementMorphed: FrameworkElementMorphedEvent;
180
+ FrameworkViewLevelSet: FrameworkViewLevelEvent;
109
181
  EdgeDirectionInverted: EdgeDirectionEvent;
110
182
  };
111
183
  /**
@@ -8,7 +8,19 @@ import type { ToolbarModule } from './module.js';
8
8
  export declare const ToolbarModuleIdentifier: import("../../../global/di/types.js").ServiceIdentifier<ToolbarModule> & (<U extends ToolbarModule = ToolbarModule>(variant: import("../../../global/di/types.js").ServiceVariant) => import("../../../global/di/types.js").ServiceIdentifier<U>);
9
9
  export declare const ToolbarRegistryIdentifier: import("../../../global/di/types.js").ServiceIdentifier<ToolbarRegistryExtension> & (<U extends ToolbarRegistryExtension = ToolbarRegistryExtension>(variant: import("../../../global/di/types.js").ServiceVariant) => import("../../../global/di/types.js").ServiceIdentifier<U>);
10
10
  export declare function ToolbarModuleExtension(module: ToolbarModule): ExtensionType;
11
+ /** The flavour a DI variant speaks for, owner suffix and all. */
12
+ export declare function toolbarModuleFlavour(variant: string): string;
13
+ /**
14
+ * A second (or third) module on one flavour, named by who contributes it.
15
+ *
16
+ * The owner is never shown and never parsed back — it exists so two
17
+ * contributors cannot claim one DI variant. Use a stable, framework-scoped
18
+ * name (`c4-morph`), because it is the identity the container refuses
19
+ * duplicates on.
20
+ */
21
+ export declare function toolbarModuleKey(flavour: string, owner: string): string;
11
22
  export declare class ToolbarRegistryExtension extends Extension {
23
+ #private;
12
24
  readonly std: BlockStdScope;
13
25
  flavour$: import("@preact/signals-core").Signal<string>;
14
26
  elementsMap$: import("@preact/signals-core").Signal<Map<string, GfxModel[]>>;
@@ -20,8 +32,40 @@ export declare class ToolbarRegistryExtension extends Extension {
20
32
  placement$: import("@preact/signals-core").Signal<ToolbarPlacement>;
21
33
  flags: Flags;
22
34
  constructor(std: BlockStdScope);
35
+ /**
36
+ * Every module of every flavour, keyed by the DI variant it was registered
37
+ * under — which for a shared flavour carries an owner suffix. Read
38
+ * {@link modulesFor} instead unless the variant itself is the question.
39
+ */
23
40
  get modules(): Map<string, ToolbarModule>;
41
+ /**
42
+ * Every module contributing to one flavour's row — the bare-key module
43
+ * first, then the owner-suffixed ones in registration order.
44
+ */
45
+ modulesFor(flavour: string): readonly ToolbarModule[];
46
+ /**
47
+ * The config registered under the flavour ITSELF, or `null`.
48
+ *
49
+ * Deliberately not "the first module of this flavour". The callers of this
50
+ * method ask a question about one identified registration — does this block
51
+ * flavour have a toolbar of its own, where does that toolbar sit — and an
52
+ * owner-suffixed contributor is an ADDITION to a row, not a stand-in for the
53
+ * module that defines it. Answering with a contributor would make the reply
54
+ * depend on which frameworks a host happened to switch on: with `wardley`
55
+ * off and `c4` on, `custom:affine:surface:group` has no bare module at all,
56
+ * and returning C4's morph there would let a placement (or a `has`-style
57
+ * probe) be decided by a menu that only some builds ship.
58
+ */
24
59
  getModuleBy(flavour: string): import("./config.js").ToolbarModuleConfig | null;
60
+ /**
61
+ * Where a flavour's row sits: the first module of it that actually SAYS.
62
+ *
63
+ * A row is one row however many modules contribute to it, so the placement
64
+ * is a property of the flavour and not of a registration — and a contributor
65
+ * that declares none must not be read as declaring the default. Scanning for
66
+ * the first module with a `placement` keeps the answer the same whether the
67
+ * framework that states it is registered first, last, or under an owner.
68
+ */
25
69
  getModulePlacement(flavour: string, fallback?: ToolbarPlacement): ToolbarPlacement;
26
70
  static setup(di: Container): void;
27
71
  }
@@ -12,6 +12,61 @@ export function ToolbarModuleExtension(module) {
12
12
  },
13
13
  };
14
14
  }
15
+ /**
16
+ * What separates a flavour from the OWNER of one of its modules.
17
+ *
18
+ * A module is registered under one DI variant, and until now that variant WAS
19
+ * the flavour — so a flavour held at most two modules, its own key and its
20
+ * `custom:` twin, and a third contributor got a `DuplicateServiceDefinitionError`
21
+ * at setup rather than a menu entry. That ceiling was invisible until two
22
+ * frameworks needed the same element: `affine:surface:group` carries the native
23
+ * group operations, `custom:affine:surface:group` carries Wardley's
24
+ * qualification dropdown, and a C4 component IS a group — so C4's morph had
25
+ * nowhere at all to be registered.
26
+ *
27
+ * {@link toolbarModuleKey} gives it one. `custom:affine:surface:group#c4-morph`
28
+ * is a DISTINCT DI variant and therefore never collides, while
29
+ * {@link ToolbarRegistryExtension.modulesFor} still hands it to the row drawn
30
+ * for `custom:affine:surface:group`. Nothing about the existing keys changes:
31
+ * a module registered under a bare flavour is that flavour's first module, and
32
+ * every registration in the library before this one stays exactly as it was.
33
+ */
34
+ const MODULE_OWNER_SEPARATOR = '#';
35
+ /** The flavour a DI variant speaks for, owner suffix and all. */
36
+ export function toolbarModuleFlavour(variant) {
37
+ const at = variant.indexOf(MODULE_OWNER_SEPARATOR);
38
+ return at < 0 ? variant : variant.slice(0, at);
39
+ }
40
+ /**
41
+ * A second (or third) module on one flavour, named by who contributes it.
42
+ *
43
+ * The owner is never shown and never parsed back — it exists so two
44
+ * contributors cannot claim one DI variant. Use a stable, framework-scoped
45
+ * name (`c4-morph`), because it is the identity the container refuses
46
+ * duplicates on.
47
+ */
48
+ export function toolbarModuleKey(flavour, owner) {
49
+ // Checked ALWAYS, and deliberately not behind a `process.env.NODE_ENV` dev
50
+ // guard. This library is published SOURCE-FIRST, so a `process` reference is
51
+ // not erased by anybody's build: a host consuming raw ESM would meet
52
+ // `ReferenceError: process is not defined` the moment a framework registered
53
+ // a shared toolbar module — i.e. at editor setup, on every document. The
54
+ // check costs one falsy test and one `includes` on a short string, run a
55
+ // handful of times per editor, which is nothing next to being the only
56
+ // `process` reference in the shipped source.
57
+ //
58
+ // What it prevents is silent: an empty owner produces `flavour#`, which is a
59
+ // DIFFERENT variant from `flavour` and would therefore register a second
60
+ // module the bare-key lookups below never see, and an owner carrying the
61
+ // separator makes `toolbarModuleFlavour` cut in the wrong place, filing the
62
+ // module under a flavour nobody draws.
63
+ if (!owner || owner.includes(MODULE_OWNER_SEPARATOR)) {
64
+ throw new Error(`toolbarModuleKey: owner must be non-empty and free of "${MODULE_OWNER_SEPARATOR}" (got ${JSON.stringify(owner)})`);
65
+ }
66
+ return `${flavour}${MODULE_OWNER_SEPARATOR}${owner}`;
67
+ }
68
+ /** One shared empty answer, so a miss allocates nothing and can be frozen. */
69
+ const NO_MODULES = Object.freeze([]);
15
70
  export class ToolbarRegistryExtension extends Extension {
16
71
  constructor(std) {
17
72
  super();
@@ -21,18 +76,121 @@ export class ToolbarRegistryExtension extends Extension {
21
76
  this.message$ = signal(null);
22
77
  this.placement$ = signal('top');
23
78
  this.flags = new Flags();
79
+ /**
80
+ * The modules grouped by the FLAVOUR they speak for, computed once.
81
+ *
82
+ * Once, because the registrations are fixed for the life of a container —
83
+ * they are DI impls, added at setup and never afterwards — while
84
+ * {@link modulesFor} is asked six times per toolbar render, and a toolbar
85
+ * re-renders on every selection change, every drag frame and every zoom
86
+ * notch.
87
+ *
88
+ * A module registered under the BARE flavour leads its group, and the rest
89
+ * follow in registration order. The order is what decides nothing much —
90
+ * `renderToolbar` sorts the merged actions by `placement`, `id` and `score`
91
+ * — and it is pinned all the same, because "which module answers a bare-key
92
+ * question" must not depend on which framework happened to be registered
93
+ * first.
94
+ *
95
+ * Each group is FROZEN: it is handed out by {@link modulesFor} on every
96
+ * render, and a caller that sorted or spliced the answer in place would
97
+ * silently rewrite the registry for every later render.
98
+ */
99
+ this.#grouped = null;
24
100
  }
101
+ /**
102
+ * Every module of every flavour, keyed by the DI variant it was registered
103
+ * under — which for a shared flavour carries an owner suffix. Read
104
+ * {@link modulesFor} instead unless the variant itself is the question.
105
+ */
25
106
  get modules() {
26
107
  return this.std.provider.getAll(ToolbarModuleIdentifier);
27
108
  }
109
+ /**
110
+ * The modules grouped by the FLAVOUR they speak for, computed once.
111
+ *
112
+ * Once, because the registrations are fixed for the life of a container —
113
+ * they are DI impls, added at setup and never afterwards — while
114
+ * {@link modulesFor} is asked six times per toolbar render, and a toolbar
115
+ * re-renders on every selection change, every drag frame and every zoom
116
+ * notch.
117
+ *
118
+ * A module registered under the BARE flavour leads its group, and the rest
119
+ * follow in registration order. The order is what decides nothing much —
120
+ * `renderToolbar` sorts the merged actions by `placement`, `id` and `score`
121
+ * — and it is pinned all the same, because "which module answers a bare-key
122
+ * question" must not depend on which framework happened to be registered
123
+ * first.
124
+ *
125
+ * Each group is FROZEN: it is handed out by {@link modulesFor} on every
126
+ * render, and a caller that sorted or spliced the answer in place would
127
+ * silently rewrite the registry for every later render.
128
+ */
129
+ #grouped;
130
+ get #groups() {
131
+ if (!this.#grouped) {
132
+ const groups = new Map();
133
+ for (const [variant, module] of this.modules) {
134
+ const flavour = toolbarModuleFlavour(variant);
135
+ const group = groups.get(flavour);
136
+ if (!group)
137
+ groups.set(flavour, [module]);
138
+ else if (variant === flavour)
139
+ group.unshift(module);
140
+ else
141
+ group.push(module);
142
+ }
143
+ for (const group of groups.values())
144
+ Object.freeze(group);
145
+ this.#grouped = groups;
146
+ }
147
+ return this.#grouped;
148
+ }
149
+ /**
150
+ * Every module contributing to one flavour's row — the bare-key module
151
+ * first, then the owner-suffixed ones in registration order.
152
+ */
153
+ modulesFor(flavour) {
154
+ return this.#groups.get(flavour) ?? NO_MODULES;
155
+ }
156
+ /**
157
+ * The config registered under the flavour ITSELF, or `null`.
158
+ *
159
+ * Deliberately not "the first module of this flavour". The callers of this
160
+ * method ask a question about one identified registration — does this block
161
+ * flavour have a toolbar of its own, where does that toolbar sit — and an
162
+ * owner-suffixed contributor is an ADDITION to a row, not a stand-in for the
163
+ * module that defines it. Answering with a contributor would make the reply
164
+ * depend on which frameworks a host happened to switch on: with `wardley`
165
+ * off and `c4` on, `custom:affine:surface:group` has no bare module at all,
166
+ * and returning C4's morph there would let a placement (or a `has`-style
167
+ * probe) be decided by a menu that only some builds ship.
168
+ */
28
169
  getModuleBy(flavour) {
29
- return this.modules.get(flavour)?.config ?? null;
170
+ return (this.modulesFor(flavour).find(module => module.id.variant === flavour)
171
+ ?.config ?? null);
30
172
  }
173
+ /**
174
+ * Where a flavour's row sits: the first module of it that actually SAYS.
175
+ *
176
+ * A row is one row however many modules contribute to it, so the placement
177
+ * is a property of the flavour and not of a registration — and a contributor
178
+ * that declares none must not be read as declaring the default. Scanning for
179
+ * the first module with a `placement` keeps the answer the same whether the
180
+ * framework that states it is registered first, last, or under an owner.
181
+ */
31
182
  getModulePlacement(flavour, fallback = 'top') {
32
- return (this.getModuleBy(`custom:${flavour}`)?.placement ??
33
- this.getModuleBy(flavour)?.placement ??
183
+ return (this.#placementOf(`custom:${flavour}`) ??
184
+ this.#placementOf(flavour) ??
34
185
  fallback);
35
186
  }
187
+ #placementOf(flavour) {
188
+ for (const module of this.modulesFor(flavour)) {
189
+ if (module.config.placement)
190
+ return module.config.placement;
191
+ }
192
+ return undefined;
193
+ }
36
194
  static setup(di) {
37
195
  di.addImpl(ToolbarRegistryIdentifier, this, [StdIdentifier]);
38
196
  }
@@ -95,7 +95,9 @@ export function buildUniverseRegistry(packs) {
95
95
  const drafts = new Map();
96
96
  const frameworks = [];
97
97
  let rank = 0;
98
- const issue = (severity, code, message, id) => issues.push(id === undefined ? { severity, code, message } : { severity, code, id, message });
98
+ const issue = (severity, code, message, id) => issues.push(id === undefined
99
+ ? { severity, code, message }
100
+ : { severity, code, id, message });
99
101
  for (const pack of packs) {
100
102
  if (pack?.formatVersion !== 1) {
101
103
  // The WHOLE pack is dropped: a format we do not understand may mean
@@ -126,7 +128,7 @@ export function buildUniverseRegistry(packs) {
126
128
  const merged = [...drafts.values()]
127
129
  .map(draft => finishDraft(draft))
128
130
  .sort((a, b) => (a.order ?? 0) - (b.order ?? 0) ||
129
- (drafts.get(a.id).rank - drafts.get(b.id).rank) ||
131
+ drafts.get(a.id).rank - drafts.get(b.id).rank ||
130
132
  a.id.localeCompare(b.id));
131
133
  // Keyed by plain `string`, not by `QualifiedId`: a lookup comes from a
132
134
  // DOCUMENT, which may carry any id at all, and the template-literal type is a
@@ -172,7 +174,7 @@ function mergeTagDef(drafts, def, pack, issue, rank) {
172
174
  const incomingOpen = def.values === 'open';
173
175
  const existingOpen = existing.values === 'open';
174
176
  if (incomingOpen !== existingOpen) {
175
- issue('error', 'duplicate-conflict', `Tag "${def.id}": pack "${pack.packId}" declares ${incomingOpen ? "an open value list" : 'a closed value list'} but the opposite was declared first and stands.`, def.id);
177
+ issue('error', 'duplicate-conflict', `Tag "${def.id}": pack "${pack.packId}" declares ${incomingOpen ? 'an open value list' : 'a closed value list'} but the opposite was declared first and stands.`, def.id);
176
178
  }
177
179
  else if (existing.values !== 'open') {
178
180
  // Additive: values union by value id, last cosmetic wins.
@@ -1,7 +1,7 @@
1
1
  import { CodeBlockSchemaExtension, DatabaseBlockSchemaExtension, ImageBlockSchemaExtension, ListBlockSchemaExtension, NoteBlockSchemaExtension, ParagraphBlockSchemaExtension, RootBlockSchemaExtension, } from '../../affine-model/index.js';
2
2
  import { Container } from '../../global/di/index.js';
3
3
  import { TextSelection } from '../../std/index.js';
4
- import { Text, } from '../../store/index.js';
4
+ import { Text } from '../../store/index.js';
5
5
  import { TestWorkspace } from '../../store/test/index.js';
6
6
  import { createTestHost } from './create-test-host.js';
7
7
  const DEFAULT_EXTENSIONS = [
@@ -1,5 +1,5 @@
1
1
  import { FrameBlockModel, GroupElementModel } from '../../affine-model/index.js';
2
- import { deserializeXYWH, getQuadBoundWithRotation, } from '../../global/gfx/index.js';
2
+ import { deserializeXYWH, getQuadBoundWithRotation } from '../../global/gfx/index.js';
3
3
  export function getSelectedRect(selected) {
4
4
  if (selected.length === 0) {
5
5
  return new DOMRect();
@@ -11,12 +11,46 @@ declare global {
11
11
  showOpenFilePicker?: (options?: OpenFilePickerOptions) => Promise<FileSystemFileHandle[]>;
12
12
  }
13
13
  }
14
+ /**
15
+ * One filter a file dialog offers: what to call it, and the MIME types it
16
+ * accepts with the extensions that go with each.
17
+ *
18
+ * The table below is the CLOSED list of filters this library ships. It is not
19
+ * the only source of one: an interchange format declares its own extensions and
20
+ * mime (`docs/adr/0012`), and a picker built from a registry entry cannot be a
21
+ * row in a hand-maintained table — the whole point of the registry is that a
22
+ * framework adds a format without a second file agreeing to it. So the shape is
23
+ * named and {@link openSingleFileWithSpec} takes it directly; `FileTypes` keeps
24
+ * naming the filters that are the editor's own.
25
+ */
26
+ export interface FilePickerSpec {
27
+ description: string;
28
+ accept: Record<string, string[]>;
29
+ }
14
30
  /**
15
31
  * See https://web.dev/patterns/files/open-one-or-multiple-files/
32
+ *
33
+ * There is no `'Bpmn'` here any more, and no row for it above. A BPMN document
34
+ * is not the editor's own kind of file: it is one INTERCHANGE FORMAT among the
35
+ * several a framework declares (`docs/adr/0012`), and its filter is built from
36
+ * that declaration by {@link openSingleFileWithSpec}. Keeping a second copy in
37
+ * this table would be a copy nobody reads and everybody has to keep in step —
38
+ * the format's `extensions` and `mime` are the one statement of what a `.bpmn`
39
+ * file is called.
16
40
  */
17
41
  type AcceptTypes = 'Any' | 'Images' | 'Videos' | 'Audios' | 'Markdown' | 'Html' | 'Zip' | 'MindMap';
18
42
  export declare function openFilesWith(acceptType?: AcceptTypes, multiple?: boolean): Promise<File[] | null>;
19
43
  export declare function openSingleFileWith(acceptType?: AcceptTypes): Promise<File | null>;
44
+ /**
45
+ * One file, filtered by a spec the caller built rather than by a name in
46
+ * {@link FileTypes}.
47
+ *
48
+ * What {@link openSingleFileWith} is for the editor's own dialogs, this is for
49
+ * anything whose filter is DECLARED somewhere else — today the interchange
50
+ * registry, whose formats each carry their extensions and their mime and are
51
+ * added by a framework, not by editing the table above.
52
+ */
53
+ export declare function openSingleFileWithSpec(spec: FilePickerSpec): Promise<File | null>;
20
54
  export declare function getImageFilesFromLocal(): Promise<File[]>;
21
55
  export declare function downloadBlob(blob: Blob, name: string): void;
22
56
  /**