@formicoidea/labre-core 0.33.0 → 0.34.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. package/dist/_pkgs/affine-block-attachment/attachment-block.js +2 -2
  2. package/dist/_pkgs/affine-block-attachment/configs/toolbar.d.ts +2 -2
  3. package/dist/_pkgs/affine-block-attachment/configs/toolbar.js +9 -7
  4. package/dist/_pkgs/affine-block-bookmark/configs/toolbar.js +12 -12
  5. package/dist/_pkgs/affine-block-code/code-block.js +2 -2
  6. package/dist/_pkgs/affine-block-code/code-toolbar/config.js +6 -0
  7. package/dist/_pkgs/affine-block-database/database-block.js +2 -2
  8. package/dist/_pkgs/affine-block-embed/common/render-linked-doc.js +3 -3
  9. package/dist/_pkgs/affine-block-embed/configs/toolbar.js +11 -11
  10. package/dist/_pkgs/affine-block-embed/embed-html-block/configs/toolbar.js +5 -5
  11. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.d.ts +7 -7
  12. package/dist/_pkgs/affine-block-embed/embed-iframe-block/configs/toolbar.js +12 -12
  13. package/dist/_pkgs/affine-block-embed/embed-iframe-block/embed-iframe-block.js +2 -2
  14. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/configs/toolbar.js +9 -9
  15. package/dist/_pkgs/affine-block-embed-doc/embed-linked-doc-block/embed-linked-doc-block.js +4 -4
  16. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/components/embed-synced-doc-card.js +5 -4
  17. package/dist/_pkgs/affine-block-embed-doc/embed-synced-doc-block/configs/toolbar.js +9 -9
  18. package/dist/_pkgs/affine-block-frame/frame-toolbar.js +2 -2
  19. package/dist/_pkgs/affine-block-image/configs/toolbar.js +4 -4
  20. package/dist/_pkgs/affine-block-note/configs/toolbar.js +2 -2
  21. package/dist/_pkgs/affine-block-root/configs/toolbar.d.ts +6 -4
  22. package/dist/_pkgs/affine-block-root/configs/toolbar.js +6 -6
  23. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.d.ts +10 -10
  24. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/misc.js +4 -4
  25. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.d.ts +7 -7
  26. package/dist/_pkgs/affine-block-root/edgeless/configs/toolbar/more.js +8 -8
  27. package/dist/_pkgs/affine-block-root/edgeless/edgeless-root-block.js +3 -1
  28. package/dist/_pkgs/affine-block-surface/extensions/interchange-import.js +15 -4
  29. package/dist/_pkgs/affine-block-surface/extensions/interchange.d.ts +17 -0
  30. package/dist/_pkgs/affine-block-surface/extensions/spotlight.d.ts +21 -0
  31. package/dist/_pkgs/affine-block-surface/extensions/spotlight.js +17 -3
  32. package/dist/_pkgs/affine-block-surface/index.d.ts +1 -0
  33. package/dist/_pkgs/affine-block-surface/index.js +1 -0
  34. package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.d.ts +17 -0
  35. package/dist/_pkgs/affine-block-surface/renderer/element-highlight-overlay.js +66 -0
  36. package/dist/_pkgs/affine-block-surface/view.js +5 -0
  37. package/dist/_pkgs/affine-block-surface-ref/configs/toolbar.js +4 -4
  38. package/dist/_pkgs/affine-components/highlight-dropdown-menu/highlight-duotone-icon.js +8 -8
  39. package/dist/_pkgs/affine-components/highlight-dropdown-menu/text-duotone-icon.js +11 -11
  40. package/dist/_pkgs/affine-components/icons/text.js +426 -426
  41. package/dist/_pkgs/affine-components/open-doc-dropdown-menu/dropdown-menu.d.ts +2 -0
  42. package/dist/_pkgs/affine-components/resource/resource.js +2 -0
  43. package/dist/_pkgs/affine-components/view-dropdown-menu/dropdown-menu.js +27 -14
  44. package/dist/_pkgs/affine-inline-link/link-node/configs/toolbar.d.ts +4 -4
  45. package/dist/_pkgs/affine-inline-link/link-node/configs/toolbar.js +6 -6
  46. package/dist/_pkgs/affine-inline-reference/reference-node/configs/toolbar.d.ts +4 -4
  47. package/dist/_pkgs/affine-inline-reference/reference-node/configs/toolbar.js +6 -6
  48. package/dist/_pkgs/affine-model/elements/bpmn/pool.d.ts +61 -0
  49. package/dist/_pkgs/affine-model/elements/bpmn/pool.js +93 -0
  50. package/dist/_pkgs/affine-model/elements/cynefin/cynefin.d.ts +7 -2
  51. package/dist/_pkgs/affine-model/elements/cynefin/cynefin.js +9 -4
  52. package/dist/_pkgs/affine-model/elements/edgy/board.d.ts +7 -2
  53. package/dist/_pkgs/affine-model/elements/edgy/board.js +9 -4
  54. package/dist/_pkgs/affine-model/elements/edgy/facets.d.ts +20 -3
  55. package/dist/_pkgs/affine-model/elements/edgy/facets.js +13 -4
  56. package/dist/_pkgs/affine-model/elements/estuarine/estuarine.d.ts +7 -2
  57. package/dist/_pkgs/affine-model/elements/estuarine/estuarine.js +9 -4
  58. package/dist/_pkgs/affine-model/elements/framework-background/background.d.ts +7 -2
  59. package/dist/_pkgs/affine-model/elements/framework-background/background.js +9 -4
  60. package/dist/_pkgs/affine-model/elements/framework-background/hit-test.d.ts +48 -0
  61. package/dist/_pkgs/affine-model/elements/framework-background/hit-test.js +55 -0
  62. package/dist/_pkgs/affine-model/elements/framework-background/index.d.ts +1 -0
  63. package/dist/_pkgs/affine-model/elements/framework-background/index.js +1 -0
  64. package/dist/_pkgs/affine-shared/services/edit-props-store.d.ts +11 -0
  65. package/dist/_pkgs/affine-shared/services/edit-props-store.js +15 -0
  66. package/dist/_pkgs/affine-shared/services/toolbar-service/action.d.ts +32 -0
  67. package/dist/_pkgs/affine-shared/services/toolbar-service/action.js +16 -0
  68. package/dist/_pkgs/affine-shared/services/toolbar-service/flags.d.ts +10 -0
  69. package/dist/_pkgs/affine-shared/services/toolbar-service/flags.js +32 -1
  70. package/dist/_pkgs/affine-shared/services/translation-service/chrome.d.ts +106 -0
  71. package/dist/_pkgs/affine-shared/services/translation-service/chrome.js +201 -0
  72. package/dist/_pkgs/affine-shared/services/translation-service/index.d.ts +1 -0
  73. package/dist/_pkgs/affine-shared/services/translation-service/index.js +1 -0
  74. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.d.ts +11 -16
  75. package/dist/_pkgs/affine-widget-edgeless-toolbar/catalogue/catalogue-groups.js +11 -28
  76. package/dist/_pkgs/affine-widget-keyboard-toolbar/config.js +2 -1
  77. package/dist/_pkgs/affine-widget-keyboard-toolbar/icons.js +98 -98
  78. package/dist/_pkgs/affine-widget-slash-menu/config.js +2 -1
  79. package/dist/_pkgs/affine-widget-toolbar/toolbar.js +5 -0
  80. package/dist/_pkgs/affine-widget-toolbar/utils.d.ts +19 -2
  81. package/dist/_pkgs/affine-widget-toolbar/utils.js +22 -7
  82. package/dist/_pkgs/data-view/core/group-by/renderer/boolean-group.js +11 -11
  83. package/dist/_pkgs/data-view/core/utils/menu-title.js +17 -17
  84. package/dist/_pkgs/data-view/view-presets/table/pc-virtual/group/top/group-title.js +18 -18
  85. package/dist/_pkgs/std/extension/command-registry.d.ts +36 -0
  86. package/dist/_pkgs/std/extension/command-registry.js +16 -0
  87. package/dist/_pkgs/std/extension/translation-manifest.d.ts +46 -1
  88. package/dist/_pkgs/std/extension/translation-manifest.js +58 -0
  89. package/dist/_pkgs/std/gfx/highlight.d.ts +84 -0
  90. package/dist/_pkgs/std/gfx/highlight.js +112 -0
  91. package/dist/_pkgs/std/gfx/index.d.ts +1 -0
  92. package/dist/_pkgs/std/gfx/index.js +1 -0
  93. package/dist/_pkgs/std/gfx/interactivity/gfx-view-event-handler.js +17 -3
  94. package/dist/_pkgs/std/scope/std-scope.d.ts +2 -1
  95. package/dist/_pkgs/std/scope/std-scope.js +2 -0
  96. package/dist/_pkgs/std/utils/gfx.js +2 -0
  97. package/dist/shortcuts.d.ts +20 -6
  98. package/dist/shortcuts.js +7 -9
  99. package/dist/translations.js +25 -18
  100. package/package.json +13 -1
@@ -2,21 +2,21 @@ import { CheckBoxCheckSolidIcon, CheckBoxUnIcon } from '@blocksuite/icons/lit';
2
2
  import { css, html } from 'lit';
3
3
  import { BaseGroup } from './base.js';
4
4
  export class BooleanGroupView extends BaseGroup {
5
- static { this.styles = css `
6
- .data-view-group-title-boolean-view {
7
- display: flex;
8
- align-items: center;
9
- }
10
- .data-view-group-title-boolean-view svg {
11
- width: 20px;
12
- height: 20px;
13
- }
5
+ static { this.styles = css `
6
+ .data-view-group-title-boolean-view {
7
+ display: flex;
8
+ align-items: center;
9
+ }
10
+ .data-view-group-title-boolean-view svg {
11
+ width: 20px;
12
+ height: 20px;
13
+ }
14
14
  `; }
15
15
  render() {
16
- return html ` <div class="data-view-group-title-boolean-view">
16
+ return html ` <div class="data-view-group-title-boolean-view">
17
17
  ${this.value
18
18
  ? CheckBoxCheckSolidIcon({ style: `color:#1E96EB` })
19
- : CheckBoxUnIcon()}
19
+ : CheckBoxUnIcon()}
20
20
  </div>`;
21
21
  }
22
22
  }
@@ -1,22 +1,22 @@
1
1
  import { ArrowLeftBigIcon } from '@blocksuite/icons/lit';
2
2
  import { html } from 'lit';
3
3
  export const menuTitle = (name, onBack) => {
4
- return html `
5
- <div
6
- style="display:flex;align-items:center;gap: 4px;padding: 3px 3px 3px 2px"
7
- >
8
- <div
9
- @click=${onBack}
10
- class="dv-icon-20 dv-hover dv-pd-2 dv-round-4"
11
- style="display:flex;"
12
- >
13
- ${ArrowLeftBigIcon()}
14
- </div>
15
- <div
16
- style="font-weight:500;font-size: 14px;line-height: 22px;color: var(--affine-text-primary-color)"
17
- >
18
- ${name}
19
- </div>
20
- </div>
4
+ return html `
5
+ <div
6
+ style="display:flex;align-items:center;gap: 4px;padding: 3px 3px 3px 2px"
7
+ >
8
+ <div
9
+ @click=${onBack}
10
+ class="dv-icon-20 dv-hover dv-pd-2 dv-round-4"
11
+ style="display:flex;"
12
+ >
13
+ ${ArrowLeftBigIcon()}
14
+ </div>
15
+ <div
16
+ style="font-weight:500;font-size: 14px;line-height: 22px;color: var(--affine-text-primary-color)"
17
+ >
18
+ ${name}
19
+ </div>
20
+ </div>
21
21
  `;
22
22
  };
@@ -19,9 +19,9 @@ export const GroupTitle = (groupData, ops) => {
19
19
  }
20
20
  const icon = groupData.value == null
21
21
  ? ''
22
- : html ` <uni-lit
23
- class="${groupHeaderIcon}"
24
- .uni="${groupData.property.icon}"
22
+ : html ` <uni-lit
23
+ class="${groupHeaderIcon}"
24
+ .uni="${groupData.property.icon}"
25
25
  ></uni-lit>`;
26
26
  const props = {
27
27
  group: groupData,
@@ -29,25 +29,25 @@ export const GroupTitle = (groupData, ops) => {
29
29
  };
30
30
  const showColumnName = groupData.property.type$.value === 'checkbox';
31
31
  const columnName = showColumnName
32
- ? html `<span class="${groupHeaderTitle}"
33
- >${groupData.property.name$.value}</span
32
+ ? html `<span class="${groupHeaderTitle}"
33
+ >${groupData.property.name$.value}</span
34
34
  >`
35
35
  : nothing;
36
36
  const opsClass = clsx(ops.groupHover && show, groupHeaderOps);
37
- return html `
38
- <div class="${groupTitleRow}">
39
- ${icon} ${renderUniLit(view, props)} ${columnName}
40
- ${GroupHeaderCount(groupData)}
41
- </div>
37
+ return html `
38
+ <div class="${groupTitleRow}">
39
+ ${icon} ${renderUniLit(view, props)} ${columnName}
40
+ ${GroupHeaderCount(groupData)}
41
+ </div>
42
42
  ${!ops.readonly
43
- ? html ` <div class="${opsClass}">
44
- <div @click="${ops.clickAdd}" class="${groupHeaderOp}">
45
- ${PlusIcon()}
46
- </div>
47
- <div @click="${ops.clickOps}" class="${groupHeaderOp}">
48
- ${MoreHorizontalIcon()}
49
- </div>
43
+ ? html ` <div class="${opsClass}">
44
+ <div @click="${ops.clickAdd}" class="${groupHeaderOp}">
45
+ ${PlusIcon()}
46
+ </div>
47
+ <div @click="${ops.clickOps}" class="${groupHeaderOp}">
48
+ ${MoreHorizontalIcon()}
49
+ </div>
50
50
  </div>`
51
- : nothing}
51
+ : nothing}
52
52
  `;
53
53
  };
@@ -325,6 +325,42 @@ export declare function runCommand(std: BlockStdScope, command: AnyCommandDescri
325
325
  * verbatim, which is what keeps hosts' persisted v0.29 override tables valid.
326
326
  */
327
327
  export declare function toShortcutDescriptor(command: AnyCommandDescriptor): ShortcutDescriptor;
328
+ /**
329
+ * The DATA-ONLY shortcut projection: what a host's Settings › Shortcuts pane
330
+ * reads to draw a row, and nothing else. No `handler`, no `run`, no `params` —
331
+ * so a module holding these entries carries no reference to the action graph
332
+ * behind the command and can be published on its own subpath (each framework
333
+ * bundle's `./commands-manifest`).
334
+ *
335
+ * It is the SIX fields the panel needs. `labelFallback` is one of them and that
336
+ * is deliberate: the projection used to drop it, which left a host with no
337
+ * catalogue rendering raw i18n keys for the rows it could not translate, and
338
+ * forced every host to re-project from the main entry to recover a wording the
339
+ * library already knew.
340
+ *
341
+ * `owner` is a {@link CommandOwner} rather than `ShortcutDescriptor`'s open
342
+ * `string`: the panel groups by owner, and the closed union is what makes that
343
+ * grouping exhaustive host-side.
344
+ */
345
+ export interface ShortcutManifestEntry {
346
+ id: string;
347
+ labelKey: string;
348
+ /** English default behind {@link labelKey}; see {@link CommandDescriptor.labelFallback}. */
349
+ labelFallback?: string;
350
+ defaultKeys: {
351
+ mac: string[];
352
+ other: string[];
353
+ };
354
+ scope: ShortcutScope;
355
+ owner: CommandOwner;
356
+ }
357
+ /**
358
+ * The third permanent projection out of {@link CommandDescriptor}, beside
359
+ * {@link toShortcutDescriptor} and {@link toCommandManifestEntry}. TOTAL for
360
+ * the same reason the shortcut descriptor is: a keyless command still yields a
361
+ * row, so Settings › Shortcuts can bind it.
362
+ */
363
+ export declare function toShortcutManifestEntry(command: AnyCommandDescriptor): ShortcutManifestEntry;
328
364
  /**
329
365
  * Project a command's zod parameter contract onto the serializable seam, so the
330
366
  * `'agent'` surface is usable end to end: without it an agent reading the
@@ -107,6 +107,22 @@ export function toShortcutDescriptor(command) {
107
107
  },
108
108
  };
109
109
  }
110
+ /**
111
+ * The third permanent projection out of {@link CommandDescriptor}, beside
112
+ * {@link toShortcutDescriptor} and {@link toCommandManifestEntry}. TOTAL for
113
+ * the same reason the shortcut descriptor is: a keyless command still yields a
114
+ * row, so Settings › Shortcuts can bind it.
115
+ */
116
+ export function toShortcutManifestEntry(command) {
117
+ return {
118
+ id: command.id,
119
+ labelKey: command.labelKey,
120
+ labelFallback: command.labelFallback,
121
+ defaultKeys: command.defaultKeys,
122
+ scope: command.scope,
123
+ owner: command.owner,
124
+ };
125
+ }
110
126
  const zodDef = (schema) => schema?._def ?? {};
111
127
  const ZOD_KIND = {
112
128
  ZodString: 'string',
@@ -12,7 +12,17 @@ import { type AnyCommandDescriptor } from './command-registry.js';
12
12
  * exactly the composition the command registry already does (see
13
13
  * `packages/affine/all/src/commands.ts` and `scripts/build-bundles.mjs`).
14
14
  */
15
- export type TranslationKeySource = 'command' | 'framework' | 'role' | 'background' | 'rule' | 'nudge' | 'profile' | 'audit-criterion' | 'reading' | 'chrome';
15
+ export type TranslationKeySource = 'command' | 'framework' | 'role' | 'background' | 'rule' | 'nudge' | 'profile' | 'audit-criterion' | 'reading'
16
+ /**
17
+ * The words a framework WRITES ONTO THE CANVAS when an artefact is placed —
18
+ * the "Task" a fresh BPMN task is captioned with, the "People" under an EDGY
19
+ * person. Document content and not chrome, which is why they are a source of
20
+ * their own: they are resolved ONCE, at placement, and what lands in the
21
+ * document is a string the author then owns and edits. Changing the
22
+ * catalogue afterwards renames nothing, exactly as renaming a shape by hand
23
+ * would not.
24
+ */
25
+ | 'seed' | 'chrome';
16
26
  export interface TranslationKeyManifestEntry {
17
27
  key: string;
18
28
  /**
@@ -41,6 +51,41 @@ export declare function collectTranslationKeys(source: TranslationKeySource, val
41
51
  * mean walking zod internals.
42
52
  */
43
53
  export declare function commandTranslationEntries(commands: readonly AnyCommandDescriptor[]): TranslationKeyManifestEntry[];
54
+ /** The i18n key prefix every artefact-catalogue group header is looked up under. */
55
+ export declare const CATALOGUE_CATEGORY_KEY_PREFIX = "com.labre.catalogue.category.";
56
+ /**
57
+ * The English default for a category header, derived from the category id.
58
+ *
59
+ * `'backgrounds'` → `'Backgrounds'`, `'value-flow'` → `'Value flow'`,
60
+ * `'boundedContext'` → `'Bounded context'`. Sentence case, not title case: a
61
+ * header is a phrase, and capitalising every word turns `'change arrows'` into
62
+ * a product name.
63
+ *
64
+ * It is a FALLBACK and nothing more. A host with a catalogue answers
65
+ * `com.labre.catalogue.category.<id>` and this is never seen; a standalone
66
+ * playground reads a word instead of a raw key. The library still invents no
67
+ * framework prose — a category id is the framework's own word, only respelled.
68
+ *
69
+ * It lives in `@labre/std` rather than beside the panel that renders it because
70
+ * a FRAMEWORK has to derive the same fallback for its own contribution to the
71
+ * key manifest (see {@link commandCategoryTranslationEntries}), and `@labre/std`
72
+ * is the only layer every framework package already depends on. The catalogue
73
+ * widget re-exports it, so the panel and the manifest cannot drift.
74
+ */
75
+ export declare function humanizeCategory(category: string): string;
76
+ /**
77
+ * The catalogue group headers a command group implies: one entry per distinct
78
+ * `category` it declares, with the panel's own humanised fallback.
79
+ *
80
+ * A framework contributes these WITH its commands, for the reason the command
81
+ * entries themselves ship with the framework: `@formicoidea/labre-core` is the
82
+ * editor minus the frameworks, so a header key derived from core's registry
83
+ * alone knows nothing about "events", "gateways" or "swimlanes" — and a bundled
84
+ * host ended up drawing translated catalogue entries under English headers
85
+ * (#183). Deriving them from the same `commands` array on both sides makes the
86
+ * monorepo assembly and the composed bundle produce the same list.
87
+ */
88
+ export declare function commandCategoryTranslationEntries(commands: readonly AnyCommandDescriptor[]): TranslationKeyManifestEntry[];
44
89
  /**
45
90
  * Compose contributions into one manifest: first occurrence of a key wins (so
46
91
  * the caller's order decides which `source` a shared key reports), sorted by
@@ -54,6 +54,64 @@ export function collectTranslationKeys(source, value) {
54
54
  export function commandTranslationEntries(commands) {
55
55
  return collectTranslationKeys('command', commands.map(toCommandManifestEntry));
56
56
  }
57
+ /** The i18n key prefix every artefact-catalogue group header is looked up under. */
58
+ export const CATALOGUE_CATEGORY_KEY_PREFIX = 'com.labre.catalogue.category.';
59
+ /**
60
+ * The English default for a category header, derived from the category id.
61
+ *
62
+ * `'backgrounds'` → `'Backgrounds'`, `'value-flow'` → `'Value flow'`,
63
+ * `'boundedContext'` → `'Bounded context'`. Sentence case, not title case: a
64
+ * header is a phrase, and capitalising every word turns `'change arrows'` into
65
+ * a product name.
66
+ *
67
+ * It is a FALLBACK and nothing more. A host with a catalogue answers
68
+ * `com.labre.catalogue.category.<id>` and this is never seen; a standalone
69
+ * playground reads a word instead of a raw key. The library still invents no
70
+ * framework prose — a category id is the framework's own word, only respelled.
71
+ *
72
+ * It lives in `@labre/std` rather than beside the panel that renders it because
73
+ * a FRAMEWORK has to derive the same fallback for its own contribution to the
74
+ * key manifest (see {@link commandCategoryTranslationEntries}), and `@labre/std`
75
+ * is the only layer every framework package already depends on. The catalogue
76
+ * widget re-exports it, so the panel and the manifest cannot drift.
77
+ */
78
+ export function humanizeCategory(category) {
79
+ const words = category
80
+ // camelCase and PascalCase boundaries, before the separators are split on:
81
+ // `'boundedContext'` has no separator to find otherwise.
82
+ .replace(/([a-z0-9])([A-Z])/g, '$1 $2')
83
+ .split(/[-_\s]+/)
84
+ .filter(Boolean)
85
+ .map(word => word.toLowerCase());
86
+ if (!words.length)
87
+ return category;
88
+ const [first, ...rest] = words;
89
+ return [first.charAt(0).toUpperCase() + first.slice(1), ...rest].join(' ');
90
+ }
91
+ /**
92
+ * The catalogue group headers a command group implies: one entry per distinct
93
+ * `category` it declares, with the panel's own humanised fallback.
94
+ *
95
+ * A framework contributes these WITH its commands, for the reason the command
96
+ * entries themselves ship with the framework: `@formicoidea/labre-core` is the
97
+ * editor minus the frameworks, so a header key derived from core's registry
98
+ * alone knows nothing about "events", "gateways" or "swimlanes" — and a bundled
99
+ * host ended up drawing translated catalogue entries under English headers
100
+ * (#183). Deriving them from the same `commands` array on both sides makes the
101
+ * monorepo assembly and the composed bundle produce the same list.
102
+ */
103
+ export function commandCategoryTranslationEntries(commands) {
104
+ const categories = new Set();
105
+ for (const command of commands) {
106
+ if (command.category)
107
+ categories.add(command.category);
108
+ }
109
+ return [...categories].map(category => ({
110
+ key: `${CATALOGUE_CATEGORY_KEY_PREFIX}${category}`,
111
+ fallback: humanizeCategory(category),
112
+ source: 'chrome',
113
+ }));
114
+ }
57
115
  /**
58
116
  * Compose contributions into one manifest: first occurrence of a key wins (so
59
117
  * the caller's order decides which `source` a shared key reports), sorted by
@@ -0,0 +1,84 @@
1
+ import { Signal } from '@preact/signals-core';
2
+ import type { GfxController } from './controller.js';
3
+ import { GfxExtension } from './extension.js';
4
+ import type { GfxModel } from './model/model.js';
5
+ /**
6
+ * How long a highlight stays visible when no `duration` is given.
7
+ */
8
+ export declare const DEFAULT_HIGHLIGHT_DURATION = 2000;
9
+ /**
10
+ * Viewport padding used when `reframe` is requested without an explicit one.
11
+ * Expressed in percentage of the viewport (see `Viewport.setViewportByBound`).
12
+ */
13
+ export declare const DEFAULT_HIGHLIGHT_PADDING: [number, number, number, number];
14
+ export interface HighlightElementsOptions {
15
+ /**
16
+ * Reframe the viewport on the union of the target bounds before emphasizing
17
+ * them. Defaults to `false`, the highlight is then purely visual.
18
+ */
19
+ reframe?: boolean;
20
+ /**
21
+ * How long the emphasis stays visible, in milliseconds.
22
+ * Defaults to {@link DEFAULT_HIGHLIGHT_DURATION}.
23
+ * Pass `0` to keep it until the next `highlightElements` or `clear` call.
24
+ */
25
+ duration?: number;
26
+ /**
27
+ * Padding applied by the reframe, only used when `reframe` is `true`.
28
+ * Defaults to {@link DEFAULT_HIGHLIGHT_PADDING}.
29
+ */
30
+ padding?: [number, number, number, number];
31
+ /**
32
+ * Animate the reframe, only used when `reframe` is `true`. Defaults to `true`.
33
+ */
34
+ smooth?: boolean;
35
+ }
36
+ /**
37
+ * Transient, read-only emphasis on a set of graphic elements.
38
+ *
39
+ * The manager owns the state only: it never writes to the store and never
40
+ * touches the selection model, so it is safe on a read-only or non-interactive
41
+ * editor (an embedded preview window, for instance). The actual ring is drawn
42
+ * by a renderer subscribing to {@link ElementHighlightManager.highlighted$},
43
+ * which keeps this extension free of any rendering concern.
44
+ */
45
+ export declare class ElementHighlightManager extends GfxExtension {
46
+ static key: string;
47
+ private _timer;
48
+ /**
49
+ * Ids of the elements currently emphasized. Empty when nothing is
50
+ * highlighted. Only ids that resolved to an actual graphic element at call
51
+ * time are kept.
52
+ */
53
+ readonly highlighted$: Signal<readonly string[]>;
54
+ /**
55
+ * The models behind {@link highlighted$}, resolved on read so that elements
56
+ * deleted meanwhile simply drop out of the highlight.
57
+ */
58
+ get highlightedElements(): GfxModel[];
59
+ static extendGfx(gfx: GfxController): void;
60
+ private _clearTimer;
61
+ /**
62
+ * Drop the current emphasis immediately.
63
+ */
64
+ clear(): void;
65
+ /**
66
+ * Emphasize the given elements for a while.
67
+ *
68
+ * Unknown ids and non-graphic blocks are ignored; if none of the ids
69
+ * resolves, any running highlight is cleared and nothing else happens.
70
+ * Calling it again replaces the previous highlight and restarts the timer.
71
+ */
72
+ highlightElements(ids: string[], opts?: HighlightElementsOptions): void;
73
+ unmounted(): void;
74
+ }
75
+ declare module './controller.js' {
76
+ interface GfxController {
77
+ readonly highlight: ElementHighlightManager;
78
+ /**
79
+ * Transiently emphasize a set of elements without mutating the document
80
+ * nor the selection. Shorthand for `gfx.highlight.highlightElements`.
81
+ */
82
+ highlightElements(ids: string[], opts?: HighlightElementsOptions): void;
83
+ }
84
+ }
@@ -0,0 +1,112 @@
1
+ import { getCommonBoundWithRotation } from '../../global/gfx/index.js';
2
+ import { Signal } from '@preact/signals-core';
3
+ import { GfxExtension, GfxExtensionIdentifier } from './extension.js';
4
+ import { GfxBlockElementModel } from './model/gfx-block-model.js';
5
+ import { GfxPrimitiveElementModel } from './model/surface/element-model.js';
6
+ /**
7
+ * How long a highlight stays visible when no `duration` is given.
8
+ */
9
+ export const DEFAULT_HIGHLIGHT_DURATION = 2000;
10
+ /**
11
+ * Viewport padding used when `reframe` is requested without an explicit one.
12
+ * Expressed in percentage of the viewport (see `Viewport.setViewportByBound`).
13
+ */
14
+ export const DEFAULT_HIGHLIGHT_PADDING = [
15
+ 0.15, 0.15, 0.15, 0.15,
16
+ ];
17
+ /**
18
+ * Only models that live on the canvas can carry an emphasis ring.
19
+ * Anything else queried by id (plain blocks, unknown ids) is ignored.
20
+ */
21
+ function isRenderableGfxModel(model) {
22
+ return (model instanceof GfxPrimitiveElementModel ||
23
+ model instanceof GfxBlockElementModel);
24
+ }
25
+ /**
26
+ * Transient, read-only emphasis on a set of graphic elements.
27
+ *
28
+ * The manager owns the state only: it never writes to the store and never
29
+ * touches the selection model, so it is safe on a read-only or non-interactive
30
+ * editor (an embedded preview window, for instance). The actual ring is drawn
31
+ * by a renderer subscribing to {@link ElementHighlightManager.highlighted$},
32
+ * which keeps this extension free of any rendering concern.
33
+ */
34
+ export class ElementHighlightManager extends GfxExtension {
35
+ constructor() {
36
+ super(...arguments);
37
+ this._timer = null;
38
+ /**
39
+ * Ids of the elements currently emphasized. Empty when nothing is
40
+ * highlighted. Only ids that resolved to an actual graphic element at call
41
+ * time are kept.
42
+ */
43
+ this.highlighted$ = new Signal([]);
44
+ }
45
+ static { this.key = 'elementHighlight'; }
46
+ /**
47
+ * The models behind {@link highlighted$}, resolved on read so that elements
48
+ * deleted meanwhile simply drop out of the highlight.
49
+ */
50
+ get highlightedElements() {
51
+ return this.highlighted$.value
52
+ .map(id => this.gfx.getElementById(id))
53
+ .filter(isRenderableGfxModel);
54
+ }
55
+ static extendGfx(gfx) {
56
+ Object.defineProperty(gfx, 'highlight', {
57
+ get() {
58
+ return this.std.get(GfxExtensionIdentifier('elementHighlight'));
59
+ },
60
+ });
61
+ Object.defineProperty(gfx, 'highlightElements', {
62
+ value: (ids, opts) => gfx.highlight.highlightElements(ids, opts),
63
+ });
64
+ }
65
+ _clearTimer() {
66
+ if (this._timer !== null) {
67
+ clearTimeout(this._timer);
68
+ this._timer = null;
69
+ }
70
+ }
71
+ /**
72
+ * Drop the current emphasis immediately.
73
+ */
74
+ clear() {
75
+ this._clearTimer();
76
+ if (this.highlighted$.peek().length === 0)
77
+ return;
78
+ this.highlighted$.value = [];
79
+ }
80
+ /**
81
+ * Emphasize the given elements for a while.
82
+ *
83
+ * Unknown ids and non-graphic blocks are ignored; if none of the ids
84
+ * resolves, any running highlight is cleared and nothing else happens.
85
+ * Calling it again replaces the previous highlight and restarts the timer.
86
+ */
87
+ highlightElements(ids, opts = {}) {
88
+ this._clearTimer();
89
+ const elements = ids
90
+ .map(id => this.gfx.getElementById(id))
91
+ .filter(isRenderableGfxModel);
92
+ if (elements.length === 0) {
93
+ this.highlighted$.value = [];
94
+ return;
95
+ }
96
+ if (opts.reframe) {
97
+ this.gfx.viewport.setViewportByBound(getCommonBoundWithRotation(elements), opts.padding ?? DEFAULT_HIGHLIGHT_PADDING, opts.smooth ?? true);
98
+ }
99
+ this.highlighted$.value = elements.map(element => element.id);
100
+ const duration = opts.duration ?? DEFAULT_HIGHLIGHT_DURATION;
101
+ if (duration > 0) {
102
+ this._timer = setTimeout(() => {
103
+ this._timer = null;
104
+ this.clear();
105
+ }, duration);
106
+ }
107
+ }
108
+ unmounted() {
109
+ this._clearTimer();
110
+ this.highlighted$.value = [];
111
+ }
112
+ }
@@ -5,6 +5,7 @@ export { GfxController } from './controller.js';
5
5
  export type { CursorType, StandardCursor } from './cursor.js';
6
6
  export { GfxExtension, GfxExtensionIdentifier } from './extension.js';
7
7
  export { GridManager } from './grid.js';
8
+ export { DEFAULT_HIGHLIGHT_DURATION, DEFAULT_HIGHLIGHT_PADDING, ElementHighlightManager, type HighlightElementsOptions, } from './highlight.js';
8
9
  export { GfxControllerIdentifier } from './identifiers.js';
9
10
  export type { BoxSelectionContext, DragEndContext, DragExtensionInitializeContext, DragInitializationOption, DragMoveContext, DragStartContext, ExtensionDragEndContext, ExtensionDragMoveContext, ExtensionDragStartContext, GfxInteractivityContext, GfxViewInteractionConfig, ResizeConstraint, ResizeEndContext, ResizeHandle, ResizeMoveContext, ResizeStartContext, RotateConstraint, RotateEndContext, RotateMoveContext, RotateStartContext, SelectContext, } from './interactivity/index.js';
10
11
  export { GfxViewEventManager, GfxViewInteractionExtension, InteractivityExtension, InteractivityIdentifier, InteractivityManager, } from './interactivity/index.js';
@@ -4,6 +4,7 @@ export { batchAddChildren, batchRemoveChildren, canSafeAddToContainer, descendan
4
4
  export { GfxController } from './controller.js';
5
5
  export { GfxExtension, GfxExtensionIdentifier } from './extension.js';
6
6
  export { GridManager } from './grid.js';
7
+ export { DEFAULT_HIGHLIGHT_DURATION, DEFAULT_HIGHLIGHT_PADDING, ElementHighlightManager, } from './highlight.js';
7
8
  export { GfxControllerIdentifier } from './identifiers.js';
8
9
  export { GfxViewEventManager, GfxViewInteractionExtension, InteractivityExtension, InteractivityIdentifier, InteractivityManager, } from './interactivity/index.js';
9
10
  export { LayerManager } from './layer.js';
@@ -51,15 +51,29 @@ export class GfxViewEventManager {
51
51
  filter: ['canvas', 'local'],
52
52
  })
53
53
  .reduce((pre, model) => {
54
- if (model.includesPoint(x, y, {
54
+ const view = this.gfx.view.get(model);
55
+ if (!view)
56
+ return pre;
57
+ // The VIEW's hit test, not the model's.
58
+ //
59
+ // `GfxElementModelView.includesPoint` delegates to the model by
60
+ // default, so for every view that does not override it this is the
61
+ // same answer as before. It is the seam a framework needs when the
62
+ // area that SELECTS an element and the area that its own gestures
63
+ // answer on are not the same rectangle: a framework background is
64
+ // picked by its border alone (issue #194), yet its editable labels
65
+ // must keep receiving the double-click that renames them, and a BPMN
66
+ // pool's lane separators must keep receiving the drag that moves them.
67
+ // Those zones are drawn by the framework's view, so that is where they
68
+ // are declared — the model layer stays free of geometry it cannot see.
69
+ if (view.includesPoint(x, y, {
55
70
  hitThreshold: 10,
56
71
  responsePadding: [5, 5],
57
72
  }, this.gfx.std.host) ||
58
73
  ('externalBound' in model
59
74
  ? model.externalBound?.isPointInBound([x, y])
60
75
  : false)) {
61
- const view = this.gfx.view.get(model);
62
- view && pre.push(view);
76
+ pre.push(view);
63
77
  }
64
78
  return pre;
65
79
  }, [])
@@ -5,6 +5,7 @@ import { CommandManager } from '../command/index.js';
5
5
  import { UIEventDispatcher } from '../event/index.js';
6
6
  import { DndController } from '../extension/dnd/index.js';
7
7
  import { ServiceManager } from '../extension/service-manager.js';
8
+ import { ElementHighlightManager } from '../gfx/highlight.js';
8
9
  import { GridManager, LayerManager } from '../gfx/index.js';
9
10
  import { GfxSelectionManager } from '../gfx/selection.js';
10
11
  import { SurfaceMiddlewareExtension } from '../gfx/surface-middleware.js';
@@ -17,7 +18,7 @@ export interface BlockStdOptions {
17
18
  extensions: ExtensionType[];
18
19
  }
19
20
  export declare class BlockStdScope {
20
- static internalExtensions: (typeof ViewStore | typeof UIEventDispatcher | typeof RangeManager | typeof CommandManager | typeof GridManager | typeof LayerManager | typeof ServiceManager | typeof GfxSelectionManager | typeof SurfaceMiddlewareExtension | typeof ViewManager)[];
21
+ static internalExtensions: (typeof ViewStore | typeof UIEventDispatcher | typeof RangeManager | typeof CommandManager | typeof GridManager | typeof LayerManager | typeof ServiceManager | typeof ElementHighlightManager | typeof GfxSelectionManager | typeof SurfaceMiddlewareExtension | typeof ViewManager)[];
21
22
  readonly container: Container;
22
23
  readonly store: Store;
23
24
  readonly provider: ServiceProvider;
@@ -8,6 +8,7 @@ import { DndController } from '../extension/dnd/index.js';
8
8
  import { EditorLifeCycleExtension } from '../extension/editor-life-cycle.js';
9
9
  import { ServiceManager } from '../extension/service-manager.js';
10
10
  import { GfxController } from '../gfx/controller.js';
11
+ import { ElementHighlightManager } from '../gfx/highlight.js';
11
12
  import { GridManager, LayerManager } from '../gfx/index.js';
12
13
  import { GfxSelectionManager } from '../gfx/selection.js';
13
14
  import { SurfaceMiddlewareExtension } from '../gfx/surface-middleware.js';
@@ -25,6 +26,7 @@ const internalExtensions = [
25
26
  Clipboard,
26
27
  GfxController,
27
28
  GfxSelectionManager,
29
+ ElementHighlightManager,
28
30
  GridManager,
29
31
  LayerManager,
30
32
  SurfaceMiddlewareExtension,
@@ -1,5 +1,7 @@
1
1
  import { effect } from '@preact/signals-core';
2
2
  import { SurfaceBlockModel } from '../gfx/model/surface/surface-model.js';
3
+ // The explicit return type keeps declaration emit off signals-core's
4
+ // `DisposeFn`, whose `[Symbol.dispose]` member tsc cannot serialize.
3
5
  export function onSurfaceAdded(doc, callback) {
4
6
  let found = false;
5
7
  let foundId = '';
@@ -1,24 +1,38 @@
1
- import type { ShortcutDescriptor } from './_pkgs/std/index.js';
1
+ import { type ShortcutManifestEntry } from './_pkgs/std/index.js';
2
2
  import type { LabreFlags } from './flags.js';
3
3
  /**
4
4
  * Manifest view of a shortcut: the metadata a host "Shortcuts" settings panel
5
5
  * needs, without the runtime `handler`.
6
6
  *
7
- * Since PF3 it is a projection of a projection: {@link CommandDescriptor} →
8
- * {@link ShortcutDescriptor} → here. It KEEPS representing shortcuts and
9
- * nothing else — no `iconKey`, no `category`; catalogue metadata travels on
10
- * `CommandManifestEntry` (`docs/adr/0008` § Icons).
7
+ * Since PF3 it is a projection of `CommandDescriptor`, and it is now declared
8
+ * ONCE, in `@labre/std` beside the projection that builds it — because the
9
+ * framework bundles publish the very same rows on their own
10
+ * `./commands-manifest` subpath, and a host composing core's manifest with a
11
+ * framework's must be handed one type, not two.
12
+ *
13
+ * It KEEPS representing shortcuts and nothing else — no `iconKey`, no
14
+ * `category`; catalogue metadata travels on `CommandManifestEntry`
15
+ * (`docs/adr/0008` § Icons). `labelFallback` is the one addition, and it is not
16
+ * catalogue metadata: it is the wording the panel SHOWS when the host has no
17
+ * translation for `labelKey`. Dropping it was why a host with no catalogue
18
+ * rendered raw keys, and why every host had to re-project from the main entry
19
+ * to recover a wording the library already knew.
11
20
  *
12
21
  * Breaking shape change for hosts: `when?: string` is gone (no descriptor in
13
22
  * the repo ever set it), `defaultKeys` is always present, and `owner` narrows
14
23
  * from `string` to `CommandOwner`.
15
24
  */
16
- export type ShortcutManifestEntry = Omit<ShortcutDescriptor, 'handler'>;
25
+ export type { ShortcutManifestEntry };
17
26
  /**
18
27
  * The enumerable shortcut manifest for a given flag set. TOTAL over the command
19
28
  * registry: every command yields a row, keyless ones included, so Settings ›
20
29
  * Shortcuts can bind precisely the commands a user most wants to bind. The
21
30
  * panel therefore grows from ~10 rows to the full command count — that is the
22
31
  * intent, and `owner` grouping is what keeps it usable.
32
+ *
33
+ * In the BUNDLED distribution this returns core's share only: each framework
34
+ * bundle's commands are stripped out of core by `scripts/build-bundles.mjs` and
35
+ * republished as that bundle's data-only `./commands-manifest` export, which
36
+ * the host concatenates for the frameworks it enables.
23
37
  */
24
38
  export declare function getShortcutManifest(flags?: LabreFlags): ShortcutManifestEntry[];