@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
|
@@ -3,7 +3,10 @@
|
|
|
3
3
|
* Used to embed plain text in the clipboard snapshot wrapper.
|
|
4
4
|
*/
|
|
5
5
|
export function escapeHTML(value) {
|
|
6
|
-
return value
|
|
6
|
+
return value
|
|
7
|
+
.replace(/&/g, '&')
|
|
8
|
+
.replace(/</g, '<')
|
|
9
|
+
.replace(/>/g, '>');
|
|
7
10
|
}
|
|
8
11
|
export function onlyContainImgElement(ast) {
|
|
9
12
|
if (ast.type === 'element') {
|
|
@@ -12,7 +12,7 @@ import { type ShortcutDescriptor, type ShortcutScope } from './shortcut.js';
|
|
|
12
12
|
*/
|
|
13
13
|
export type ElementCreationSource = 'shortcut' | 'toolbar:general' | 'toolbar:dnd' | 'canvas:drop' | 'canvas:draw' | 'canvas:dbclick' | 'canvas:paste' | 'context-menu' | 'ai' | 'internal' | 'conversation' | 'manually save';
|
|
14
14
|
/**
|
|
15
|
-
* The
|
|
15
|
+
* The eight framework modules that own commands. An EXPLICIT list, not a
|
|
16
16
|
* subset assertion over `OPTIONAL_BLOCKS`: that list mixes frameworks with
|
|
17
17
|
* plain blocks. `packages/affine/all/src/frameworks.ts` asserts that every id
|
|
18
18
|
* here is a real flag key (`satisfies readonly OptionalBlock[]`).
|
|
@@ -20,7 +20,7 @@ export type ElementCreationSource = 'shortcut' | 'toolbar:general' | 'toolbar:dn
|
|
|
20
20
|
* `ddd-aggregate` is deliberately absent: it ships DDD *templates* only — no
|
|
21
21
|
* senior button, no commands — so it is a packaging bundle, not a framework.
|
|
22
22
|
*/
|
|
23
|
-
export declare const FRAMEWORK_IDS: readonly ["wardley", "edgy", "cynefin-estuarine", "bpmn", "ddd-event-storming", "ddd-core-domain", "ddd-context-map"];
|
|
23
|
+
export declare const FRAMEWORK_IDS: readonly ["wardley", "edgy", "cynefin-estuarine", "bpmn", "c4", "ddd-event-storming", "ddd-core-domain", "ddd-context-map"];
|
|
24
24
|
export type FrameworkId = (typeof FRAMEWORK_IDS)[number];
|
|
25
25
|
export type CommandOwner = 'core' | FrameworkId;
|
|
26
26
|
/**
|
|
@@ -204,6 +204,13 @@ export interface FrameworkDescriptor {
|
|
|
204
204
|
id: FrameworkId;
|
|
205
205
|
/** Replaces the raw English `SeniorTool.name`. */
|
|
206
206
|
labelKey: string;
|
|
207
|
+
/**
|
|
208
|
+
* The English wording behind {@link labelKey} — what a standalone editor
|
|
209
|
+
* shows and what every restatement of the name (senior button, tooltip)
|
|
210
|
+
* must agree with. Carried HERE so the manifest knows the fallback and the
|
|
211
|
+
* drift check can hold the restatements to it.
|
|
212
|
+
*/
|
|
213
|
+
labelFallback: string;
|
|
207
214
|
iconKey: string;
|
|
208
215
|
order?: number;
|
|
209
216
|
/** First keystroke of this framework's chords — allocated, not ad hoc. */
|
|
@@ -248,6 +255,37 @@ export type CommandTelemetryReporter = (report: {
|
|
|
248
255
|
invocation: CommandInvocation;
|
|
249
256
|
}) => void;
|
|
250
257
|
export declare const CommandTelemetryIdentifier: import("../../global/di/types.js").ServiceIdentifier<CommandTelemetryReporter> & (<U extends CommandTelemetryReporter = CommandTelemetryReporter>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
|
|
258
|
+
/** What a {@link CommandUsageStore} knows about one command. */
|
|
259
|
+
export interface CommandUsageStats {
|
|
260
|
+
count: number;
|
|
261
|
+
lastUsedAt: number;
|
|
262
|
+
}
|
|
263
|
+
/**
|
|
264
|
+
* Per-user recency and frequency, measured at the same bottleneck telemetry is
|
|
265
|
+
* emitted from — and deliberately NOT the same thing.
|
|
266
|
+
*
|
|
267
|
+
* Telemetry is analytics: it leaves for a product dashboard, it only covers
|
|
268
|
+
* commands that declare a `telemetry` field, and no in-editor decision may
|
|
269
|
+
* depend on it. Usage is local state read back BY the editor: PF6 shows only
|
|
270
|
+
* thirteen of a framework's commands in the senior sub-menu once the framework
|
|
271
|
+
* has more than fourteen artefacts, ranked as "seven most-recent + six
|
|
272
|
+
* most-used" (PO re-arbitration of 2026-08-28, superseding the 4 + 3 of
|
|
273
|
+
* 2026-08-26). That ranking needs a measure for EVERY command — a `core` action, a
|
|
274
|
+
* self-emitting one, one nobody ever thought to instrument — so the record call
|
|
275
|
+
* sits outside the telemetry condition and stays there.
|
|
276
|
+
*
|
|
277
|
+
* ADR 0008 listed this as an open question ("needs a host-side usage store; the
|
|
278
|
+
* registry only needs to accept an injected comparator"). This identifier is
|
|
279
|
+
* the measurement half of that answer; the selection half is
|
|
280
|
+
* {@link selectSeniorMenuCommands}.
|
|
281
|
+
*/
|
|
282
|
+
export interface CommandUsageStore {
|
|
283
|
+
/** One invocation happened. Called by {@link runCommand}, best-effort. */
|
|
284
|
+
record(command: AnyCommandDescriptor, invocation: CommandInvocation): void;
|
|
285
|
+
/** `undefined` when the command was never invoked by this user. */
|
|
286
|
+
statsOf(commandId: string): CommandUsageStats | undefined;
|
|
287
|
+
}
|
|
288
|
+
export declare const CommandUsageIdentifier: import("../../global/di/types.js").ServiceIdentifier<CommandUsageStore> & (<U extends CommandUsageStore = CommandUsageStore>(variant: import("../../global/di/types.js").ServiceVariant) => import("../../global/di/types.js").ServiceIdentifier<U>);
|
|
251
289
|
/**
|
|
252
290
|
* Mirrors `GfxSelectionManager.editing` without importing `@labre/std/gfx`
|
|
253
291
|
* (which would close an import cycle through `extension/`): the gfx manager
|
|
@@ -272,9 +310,9 @@ export declare const isEditingOnCanvas: (std: BlockStdScope) => boolean;
|
|
|
272
310
|
export declare function isCommandAvailable(std: BlockStdScope, command: AnyCommandDescriptor): boolean;
|
|
273
311
|
/**
|
|
274
312
|
* THE bottleneck: the one place a command runs, and therefore the one place
|
|
275
|
-
* its telemetry is emitted. Every surface (sub-menu,
|
|
276
|
-
* sidepanel, agent) goes through here, which is what removes
|
|
277
|
-
* `track()` duplicates.
|
|
313
|
+
* its telemetry is emitted and its usage measured. Every surface (sub-menu,
|
|
314
|
+
* shortcut, palette, sidepanel, agent) goes through here, which is what removes
|
|
315
|
+
* the per-menu `track()` duplicates.
|
|
278
316
|
*/
|
|
279
317
|
export declare function runCommand(std: BlockStdScope, command: AnyCommandDescriptor, invocation: CommandInvocation, params?: unknown): void;
|
|
280
318
|
/**
|
|
@@ -317,5 +355,121 @@ export declare function getRegisteredCommands(std: BlockStdScope): AnyCommandDes
|
|
|
317
355
|
* button list anywhere.
|
|
318
356
|
*/
|
|
319
357
|
export declare function getCommandsForSurface(std: BlockStdScope, owner: CommandOwner, surface: CommandSurface): AnyCommandDescriptor[];
|
|
358
|
+
/**
|
|
359
|
+
* The senior sub-menu's hard cap: 14 buttons in one row of the popover. The
|
|
360
|
+
* registry test asserts no owner DECLARES more than this; the ranking below is
|
|
361
|
+
* what happens when an owner's whole catalogue outgrows it.
|
|
362
|
+
*/
|
|
363
|
+
export declare const SENIOR_MENU_CAP = 14;
|
|
364
|
+
/**
|
|
365
|
+
* How many buttons survive once an owner overflows: {@link MENU_RECENT_SLOTS}
|
|
366
|
+
* by recency plus {@link MENU_USED_SLOTS} by frequency — thirteen (PO
|
|
367
|
+
* re-arbitration of 2026-08-28, superseding the 4 + 3 of 2026-08-26).
|
|
368
|
+
*
|
|
369
|
+
* Thirteen and not fourteen because the permanent "More artefacts…" button is
|
|
370
|
+
* the fourteenth: an overflowed sub-menu therefore renders exactly the cap, and
|
|
371
|
+
* the row a user meets past the cap is as wide as the one they meet below it.
|
|
372
|
+
*/
|
|
373
|
+
export declare const SENIOR_MENU_RANKED_SLOTS: number;
|
|
374
|
+
/**
|
|
375
|
+
* How many rows the catalogue sidepanel's "Recent & frequent" head section
|
|
376
|
+
* holds: {@link HEAD_RECENT_SLOTS} + {@link HEAD_USED_SLOTS} = seven.
|
|
377
|
+
*
|
|
378
|
+
* Deliberately NOT {@link SENIOR_MENU_RANKED_SLOTS}, and this is the third
|
|
379
|
+
* decision of 2026-08-28 (architect's ruling on review of the two PO ones). The
|
|
380
|
+
* sub-menu's thirteen is argued from ITS geometry — a horizontal row of ~24px
|
|
381
|
+
* icon buttons, thirteen of them plus the More button making up the fourteen
|
|
382
|
+
* cap. None of that transfers to a vertical list of 44px rows in a panel
|
|
383
|
+
* `min(320px, 85vw)` wide: thirteen of those is ~604px of head section, which
|
|
384
|
+
* on a 13" laptop is the entire first screen, every row of it a duplicate of a
|
|
385
|
+
* row filed below, pushing the categories — the panel's actual information
|
|
386
|
+
* architecture — wholly under the fold. Worst for exactly the power user the
|
|
387
|
+
* section exists to serve.
|
|
388
|
+
*
|
|
389
|
+
* So the two surfaces share the ARBITRATION and not the MAGNITUDE: one
|
|
390
|
+
* algorithm, two parameterisations. Seven keeps the size the PO recette of
|
|
391
|
+
* 27/08/2026 signed off on, and the 4 + 3 split keeps both halves of a section
|
|
392
|
+
* labelled "Recent & frequent" — a plain `.slice(0, 7)` of the sub-menu's pick
|
|
393
|
+
* would have returned seven recency picks and zero frequency ones, quietly
|
|
394
|
+
* deleting the "& frequent" half.
|
|
395
|
+
*/
|
|
396
|
+
export declare const CATALOGUE_HEAD_RANKED_SLOTS: number;
|
|
397
|
+
/**
|
|
398
|
+
* Which of an owner's commands the senior sub-menu actually shows.
|
|
399
|
+
*
|
|
400
|
+
* Pure — no `std`, no DOM, no store — because this is the arbitration between
|
|
401
|
+
* a framework's whole toolbox and fourteen buttons, and an arbitration nobody
|
|
402
|
+
* can run in a unit test is a design review in disguise (`docs/adr/0008`,
|
|
403
|
+
* amendment of 2026-08-26).
|
|
404
|
+
*
|
|
405
|
+
* Below the cap nothing is arbitrated: the sub-menu IS the framework's
|
|
406
|
+
* `'senior-menu'` surface, authored order and all. Past it the sub-menu becomes
|
|
407
|
+
* a shortcut to the thirteen commands THIS user reaches for, beside one button
|
|
408
|
+
* to the catalogue sidepanel where the rest live.
|
|
409
|
+
*
|
|
410
|
+
* **Eligibility is declared, never earned** (PO ruling of 2026-08-28). What
|
|
411
|
+
* gets ranked is the `'senior-menu'` surface — the ≤14 its author nominated —
|
|
412
|
+
* and NOT the whole catalogue. The trigger still reads the catalogue (an owner
|
|
413
|
+
* overflows when its total toolbox outgrows the cap), but a command that
|
|
414
|
+
* deliberately declines the sub-menu stays out of it however often it is
|
|
415
|
+
* invoked. `bpmn.exportXml` is the case that made the rule: its subject is the
|
|
416
|
+
* whole BOARD, it lives in the pool's "⋮" and in the catalogue, and usage alone
|
|
417
|
+
* used to drag it into a row of things you DRAW — where "Export BPMN" answers
|
|
418
|
+
* no question a user asked. A surface a command declines is a statement about
|
|
419
|
+
* where it belongs, not a default to be out-voted.
|
|
420
|
+
*
|
|
421
|
+
* The thirteen are chosen on two axes because one is not enough — frequency
|
|
422
|
+
* alone never surfaces what the user picked up yesterday, recency alone
|
|
423
|
+
* reshuffles every click. They are then laid out in **authored order**, not in
|
|
424
|
+
* rank order: a menu whose buttons swap places under the cursor is the dark
|
|
425
|
+
* pattern this whole feature exists to avoid, so what the ranking decides is
|
|
426
|
+
* membership, never position.
|
|
427
|
+
*
|
|
428
|
+
* With no usage recorded at all, both axes collapse to authored order and the
|
|
429
|
+
* result is the first thirteen of the nominated list — a deterministic cold
|
|
430
|
+
* start, not an empty menu.
|
|
431
|
+
*
|
|
432
|
+
* @param menu the owner's `'senior-menu'` commands, already order-sorted. It is
|
|
433
|
+
* both the pool the ranking draws membership from and the authored order the
|
|
434
|
+
* survivors are laid back out in.
|
|
435
|
+
* @param catalogue the owner's whole `'catalogue'` surface, already
|
|
436
|
+
* order-sorted. Read only for its length, to decide whether the owner
|
|
437
|
+
* overflows at all; it contributes no candidate.
|
|
438
|
+
* @param statsOf per-user measure; `undefined` for a command never invoked
|
|
439
|
+
*/
|
|
440
|
+
export declare function selectSeniorMenuCommands(menu: AnyCommandDescriptor[], catalogue: AnyCommandDescriptor[], statsOf: (id: string) => CommandUsageStats | undefined): {
|
|
441
|
+
commands: AnyCommandDescriptor[];
|
|
442
|
+
overflow: boolean;
|
|
443
|
+
};
|
|
444
|
+
/**
|
|
445
|
+
* The commands a user actually reaches for, for surfaces that lead with them —
|
|
446
|
+
* the catalogue sidepanel's "recent & frequent" head section (PO recette,
|
|
447
|
+
* 27/08/2026).
|
|
448
|
+
*
|
|
449
|
+
* Same arbitration as the senior sub-menu ({@link pickByUsage}) — one ranking,
|
|
450
|
+
* two consumers, never two opinions — with four deliberate differences:
|
|
451
|
+
*
|
|
452
|
+
* - it seats {@link CATALOGUE_HEAD_RANKED_SLOTS} (4 recent + 3 used), not the
|
|
453
|
+
* sub-menu's thirteen. The two surfaces share the arbitration and not the
|
|
454
|
+
* magnitude: see {@link CATALOGUE_HEAD_RANKED_SLOTS} for why thirteen 44px
|
|
455
|
+
* rows in a 320px panel would bury the categories under the fold.
|
|
456
|
+
* - it ranks the CATALOGUE surface, and keeps doing so after the eligibility
|
|
457
|
+
* ruling of 2026-08-28 narrowed the sub-menu to `'senior-menu'` declarers.
|
|
458
|
+
* That ruling is about the sub-menu, which is a row of things you DRAW; the
|
|
459
|
+
* sidepanel is the full-catalogue surface, the one place every command of a
|
|
460
|
+
* framework is reachable, so a catalogue-only command like `bpmn.exportXml`
|
|
461
|
+
* heading this section is the section working — "here is what you reach for"
|
|
462
|
+
* over a panel that lists everything, not a board action smuggled into a
|
|
463
|
+
* palette. Passing the menu surface here would hide a user's own habits from
|
|
464
|
+
* the very panel built to show them.
|
|
465
|
+
* - only commands that CARRY a measure are returned. The sub-menu backfills
|
|
466
|
+
* with authored order because it must always show something; a "recently
|
|
467
|
+
* used" section padded with the never-used would be a label that lies, so
|
|
468
|
+
* with no usage at all this returns `[]` and the section is simply absent.
|
|
469
|
+
* - the PICK order is kept (the recency ranks, then the frequency additions),
|
|
470
|
+
* not re-sorted by authored order: the section's whole message is "yours,
|
|
471
|
+
* latest first", and it only recomposes when the panel reopens.
|
|
472
|
+
*/
|
|
473
|
+
export declare function rankCommandsByUsage(catalogue: AnyCommandDescriptor[], statsOf: (id: string) => CommandUsageStats | undefined): AnyCommandDescriptor[];
|
|
320
474
|
/** Resolve an `iconKey` through the registered icon tables. */
|
|
321
475
|
export declare function getCommandIcon(std: BlockStdScope, iconKey: string | undefined): TemplateResult | undefined;
|
|
@@ -2,7 +2,7 @@ import { createIdentifier } from '../../global/di/index.js';
|
|
|
2
2
|
import { SurfaceSelection } from '../selection/index.js';
|
|
3
3
|
import { ShortcutExtension, } from './shortcut.js';
|
|
4
4
|
/**
|
|
5
|
-
* The
|
|
5
|
+
* The eight framework modules that own commands. An EXPLICIT list, not a
|
|
6
6
|
* subset assertion over `OPTIONAL_BLOCKS`: that list mixes frameworks with
|
|
7
7
|
* plain blocks. `packages/affine/all/src/frameworks.ts` asserts that every id
|
|
8
8
|
* here is a real flag key (`satisfies readonly OptionalBlock[]`).
|
|
@@ -15,6 +15,7 @@ export const FRAMEWORK_IDS = [
|
|
|
15
15
|
'edgy',
|
|
16
16
|
'cynefin-estuarine',
|
|
17
17
|
'bpmn',
|
|
18
|
+
'c4',
|
|
18
19
|
'ddd-event-storming',
|
|
19
20
|
'ddd-core-domain',
|
|
20
21
|
'ddd-context-map',
|
|
@@ -24,6 +25,7 @@ export const CommandDescriptorIdentifier = createIdentifier('CommandDescriptor')
|
|
|
24
25
|
/** Multi-instance: one `iconKey → template` table per contributing package. */
|
|
25
26
|
export const CommandIconsIdentifier = createIdentifier('CommandIcons');
|
|
26
27
|
export const CommandTelemetryIdentifier = createIdentifier('CommandTelemetry');
|
|
28
|
+
export const CommandUsageIdentifier = createIdentifier('CommandUsage');
|
|
27
29
|
const surfaceSelections = (std) => std.selection.filter(SurfaceSelection);
|
|
28
30
|
/**
|
|
29
31
|
* Mirrors `GfxSelectionManager.editing` without importing `@labre/std/gfx`
|
|
@@ -60,14 +62,17 @@ export function isCommandAvailable(std, command) {
|
|
|
60
62
|
}
|
|
61
63
|
/**
|
|
62
64
|
* THE bottleneck: the one place a command runs, and therefore the one place
|
|
63
|
-
* its telemetry is emitted. Every surface (sub-menu,
|
|
64
|
-
* sidepanel, agent) goes through here, which is what removes
|
|
65
|
-
* `track()` duplicates.
|
|
65
|
+
* its telemetry is emitted and its usage measured. Every surface (sub-menu,
|
|
66
|
+
* shortcut, palette, sidepanel, agent) goes through here, which is what removes
|
|
67
|
+
* the per-menu `track()` duplicates.
|
|
66
68
|
*/
|
|
67
69
|
export function runCommand(std, command, invocation, params) {
|
|
68
70
|
const result = command.run(std, invocation, params);
|
|
69
71
|
if (result instanceof Promise)
|
|
70
72
|
result.catch(console.error);
|
|
73
|
+
// Before the telemetry gate on purpose: usage is measured for EVERY command,
|
|
74
|
+
// including the ones with no `telemetry` field and the self-emitting ones.
|
|
75
|
+
std.getOptional(CommandUsageIdentifier)?.record(command, invocation);
|
|
71
76
|
if (!command.telemetry)
|
|
72
77
|
return;
|
|
73
78
|
std.getOptional(CommandTelemetryIdentifier)?.({ std, command, invocation });
|
|
@@ -220,6 +225,199 @@ export function getCommandsForSurface(std, owner, surface) {
|
|
|
220
225
|
.filter(c => c.owner === owner && c.surfaces.includes(surface))
|
|
221
226
|
.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));
|
|
222
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* The senior sub-menu's hard cap: 14 buttons in one row of the popover. The
|
|
230
|
+
* registry test asserts no owner DECLARES more than this; the ranking below is
|
|
231
|
+
* what happens when an owner's whole catalogue outgrows it.
|
|
232
|
+
*/
|
|
233
|
+
export const SENIOR_MENU_CAP = 14;
|
|
234
|
+
const MENU_RECENT_SLOTS = 7;
|
|
235
|
+
const MENU_USED_SLOTS = 6;
|
|
236
|
+
/**
|
|
237
|
+
* How many buttons survive once an owner overflows: {@link MENU_RECENT_SLOTS}
|
|
238
|
+
* by recency plus {@link MENU_USED_SLOTS} by frequency — thirteen (PO
|
|
239
|
+
* re-arbitration of 2026-08-28, superseding the 4 + 3 of 2026-08-26).
|
|
240
|
+
*
|
|
241
|
+
* Thirteen and not fourteen because the permanent "More artefacts…" button is
|
|
242
|
+
* the fourteenth: an overflowed sub-menu therefore renders exactly the cap, and
|
|
243
|
+
* the row a user meets past the cap is as wide as the one they meet below it.
|
|
244
|
+
*/
|
|
245
|
+
export const SENIOR_MENU_RANKED_SLOTS = MENU_RECENT_SLOTS + MENU_USED_SLOTS;
|
|
246
|
+
const HEAD_RECENT_SLOTS = 4;
|
|
247
|
+
const HEAD_USED_SLOTS = 3;
|
|
248
|
+
/**
|
|
249
|
+
* How many rows the catalogue sidepanel's "Recent & frequent" head section
|
|
250
|
+
* holds: {@link HEAD_RECENT_SLOTS} + {@link HEAD_USED_SLOTS} = seven.
|
|
251
|
+
*
|
|
252
|
+
* Deliberately NOT {@link SENIOR_MENU_RANKED_SLOTS}, and this is the third
|
|
253
|
+
* decision of 2026-08-28 (architect's ruling on review of the two PO ones). The
|
|
254
|
+
* sub-menu's thirteen is argued from ITS geometry — a horizontal row of ~24px
|
|
255
|
+
* icon buttons, thirteen of them plus the More button making up the fourteen
|
|
256
|
+
* cap. None of that transfers to a vertical list of 44px rows in a panel
|
|
257
|
+
* `min(320px, 85vw)` wide: thirteen of those is ~604px of head section, which
|
|
258
|
+
* on a 13" laptop is the entire first screen, every row of it a duplicate of a
|
|
259
|
+
* row filed below, pushing the categories — the panel's actual information
|
|
260
|
+
* architecture — wholly under the fold. Worst for exactly the power user the
|
|
261
|
+
* section exists to serve.
|
|
262
|
+
*
|
|
263
|
+
* So the two surfaces share the ARBITRATION and not the MAGNITUDE: one
|
|
264
|
+
* algorithm, two parameterisations. Seven keeps the size the PO recette of
|
|
265
|
+
* 27/08/2026 signed off on, and the 4 + 3 split keeps both halves of a section
|
|
266
|
+
* labelled "Recent & frequent" — a plain `.slice(0, 7)` of the sub-menu's pick
|
|
267
|
+
* would have returned seven recency picks and zero frequency ones, quietly
|
|
268
|
+
* deleting the "& frequent" half.
|
|
269
|
+
*/
|
|
270
|
+
export const CATALOGUE_HEAD_RANKED_SLOTS = HEAD_RECENT_SLOTS + HEAD_USED_SLOTS;
|
|
271
|
+
/**
|
|
272
|
+
* Which of an owner's commands the senior sub-menu actually shows.
|
|
273
|
+
*
|
|
274
|
+
* Pure — no `std`, no DOM, no store — because this is the arbitration between
|
|
275
|
+
* a framework's whole toolbox and fourteen buttons, and an arbitration nobody
|
|
276
|
+
* can run in a unit test is a design review in disguise (`docs/adr/0008`,
|
|
277
|
+
* amendment of 2026-08-26).
|
|
278
|
+
*
|
|
279
|
+
* Below the cap nothing is arbitrated: the sub-menu IS the framework's
|
|
280
|
+
* `'senior-menu'` surface, authored order and all. Past it the sub-menu becomes
|
|
281
|
+
* a shortcut to the thirteen commands THIS user reaches for, beside one button
|
|
282
|
+
* to the catalogue sidepanel where the rest live.
|
|
283
|
+
*
|
|
284
|
+
* **Eligibility is declared, never earned** (PO ruling of 2026-08-28). What
|
|
285
|
+
* gets ranked is the `'senior-menu'` surface — the ≤14 its author nominated —
|
|
286
|
+
* and NOT the whole catalogue. The trigger still reads the catalogue (an owner
|
|
287
|
+
* overflows when its total toolbox outgrows the cap), but a command that
|
|
288
|
+
* deliberately declines the sub-menu stays out of it however often it is
|
|
289
|
+
* invoked. `bpmn.exportXml` is the case that made the rule: its subject is the
|
|
290
|
+
* whole BOARD, it lives in the pool's "⋮" and in the catalogue, and usage alone
|
|
291
|
+
* used to drag it into a row of things you DRAW — where "Export BPMN" answers
|
|
292
|
+
* no question a user asked. A surface a command declines is a statement about
|
|
293
|
+
* where it belongs, not a default to be out-voted.
|
|
294
|
+
*
|
|
295
|
+
* The thirteen are chosen on two axes because one is not enough — frequency
|
|
296
|
+
* alone never surfaces what the user picked up yesterday, recency alone
|
|
297
|
+
* reshuffles every click. They are then laid out in **authored order**, not in
|
|
298
|
+
* rank order: a menu whose buttons swap places under the cursor is the dark
|
|
299
|
+
* pattern this whole feature exists to avoid, so what the ranking decides is
|
|
300
|
+
* membership, never position.
|
|
301
|
+
*
|
|
302
|
+
* With no usage recorded at all, both axes collapse to authored order and the
|
|
303
|
+
* result is the first thirteen of the nominated list — a deterministic cold
|
|
304
|
+
* start, not an empty menu.
|
|
305
|
+
*
|
|
306
|
+
* @param menu the owner's `'senior-menu'` commands, already order-sorted. It is
|
|
307
|
+
* both the pool the ranking draws membership from and the authored order the
|
|
308
|
+
* survivors are laid back out in.
|
|
309
|
+
* @param catalogue the owner's whole `'catalogue'` surface, already
|
|
310
|
+
* order-sorted. Read only for its length, to decide whether the owner
|
|
311
|
+
* overflows at all; it contributes no candidate.
|
|
312
|
+
* @param statsOf per-user measure; `undefined` for a command never invoked
|
|
313
|
+
*/
|
|
314
|
+
export function selectSeniorMenuCommands(menu, catalogue, statsOf) {
|
|
315
|
+
if (catalogue.length <= SENIOR_MENU_CAP) {
|
|
316
|
+
return { commands: menu, overflow: false };
|
|
317
|
+
}
|
|
318
|
+
const authored = new Map(menu.map((command, index) => [command, index]));
|
|
319
|
+
const order = (command) => authored.get(command) ?? 0;
|
|
320
|
+
// (7, 6): thirteen icon buttons, plus the "More artefacts…" one, is the cap.
|
|
321
|
+
const { commands } = pickByUsage(menu, statsOf, MENU_RECENT_SLOTS, MENU_USED_SLOTS);
|
|
322
|
+
return {
|
|
323
|
+
commands: commands.sort((a, b) => order(a) - order(b)),
|
|
324
|
+
overflow: true,
|
|
325
|
+
};
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The shared arbitration, over whatever pool its caller hands it: `recentSlots`
|
|
329
|
+
* by recency plus `usedSlots` by frequency, deduplicated.
|
|
330
|
+
*
|
|
331
|
+
* The two slot counts are PARAMETERS rather than constants because the two
|
|
332
|
+
* consumers share this ranking and not its magnitude — the sub-menu passes
|
|
333
|
+
* (7, 6) for a row of icon buttons, the sidepanel head (4, 3) for a vertical
|
|
334
|
+
* list of 44px rows. One arbitration, two parameterisations, still never two
|
|
335
|
+
* opinions.
|
|
336
|
+
*
|
|
337
|
+
* Recency goes FIRST since the PO's re-arbitration of 2026-08-28 (it was 4
|
|
338
|
+
* most-used + 3 most-recent): what a user reached for this morning is what they
|
|
339
|
+
* are still working on, and a row that leads with the all-time workhorses is a
|
|
340
|
+
* row that takes weeks to notice a new habit. So a command that tops BOTH axes
|
|
341
|
+
* consumes a RECENT slot, and the most-used slot it would have taken goes to
|
|
342
|
+
* the next candidate down the frequency ranking — which is what the backfill
|
|
343
|
+
* loop below does by walking `mostUsed` whole rather than only its head.
|
|
344
|
+
*
|
|
345
|
+
* Returned in PICK order (the recency ranks first, the frequency additions
|
|
346
|
+
* after); each consumer imposes its own display order. The `stats` map is
|
|
347
|
+
* returned with them so a consumer that filters on "was this ever measured?"
|
|
348
|
+
* does not ask the store a second time — see {@link rankCommandsByUsage}.
|
|
349
|
+
*/
|
|
350
|
+
function pickByUsage(pool, statsOf, recentSlots, usedSlots) {
|
|
351
|
+
const authored = new Map(pool.map((command, index) => [command, index]));
|
|
352
|
+
const order = (command) => authored.get(command) ?? 0;
|
|
353
|
+
// One read per command per selection: the store answers from storage, and the
|
|
354
|
+
// two axes would otherwise ask it the same question twice.
|
|
355
|
+
const stats = new Map(pool.map(c => [c, statsOf(c.id)]));
|
|
356
|
+
const rankBy = (primary, secondary) => [...pool].sort((a, b) => {
|
|
357
|
+
const left = stats.get(a);
|
|
358
|
+
const right = stats.get(b);
|
|
359
|
+
// A command nobody ever invoked ranks after every command somebody did,
|
|
360
|
+
// on either axis — it has no measure, not a low one.
|
|
361
|
+
if (!left || !right) {
|
|
362
|
+
return left ? -1 : right ? 1 : order(a) - order(b);
|
|
363
|
+
}
|
|
364
|
+
return (primary(right) - primary(left) ||
|
|
365
|
+
secondary(right) - secondary(left) ||
|
|
366
|
+
order(a) - order(b));
|
|
367
|
+
});
|
|
368
|
+
const mostUsed = rankBy(used => used.count, used => used.lastUsedAt);
|
|
369
|
+
const mostRecent = rankBy(used => used.lastUsedAt, used => used.count);
|
|
370
|
+
const total = recentSlots + usedSlots;
|
|
371
|
+
const chosen = new Set();
|
|
372
|
+
for (const command of mostRecent.slice(0, recentSlots)) {
|
|
373
|
+
chosen.add(command);
|
|
374
|
+
}
|
|
375
|
+
// The `usedSlots` remaining seats, from the frequency ranking: walking it
|
|
376
|
+
// whole rather than only its head is what makes a double pick free its
|
|
377
|
+
// most-used slot for the next candidate instead of wasting it.
|
|
378
|
+
for (const command of mostUsed) {
|
|
379
|
+
if (chosen.size >= total)
|
|
380
|
+
break;
|
|
381
|
+
chosen.add(command);
|
|
382
|
+
}
|
|
383
|
+
return { commands: [...chosen].slice(0, total), stats };
|
|
384
|
+
}
|
|
385
|
+
/**
|
|
386
|
+
* The commands a user actually reaches for, for surfaces that lead with them —
|
|
387
|
+
* the catalogue sidepanel's "recent & frequent" head section (PO recette,
|
|
388
|
+
* 27/08/2026).
|
|
389
|
+
*
|
|
390
|
+
* Same arbitration as the senior sub-menu ({@link pickByUsage}) — one ranking,
|
|
391
|
+
* two consumers, never two opinions — with four deliberate differences:
|
|
392
|
+
*
|
|
393
|
+
* - it seats {@link CATALOGUE_HEAD_RANKED_SLOTS} (4 recent + 3 used), not the
|
|
394
|
+
* sub-menu's thirteen. The two surfaces share the arbitration and not the
|
|
395
|
+
* magnitude: see {@link CATALOGUE_HEAD_RANKED_SLOTS} for why thirteen 44px
|
|
396
|
+
* rows in a 320px panel would bury the categories under the fold.
|
|
397
|
+
* - it ranks the CATALOGUE surface, and keeps doing so after the eligibility
|
|
398
|
+
* ruling of 2026-08-28 narrowed the sub-menu to `'senior-menu'` declarers.
|
|
399
|
+
* That ruling is about the sub-menu, which is a row of things you DRAW; the
|
|
400
|
+
* sidepanel is the full-catalogue surface, the one place every command of a
|
|
401
|
+
* framework is reachable, so a catalogue-only command like `bpmn.exportXml`
|
|
402
|
+
* heading this section is the section working — "here is what you reach for"
|
|
403
|
+
* over a panel that lists everything, not a board action smuggled into a
|
|
404
|
+
* palette. Passing the menu surface here would hide a user's own habits from
|
|
405
|
+
* the very panel built to show them.
|
|
406
|
+
* - only commands that CARRY a measure are returned. The sub-menu backfills
|
|
407
|
+
* with authored order because it must always show something; a "recently
|
|
408
|
+
* used" section padded with the never-used would be a label that lies, so
|
|
409
|
+
* with no usage at all this returns `[]` and the section is simply absent.
|
|
410
|
+
* - the PICK order is kept (the recency ranks, then the frequency additions),
|
|
411
|
+
* not re-sorted by authored order: the section's whole message is "yours,
|
|
412
|
+
* latest first", and it only recomposes when the panel reopens.
|
|
413
|
+
*/
|
|
414
|
+
export function rankCommandsByUsage(catalogue, statsOf) {
|
|
415
|
+
const { commands, stats } = pickByUsage(catalogue, statsOf, HEAD_RECENT_SLOTS, HEAD_USED_SLOTS);
|
|
416
|
+
// Through the map the ranking already built, not a second round of store
|
|
417
|
+
// reads: "one read per command per selection" is a promise this function used
|
|
418
|
+
// to break once per returned row.
|
|
419
|
+
return commands.filter(command => stats.get(command) !== undefined);
|
|
420
|
+
}
|
|
223
421
|
/** Resolve an `iconKey` through the registered icon tables. */
|
|
224
422
|
export function getCommandIcon(std, iconKey) {
|
|
225
423
|
if (!iconKey)
|
|
@@ -60,7 +60,8 @@ const LEGACY_MODIFIER = /^(mod|cmd|meta|ctrl|control|alt|shift)$/i;
|
|
|
60
60
|
* folded: they are valid chord prefixes.
|
|
61
61
|
*/
|
|
62
62
|
export function normalizeLegacyCombo(keys) {
|
|
63
|
-
if (keys.length > 1 &&
|
|
63
|
+
if (keys.length > 1 &&
|
|
64
|
+
keys.slice(0, -1).every(k => LEGACY_MODIFIER.test(k))) {
|
|
64
65
|
return [keys.join('-')];
|
|
65
66
|
}
|
|
66
67
|
return keys;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { DisposableGroup } from '../../global/disposable/index.js';
|
|
2
|
-
import { Bound, getBoundWithRotation, intersects
|
|
2
|
+
import { Bound, getBoundWithRotation, intersects } from '../../global/gfx/index.js';
|
|
3
3
|
import { compare } from '../utils/layer.js';
|
|
4
4
|
import { GfxExtension } from './extension.js';
|
|
5
5
|
import { GfxBlockElementModel } from './model/gfx-block-model.js';
|
|
@@ -14,7 +14,7 @@ export { gfxGroupCompatibleSymbol, isGfxGroupCompatibleModel, } from './model/ba
|
|
|
14
14
|
export { GfxBlockElementModel, type GfxCommonBlockProps, GfxCompatibleBlockModel as GfxCompatible, type GfxCompatibleProps, } from './model/gfx-block-model.js';
|
|
15
15
|
export { type GfxModel, isPrimitiveModel } from './model/model.js';
|
|
16
16
|
export { convert, convertProps, derive, field, getDerivedProps, getFieldPropsSet, getLocalPropsSet, initializeObservers, initializeWatchers, local, observe, updateDerivedProps, watch, } from './model/surface/decorators/index.js';
|
|
17
|
-
export { type BaseElementProps, GfxGroupLikeElementModel, GfxPrimitiveElementModel, type SerializedElement, type ValidationException, } from './model/surface/element-model.js';
|
|
17
|
+
export { type BaseElementProps, type ForeignInterchange, type InterchangeScope, GfxGroupLikeElementModel, GfxPrimitiveElementModel, type SerializedElement, type ValidationException, } from './model/surface/element-model.js';
|
|
18
18
|
export { GfxLocalElementModel, prop, } from './model/surface/local-element-model.js';
|
|
19
19
|
export { collectPivotOccurrences, isPivotBound, type PivotBoundElement, type PivotOccurrence, } from './model/surface/pivot.js';
|
|
20
20
|
export { type EdgeDirectionDef, findRoleDef, isTypedEdgeRole, type RoleDef, type RoleDefs, type RoleId, roleIsA, type RoleKind, RoleVocabularyExtension, RoleVocabularyIdentifier, } from './model/surface/role.js';
|