@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.
- package/dist/_pkgs/affine-block-attachment/attachment-block.js +1 -1
- package/dist/_pkgs/affine-block-callout/callout-block.js +1 -1
- package/dist/_pkgs/affine-block-code/code-block-inline.js +1 -1
- package/dist/_pkgs/affine-block-code/code-toolbar/config.js +3 -1
- package/dist/_pkgs/affine-block-code/code-toolbar/index.js +1 -1
- package/dist/_pkgs/affine-block-database/data-source.js +1 -2
- package/dist/_pkgs/affine-block-database/external-data-source.js +1 -1
- package/dist/_pkgs/affine-block-edgeless-text/edgeless-text-block.js +1 -1
- package/dist/_pkgs/affine-block-edgeless-text/edgeless-toolbar/config.js +1 -1
- package/dist/_pkgs/affine-block-embed/embed-figma-block/embed-figma-service.js +1 -1
- package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +2 -2
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/toolbar.js +1 -1
- package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/utils.js +1 -1
- package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +1 -1
- package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/embed-synced-doc-block.js +1 -1
- package/dist/_pkgs/affine-block-frame/frame-manager.js +3 -1
- package/dist/_pkgs/affine-block-frame/frame-tool.js +1 -1
- package/dist/_pkgs/affine-block-image/components/page-image-block.js +1 -1
- package/dist/_pkgs/affine-block-image/image-edgeless-block.js +1 -1
- package/dist/_pkgs/affine-block-list/utils/forward-delete.js +1 -1
- package/dist/_pkgs/affine-block-note/commands/select-block.js +1 -1
- package/dist/_pkgs/affine-block-note/commands/select-blocks-between.js +1 -1
- package/dist/_pkgs/affine-block-note/move-block.js +1 -1
- package/dist/_pkgs/affine-block-paragraph/utils/forward-delete.js +2 -2
- package/dist/_pkgs/affine-block-paragraph/utils/merge-with-prev.js +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/edgeless-keyboard.js +1 -1
- package/dist/_pkgs/affine-block-root/edgeless/utils/clipboard-utils.js +1 -1
- package/dist/_pkgs/affine-block-surface/adapters/markdown/element-adapter/type.js +1 -1
- package/dist/_pkgs/affine-block-surface/adapters/plain-text/element-adapter/type.js +1 -1
- package/dist/_pkgs/affine-block-surface/element-model/index.d.ts +18 -3
- package/dist/_pkgs/affine-block-surface/element-model/index.js +12 -2
- package/dist/_pkgs/affine-block-surface/extensions/audit.js +23 -6
- package/dist/_pkgs/affine-block-surface/extensions/crud-extension.js +10 -5
- package/dist/_pkgs/affine-block-surface/extensions/editor-anchored-panel.js +2 -1
- package/dist/_pkgs/affine-block-surface/extensions/element-renderer.js +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/export-manager/export-manager.js +1 -1
- package/dist/_pkgs/affine-block-surface/extensions/index.d.ts +4 -0
- package/dist/_pkgs/affine-block-surface/extensions/index.js +4 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-import.d.ts +172 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +393 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +306 -0
- package/dist/_pkgs/affine-block-surface/extensions/interchange.js +64 -0
- package/dist/_pkgs/affine-block-surface/extensions/map-quality-widget.js +61 -61
- package/dist/_pkgs/affine-block-surface/extensions/morph-toolbar.d.ts +248 -0
- package/dist/_pkgs/affine-block-surface/extensions/morph-toolbar.js +248 -0
- package/dist/_pkgs/affine-block-surface/extensions/reading-widget.js +78 -80
- package/dist/_pkgs/affine-block-surface/extensions/reading.js +2 -2
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.d.ts +107 -0
- package/dist/_pkgs/affine-block-surface/extensions/svg-sketch.js +958 -0
- package/dist/_pkgs/affine-block-surface/extensions/validation.d.ts +809 -20
- package/dist/_pkgs/affine-block-surface/extensions/validation.js +1661 -50
- package/dist/_pkgs/affine-block-surface/extensions/violation-detail-widget.d.ts +50 -6
- package/dist/_pkgs/affine-block-surface/extensions/violation-detail-widget.js +129 -32
- package/dist/_pkgs/affine-block-surface/framework-background/def.d.ts +289 -0
- package/dist/_pkgs/affine-block-surface/framework-background/def.js +126 -1
- package/dist/_pkgs/affine-block-surface/framework-background/labels.d.ts +42 -2
- package/dist/_pkgs/affine-block-surface/framework-background/labels.js +80 -4
- package/dist/_pkgs/affine-block-surface/framework-background/renderer.js +172 -11
- package/dist/_pkgs/affine-block-surface/renderer/overlay.js +1 -1
- package/dist/_pkgs/affine-block-surface-ref/portal/note.js +2 -2
- package/dist/_pkgs/affine-block-table/commands.js +1 -1
- package/dist/_pkgs/affine-components/edgeless-shape-color-picker/color-picker.js +1 -1
- package/dist/_pkgs/affine-components/embed-card-modal/embed-card-edit-modal.js +1 -1
- package/dist/_pkgs/affine-components/filterable-list/styles.js +1 -1
- package/dist/_pkgs/affine-components/peek/peekable.js +1 -1
- package/dist/_pkgs/affine-components/toolbar/index.d.ts +1 -0
- package/dist/_pkgs/affine-components/toolbar/index.js +1 -0
- package/dist/_pkgs/affine-components/toolbar/menu-button.js +1 -1
- package/dist/_pkgs/affine-components/toolbar/picker-menu.d.ts +55 -0
- package/dist/_pkgs/affine-components/toolbar/picker-menu.js +47 -0
- package/dist/_pkgs/affine-fragment-frame-panel/body/frame-panel-body.js +2 -2
- package/dist/_pkgs/affine-fragment-frame-panel/header/frame-panel-header.js +2 -2
- package/dist/_pkgs/affine-fragment-frame-panel/header/frames-setting-menu.js +1 -1
- package/dist/_pkgs/affine-fragment-outline/mobile-outline-panel.js +1 -1
- package/dist/_pkgs/affine-gfx-brush/toolbar/components/pen/pen-tool-button.js +1 -1
- package/dist/_pkgs/affine-gfx-connector/connector-manager.js +7 -10
- package/dist/_pkgs/affine-gfx-connector/connector-tool.d.ts +19 -1
- package/dist/_pkgs/affine-gfx-connector/connector-tool.js +10 -2
- package/dist/_pkgs/affine-gfx-connector/connector-watcher.js +1 -4
- package/dist/_pkgs/affine-gfx-connector/direction/direction-reveal.js +2 -1
- package/dist/_pkgs/affine-gfx-connector/direction/invert-direction.js +1 -3
- package/dist/_pkgs/affine-gfx-connector/element-renderer/index.js +1 -1
- package/dist/_pkgs/affine-gfx-connector/element-renderer/utils.js +1 -1
- package/dist/_pkgs/affine-gfx-connector/toolbar/config.js +1 -1
- package/dist/_pkgs/affine-gfx-mindmap/adapter/markdown/index.js +1 -1
- package/dist/_pkgs/affine-gfx-mindmap/adapter/utils.js +1 -1
- package/dist/_pkgs/affine-gfx-mindmap/templates/index.js +583 -4
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-menu.js +23 -23
- package/dist/_pkgs/affine-gfx-mindmap/toolbar/mindmap-tool-button.js +5 -1
- package/dist/_pkgs/affine-gfx-mindmap/view/utils.js +1 -1
- package/dist/_pkgs/affine-gfx-note/overlay/overlay.js +1 -1
- package/dist/_pkgs/affine-gfx-note/toolbar/note-senior-button.js +1 -1
- package/dist/_pkgs/affine-gfx-shape/consts.js +1 -1
- package/dist/_pkgs/affine-gfx-shape/draggable/shape-draggable.js +1 -1
- package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/polygon.js +17 -9
- package/dist/_pkgs/affine-gfx-shape/element-renderer/shape/utils.js +8 -3
- package/dist/_pkgs/affine-gfx-shape/element-view.js +19 -12
- package/dist/_pkgs/affine-gfx-shape/overlay/polygon-drawing-overlay.js +1 -1
- package/dist/_pkgs/affine-gfx-shape/overlay/polygon-vertex-editing-overlay.js +12 -7
- package/dist/_pkgs/affine-gfx-shape/overlay/shape-overlay.js +1 -1
- package/dist/_pkgs/affine-gfx-shape/polygon-tool.js +4 -2
- package/dist/_pkgs/affine-gfx-shape/shape-tool.js +1 -1
- package/dist/_pkgs/affine-gfx-shape/text/edgeless-shape-text-editor.js +1 -3
- package/dist/_pkgs/affine-gfx-template/builtin/other.js +66 -17
- package/dist/_pkgs/affine-gfx-template/toolbar/overlay-scrollbar.js +1 -1
- package/dist/_pkgs/affine-gfx-template/toolbar/template-panel.js +1 -1
- package/dist/_pkgs/affine-gfx-text/toolbar/actions.js +1 -1
- package/dist/_pkgs/affine-inline-link/command.js +1 -1
- package/dist/_pkgs/affine-inline-preset/command/format-block.js +1 -1
- package/dist/_pkgs/affine-inline-preset/command/format-native.js +2 -2
- package/dist/_pkgs/affine-inline-preset/command/format-text.js +1 -1
- package/dist/_pkgs/affine-inline-preset/inline-spec.js +1 -1
- package/dist/_pkgs/affine-inline-reference/reference-node/reference-node.js +1 -1
- package/dist/_pkgs/affine-model/elements/bpmn/node.d.ts +40 -7
- package/dist/_pkgs/affine-model/elements/bpmn/node.js +4 -2
- package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +74 -17
- package/dist/_pkgs/affine-model/elements/bpmn/pool.js +55 -26
- package/dist/_pkgs/affine-model/elements/c4/board.d.ts +88 -0
- package/dist/_pkgs/affine-model/elements/c4/board.js +128 -0
- package/dist/_pkgs/affine-model/elements/c4/boundary.d.ts +63 -0
- package/dist/_pkgs/affine-model/elements/c4/boundary.js +134 -0
- package/dist/_pkgs/affine-model/elements/c4/index.d.ts +3 -0
- package/dist/_pkgs/affine-model/elements/c4/index.js +3 -0
- package/dist/_pkgs/affine-model/elements/c4/node.d.ts +97 -0
- package/dist/_pkgs/affine-model/elements/c4/node.js +130 -0
- package/dist/_pkgs/affine-model/elements/connector/connector.js +1 -1
- package/dist/_pkgs/affine-model/elements/context-map/context-map.d.ts +27 -0
- package/dist/_pkgs/affine-model/elements/context-map/context-map.js +94 -0
- package/dist/_pkgs/affine-model/elements/context-map/index.d.ts +1 -0
- package/dist/_pkgs/affine-model/elements/context-map/index.js +1 -0
- package/dist/_pkgs/affine-model/elements/core-domain/core-domain.d.ts +34 -17
- package/dist/_pkgs/affine-model/elements/core-domain/core-domain.js +28 -26
- package/dist/_pkgs/affine-model/elements/event-storming/event-storming.d.ts +36 -0
- package/dist/_pkgs/affine-model/elements/event-storming/event-storming.js +103 -0
- package/dist/_pkgs/affine-model/elements/event-storming/index.d.ts +1 -0
- package/dist/_pkgs/affine-model/elements/event-storming/index.js +1 -0
- package/dist/_pkgs/affine-model/elements/index.d.ts +11 -0
- package/dist/_pkgs/affine-model/elements/index.js +3 -0
- package/dist/_pkgs/affine-model/elements/shape/api/polygon.js +2 -8
- package/dist/_pkgs/affine-shared/adapters/html/block-adapter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/markdown/block-adapter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/markdown/delta-converter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/notion-html/block-adapter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/notion-html/delta-converter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/plain-text/block-adapter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/plain-text/delta-converter.js +1 -1
- package/dist/_pkgs/affine-shared/adapters/utils/color.js +5 -2
- package/dist/_pkgs/affine-shared/commands/model-crud/draft-selected-models.js +1 -1
- package/dist/_pkgs/affine-shared/commands/selection/focus-block-end.js +1 -1
- package/dist/_pkgs/affine-shared/commands/selection/focus-block-start.js +1 -1
- package/dist/_pkgs/affine-shared/consts/index.d.ts +10 -0
- package/dist/_pkgs/affine-shared/consts/index.js +10 -0
- package/dist/_pkgs/affine-shared/services/artefact-catalogue-service.d.ts +55 -0
- package/dist/_pkgs/affine-shared/services/artefact-catalogue-service.js +35 -0
- package/dist/_pkgs/affine-shared/services/audit-service.d.ts +11 -0
- package/dist/_pkgs/affine-shared/services/command-usage-service.d.ts +31 -0
- package/dist/_pkgs/affine-shared/services/command-usage-service.js +109 -0
- package/dist/_pkgs/affine-shared/services/comment-service/block-comment-manager.js +1 -1
- package/dist/_pkgs/affine-shared/services/doc-display-meta-service.js +1 -1
- package/dist/_pkgs/affine-shared/services/embed-iframe/embed-iframe-config.js +1 -1
- package/dist/_pkgs/affine-shared/services/index.d.ts +2 -0
- package/dist/_pkgs/affine-shared/services/index.js +2 -0
- package/dist/_pkgs/affine-shared/services/pivot-materiality-publisher.js +2 -1
- package/dist/_pkgs/affine-shared/services/telemetry-service/lifecycle.d.ts +73 -1
- package/dist/_pkgs/affine-shared/services/toolbar-service/registry.d.ts +44 -0
- package/dist/_pkgs/affine-shared/services/toolbar-service/registry.js +161 -3
- package/dist/_pkgs/affine-shared/services/universe-tag-defs-service.js +5 -3
- package/dist/_pkgs/affine-shared/test-utils/affine-template.js +1 -1
- package/dist/_pkgs/affine-shared/utils/edgeless.js +1 -1
- package/dist/_pkgs/affine-shared/utils/file/filesys.d.ts +34 -0
- package/dist/_pkgs/affine-shared/utils/file/filesys.js +48 -7
- package/dist/_pkgs/affine-shared/utils/zod-schema.js +1 -3
- package/dist/_pkgs/affine-widget-drag-handle/utils.js +1 -1
- package/dist/_pkgs/affine-widget-edgeless-auto-connect/index.js +1 -1
- package/dist/_pkgs/affine-widget-edgeless-dragging-area/edgeless-dragging-area-rect.js +1 -1
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-default.d.ts +14 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-default.js +40 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.d.ts +158 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/artefact-catalogue-widget.js +572 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.d.ts +46 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.js +66 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/chord-format.d.ts +13 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/chord-format.js +50 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/edgeless-toolbar.d.ts +6 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/edgeless-toolbar.js +15 -5
- package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.d.ts +2 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/effects.js +2 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/extension/index.d.ts +7 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/index.d.ts +4 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/index.js +4 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.d.ts +19 -0
- package/dist/_pkgs/affine-widget-edgeless-toolbar/menu/command-menu.js +51 -10
- package/dist/_pkgs/affine-widget-edgeless-toolbar/view.js +9 -0
- package/dist/_pkgs/affine-widget-keyboard-toolbar/keyboard-tool-panel.js +1 -1
- package/dist/_pkgs/affine-widget-linked-doc/import-doc/import-doc.js +1 -1
- package/dist/_pkgs/affine-widget-linked-doc/linked-doc-popover.js +1 -1
- package/dist/_pkgs/affine-widget-page-dragging-area/index.js +1 -1
- package/dist/_pkgs/affine-widget-remote-selection/doc/doc-remote-selection.js +1 -1
- package/dist/_pkgs/affine-widget-toolbar/toolbar.js +6 -2
- package/dist/_pkgs/affine-widget-toolbar/utils.js +6 -2
- package/dist/_pkgs/data-view/view-presets/calendar/pc/actions.js +8 -12
- package/dist/_pkgs/data-view/view-presets/calendar/pc/view.js +52 -40
- package/dist/_pkgs/data-view/view-presets/table/mobile/group.js +3 -1
- package/dist/_pkgs/data-view/view-presets/table/pc/group.js +3 -1
- package/dist/_pkgs/std/clipboard/utils.js +4 -1
- package/dist/_pkgs/std/extension/command-registry.d.ts +159 -5
- package/dist/_pkgs/std/extension/command-registry.js +202 -4
- package/dist/_pkgs/std/extension/shortcut.js +2 -1
- package/dist/_pkgs/std/gfx/grid.js +1 -1
- package/dist/_pkgs/std/gfx/index.d.ts +1 -1
- package/dist/_pkgs/std/gfx/model/surface/element-model.d.ts +160 -0
- package/dist/_pkgs/std/gfx/model/surface/element-model.js +75 -1
- package/dist/_pkgs/std/gfx/model/surface/role.js +5 -3
- package/dist/_pkgs/std/gfx/model/surface/surface-model.js +2 -1
- package/dist/_pkgs/std/gfx/selection.js +1 -1
- package/dist/_pkgs/std/gfx/viewport.js +1 -1
- package/dist/_pkgs/std/inline/extensions/inline-manager.js +1 -1
- package/dist/_pkgs/std/inline/extensions/markdown-matcher.js +1 -1
- package/dist/_pkgs/store/model/store/crud.js +4 -1
- package/dist/commands.js +1 -1
- package/dist/extensions/command-usage.d.ts +23 -0
- package/dist/extensions/command-usage.js +37 -0
- package/dist/extensions/view.js +12 -0
- package/dist/frameworks.js +32 -0
- package/dist/translations.js +90 -2
- 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
|
|
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
|
|
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
|
|
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)
|
|
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.
|
|
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
|
|
33
|
-
this
|
|
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
|
|
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
|
-
|
|
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 ?
|
|
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
|
|
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
|
|
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
|
/**
|