@formicoidea/labre-framework-wardley 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (46) hide show
  1. package/dist/actions.d.ts +14 -18
  2. package/dist/actions.js +79 -38
  3. package/dist/audit-criteria.d.ts +31 -0
  4. package/dist/audit-criteria.js +90 -0
  5. package/dist/background.d.ts +14 -0
  6. package/dist/background.js +338 -0
  7. package/dist/commands.d.ts +5 -0
  8. package/dist/commands.js +190 -0
  9. package/dist/consts.d.ts +10 -4
  10. package/dist/consts.js +10 -9
  11. package/dist/descriptor.d.ts +8 -3
  12. package/dist/descriptor.js +6 -3
  13. package/dist/element-renderer.d.ts +8 -5
  14. package/dist/element-renderer.js +12 -147
  15. package/dist/element-view.d.ts +8 -4
  16. package/dist/element-view.js +30 -22
  17. package/dist/gradient.d.ts +6 -11
  18. package/dist/gradient.js +58 -47
  19. package/dist/index.d.ts +9 -1
  20. package/dist/index.js +9 -1
  21. package/dist/legend.js +8 -0
  22. package/dist/natures.d.ts +50 -0
  23. package/dist/natures.js +93 -0
  24. package/dist/nudges.d.ts +41 -0
  25. package/dist/nudges.js +69 -0
  26. package/dist/profiles.d.ts +2 -0
  27. package/dist/profiles.js +87 -0
  28. package/dist/reading.d.ts +3 -0
  29. package/dist/reading.js +129 -0
  30. package/dist/roles.d.ts +50 -0
  31. package/dist/roles.js +132 -0
  32. package/dist/rules.d.ts +2 -0
  33. package/dist/rules.js +270 -0
  34. package/dist/templates/index.js +51 -7
  35. package/dist/templates/maps.js +121 -17
  36. package/dist/toolbar/wardley-menu.d.ts +8 -15
  37. package/dist/toolbar/wardley-menu.js +8 -136
  38. package/dist/translations.d.ts +16 -0
  39. package/dist/translations.js +24 -0
  40. package/dist/view.d.ts +17 -0
  41. package/dist/view.js +116 -13
  42. package/package.json +2 -2
  43. package/dist/label-layout.d.ts +0 -20
  44. package/dist/label-layout.js +0 -72
  45. package/dist/shortcuts.d.ts +0 -2
  46. package/dist/shortcuts.js +0 -37
@@ -1,144 +1,16 @@
1
1
  import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
2
- import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
3
- import { css, html, LitElement } from 'lit';
4
- import { activateWardleyConnector, createWardleyBackground, createWardleyInertia, createWardleyMarket, createWardleyNode, createWardleyPipeline, } from '../actions.js';
5
- import { wardleyAnchorIcon, wardleyArrowIcon, wardleyBackgroundIcon, wardleyBenefitIcon, wardleyComponentIcon, wardleyEcosystemIcon, wardleyInertiaIcon, wardleyLinkIcon, wardleyMarketIcon, wardleyMethodIcon, wardleyEvolutionGradientIcon, wardleyOpportunityIcon, wardleyPipelineIcon, } from './icons.js';
2
+ import { EdgelessCommandMenu } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
6
3
  /**
7
- * The popover that opens above the toolbar for the Wardley toolbox. Each item
8
- * creates a pre-formatted Wardley object (see `../actions.ts` — the same
9
- * actions back the wardley keyboard shortcuts).
4
+ * The popover that opens above the toolbar for the Wardley toolbox.
5
+ *
6
+ * Since PF3 it holds no artefact list of its own: {@link EdgelessCommandMenu}
7
+ * renders whatever `wardleyCommands` declares for the `senior-menu` surface, so
8
+ * the menu and the shortcut manifest can no longer drift (they did: 13 vs 7).
10
9
  */
11
- export class EdgelessWardleyMenu extends EdgelessToolbarToolMixin(LitElement) {
10
+ export class EdgelessWardleyMenu extends EdgelessCommandMenu {
12
11
  constructor() {
13
12
  super(...arguments);
13
+ this.owner = 'wardley';
14
14
  this.type = EmptyTool;
15
- this._createInertia = () => {
16
- createWardleyInertia(this.gfx);
17
- };
18
- this._createPipeline = () => {
19
- createWardleyPipeline(this.gfx);
20
- };
21
- this._createMarket = () => {
22
- createWardleyMarket(this.gfx);
23
- };
24
- }
25
- static { this.styles = css `
26
- :host {
27
- display: flex;
28
- z-index: -1;
29
- justify-content: center;
30
- }
31
- .menu-content {
32
- display: flex;
33
- align-items: center;
34
- justify-content: center;
35
- }
36
- .button-group-container {
37
- display: flex;
38
- align-items: center;
39
- gap: 14px;
40
- fill: var(--affine-icon-color);
41
- }
42
- .button-group-container svg {
43
- width: 24px;
44
- height: 24px;
45
- }
46
- `; }
47
- _createBackground(variant = 'classic') {
48
- createWardleyBackground(this.gfx, variant);
49
- }
50
- _createNode(kind) {
51
- createWardleyNode(this.gfx, kind);
52
- }
53
- _activateConnector(kind) {
54
- activateWardleyConnector(this.gfx, kind);
55
- }
56
- render() {
57
- return html `
58
- <edgeless-slide-menu>
59
- <div class="menu-content">
60
- <div class="button-group-container">
61
- <edgeless-tool-icon-button
62
- .tooltip=${'Wardley map background'}
63
- @click=${() => this._createBackground('classic')}
64
- >
65
- ${wardleyBackgroundIcon}
66
- </edgeless-tool-icon-button>
67
- <edgeless-tool-icon-button
68
- .tooltip=${'Opportunity background (gradient)'}
69
- @click=${() => this._createBackground('opportunity')}
70
- >
71
- ${wardleyOpportunityIcon}
72
- </edgeless-tool-icon-button>
73
- <edgeless-tool-icon-button
74
- .tooltip=${'Benefit / Investment background (gradient)'}
75
- @click=${() => this._createBackground('benefit')}
76
- >
77
- ${wardleyBenefitIcon}
78
- </edgeless-tool-icon-button>
79
- <edgeless-tool-icon-button
80
- .tooltip=${'Evolution background (Wardley presentation)'}
81
- @click=${() => this._createBackground('evolution-gradient')}
82
- >
83
- ${wardleyEvolutionGradientIcon}
84
- </edgeless-tool-icon-button>
85
- <edgeless-tool-icon-button
86
- .tooltip=${'Component'}
87
- @click=${() => this._createNode('component')}
88
- >
89
- ${wardleyComponentIcon}
90
- </edgeless-tool-icon-button>
91
- <edgeless-tool-icon-button
92
- .tooltip=${'Component + method'}
93
- @click=${() => this._createNode('method')}
94
- >
95
- ${wardleyMethodIcon}
96
- </edgeless-tool-icon-button>
97
- <edgeless-tool-icon-button
98
- .tooltip=${'Market'}
99
- @click=${this._createMarket}
100
- >
101
- ${wardleyMarketIcon}
102
- </edgeless-tool-icon-button>
103
- <edgeless-tool-icon-button
104
- .tooltip=${'Ecosystem'}
105
- @click=${() => this._createNode('ecosystem')}
106
- >
107
- ${wardleyEcosystemIcon}
108
- </edgeless-tool-icon-button>
109
- <edgeless-tool-icon-button
110
- .tooltip=${'Anchor'}
111
- @click=${() => this._createNode('anchor')}
112
- >
113
- ${wardleyAnchorIcon}
114
- </edgeless-tool-icon-button>
115
- <edgeless-tool-icon-button
116
- .tooltip=${'Pipeline'}
117
- @click=${this._createPipeline}
118
- >
119
- ${wardleyPipelineIcon}
120
- </edgeless-tool-icon-button>
121
- <edgeless-tool-icon-button
122
- .tooltip=${'Link'}
123
- @click=${() => this._activateConnector('link')}
124
- >
125
- ${wardleyLinkIcon}
126
- </edgeless-tool-icon-button>
127
- <edgeless-tool-icon-button
128
- .tooltip=${'Arrow (evolution)'}
129
- @click=${() => this._activateConnector('arrow')}
130
- >
131
- ${wardleyArrowIcon}
132
- </edgeless-tool-icon-button>
133
- <edgeless-tool-icon-button
134
- .tooltip=${'Inertia'}
135
- @click=${this._createInertia}
136
- >
137
- ${wardleyInertiaIcon}
138
- </edgeless-tool-icon-button>
139
- </div>
140
- </div>
141
- </edgeless-slide-menu>
142
- `;
143
15
  }
144
16
  }
@@ -0,0 +1,16 @@
1
+ import { type TranslationKeyManifestEntry } from '@formicoidea/labre-core/std';
2
+ /**
3
+ * THIS framework's contribution to the translation-key manifest — every
4
+ * `com.labre.*` key Wardley can hand to `TranslationProvider.t`, derived from
5
+ * the very declarations the editor registers.
6
+ *
7
+ * It lives HERE, not in `@labre/affine/translations`, because the bundled
8
+ * distribution splits the library along exactly this line:
9
+ * `@formicoidea/labre-core` is the editor MINUS the frameworks, and a host that
10
+ * installs `@formicoidea/labre-framework-wardley` composes this export into the
11
+ * catalogue it builds — the same sentence that already holds for
12
+ * `wardleyCommands`. A manifest that named Wardley from the core side would be
13
+ * complete in the monorepo and silently 61 % short (107 keys of 175) in the
14
+ * distribution hosts actually consume.
15
+ */
16
+ export declare const wardleyTranslationEntries: TranslationKeyManifestEntry[];
@@ -0,0 +1,24 @@
1
+ import { collectTranslationKeys, commandTranslationEntries, mergeTranslationEntries, } from '@formicoidea/labre-core/std';
2
+ import { WARDLEY_AUDIT_CRITERIA } from './audit-criteria.js';
3
+ import { WARDLEY_BACKGROUND } from './background.js';
4
+ import { wardleyCommands } from './commands.js';
5
+ import { WARDLEY_NUDGES } from './nudges.js';
6
+ import { WARDLEY_PROFILES } from './profiles.js';
7
+ import { WARDLEY_READING } from './reading.js';
8
+ import { WARDLEY_ROLES } from './roles.js';
9
+ import { WARDLEY_RULES } from './rules.js';
10
+ /**
11
+ * THIS framework's contribution to the translation-key manifest — every
12
+ * `com.labre.*` key Wardley can hand to `TranslationProvider.t`, derived from
13
+ * the very declarations the editor registers.
14
+ *
15
+ * It lives HERE, not in `@labre/affine/translations`, because the bundled
16
+ * distribution splits the library along exactly this line:
17
+ * `@formicoidea/labre-core` is the editor MINUS the frameworks, and a host that
18
+ * installs `@formicoidea/labre-framework-wardley` composes this export into the
19
+ * catalogue it builds — the same sentence that already holds for
20
+ * `wardleyCommands`. A manifest that named Wardley from the core side would be
21
+ * complete in the monorepo and silently 61 % short (107 keys of 175) in the
22
+ * distribution hosts actually consume.
23
+ */
24
+ export const wardleyTranslationEntries = mergeTranslationEntries(commandTranslationEntries(wardleyCommands), collectTranslationKeys('role', WARDLEY_ROLES), collectTranslationKeys('background', WARDLEY_BACKGROUND), collectTranslationKeys('rule', WARDLEY_RULES), collectTranslationKeys('nudge', WARDLEY_NUDGES), collectTranslationKeys('profile', WARDLEY_PROFILES), collectTranslationKeys('audit-criterion', WARDLEY_AUDIT_CRITERIA), collectTranslationKeys('reading', WARDLEY_READING));
package/dist/view.d.ts CHANGED
@@ -1,4 +1,21 @@
1
1
  import { type ViewExtensionContext, ViewExtensionProvider } from '@formicoidea/labre-core/ext-loader';
2
+ /**
3
+ * Wardley rendering — ALWAYS registered, independent of any flag. Disabling
4
+ * `wardley` hides only the creation tooling (see {@link WardleyViewExtension});
5
+ * maps already drawn must still paint, stay selectable, stay editable and keep
6
+ * their contextual toolbar. See `docs/adr/0009`.
7
+ */
8
+ export declare class WardleyRenderViewExtension extends ViewExtensionProvider {
9
+ name: string;
10
+ setup(context: ViewExtensionContext): void;
11
+ }
12
+ /**
13
+ * Wardley creation tooling — flag-gated (`wardley`): the senior toolbar button,
14
+ * its templates category, the edgeless chords (w+c, w+l, ...) and the
15
+ * validation rules and profiles. Both are tooling: a map drawn while the flag
16
+ * was on keeps rendering when it goes off, it just stops being checked — and
17
+ * the profile it was put on stays written, unread, until the flag comes back.
18
+ */
2
19
  export declare class WardleyViewExtension extends ViewExtensionProvider {
3
20
  name: string;
4
21
  effect(): void;
package/dist/view.js CHANGED
@@ -1,8 +1,18 @@
1
+ import { ReadingProfileExtension, tagsToolbarConfig, validationToolbarConfig, QualityNudgeExtension, ValidationProfileExtension, ValidationRuleExtension, } from '@formicoidea/labre-core/blocks/surface';
2
+ import { AuditCriterionExtension, ToolbarModuleExtension, UniverseTagDefsExtension, } from '@formicoidea/labre-core/shared/services';
1
3
  import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
2
4
  import { extendTemplateCategory } from '@formicoidea/labre-core/gfx/template';
3
- import { ShortcutExtension } from '@formicoidea/labre-core/std';
5
+ import { BlockFlavourIdentifier, CommandExtension } from '@formicoidea/labre-core/std';
6
+ import { RoleVocabularyExtension } from '@formicoidea/labre-core/std/gfx';
7
+ import { WARDLEY_AUDIT_CRITERIA } from './audit-criteria.js';
8
+ import { wardleyCommandIcons, wardleyCommands } from './commands.js';
4
9
  import { effects } from './effects.js';
5
- import { wardleyShortcuts } from './shortcuts.js';
10
+ import { WARDLEY_TAG_DEFS } from './natures.js';
11
+ import { WARDLEY_PROFILES } from './profiles.js';
12
+ import { WARDLEY_NUDGES } from './nudges.js';
13
+ import { WARDLEY_READING } from './reading.js';
14
+ import { WARDLEY_ROLES } from './roles.js';
15
+ import { WARDLEY_RULES } from './rules.js';
6
16
  import { wardleyTemplateCategory } from './templates/index.js';
7
17
  import { WardleyElementRendererExtension } from './element-renderer.js';
8
18
  import { WardleyInteraction, WardleyView } from './element-view.js';
@@ -11,6 +21,44 @@ import { WardleyNodeView } from './node/node-view.js';
11
21
  import { wardleyNodeToolbarExtension } from './toolbar/node-config.js';
12
22
  import { wardleyToolbarExtension } from './toolbar/config.js';
13
23
  import { wardleySeniorTool } from './toolbar/senior-tool.js';
24
+ /**
25
+ * Wardley rendering — ALWAYS registered, independent of any flag. Disabling
26
+ * `wardley` hides only the creation tooling (see {@link WardleyViewExtension});
27
+ * maps already drawn must still paint, stay selectable, stay editable and keep
28
+ * their contextual toolbar. See `docs/adr/0009`.
29
+ */
30
+ export class WardleyRenderViewExtension extends ViewExtensionProvider {
31
+ constructor() {
32
+ super(...arguments);
33
+ this.name = 'affine-wardley-render-gfx';
34
+ }
35
+ setup(context) {
36
+ super.setup(context);
37
+ context.register(WardleyView);
38
+ context.register(WardleyElementRendererExtension);
39
+ context.register(WardleyNodeView);
40
+ context.register(WardleyNodeRendererExtension);
41
+ // The role VOCABULARY, always on. A role is written in the document, not in
42
+ // the tooling: the direction reveal of a typed edge, the inversion command
43
+ // and the toolbar entry that must not lie about one all read this, and they
44
+ // have to keep working on a map drawn while the flag was on and opened
45
+ // while it is off (`docs/adr/0009`, `docs/adr/0010`). The rules that
46
+ // JUDGE those roles stay in the flag-gated extension below.
47
+ context.register(RoleVocabularyExtension(WARDLEY_ROLES));
48
+ if (this.isEdgeless(context.scope)) {
49
+ context.register(WardleyInteraction);
50
+ context.register(wardleyToolbarExtension);
51
+ context.register(wardleyNodeToolbarExtension);
52
+ }
53
+ }
54
+ }
55
+ /**
56
+ * Wardley creation tooling — flag-gated (`wardley`): the senior toolbar button,
57
+ * its templates category, the edgeless chords (w+c, w+l, ...) and the
58
+ * validation rules and profiles. Both are tooling: a map drawn while the flag
59
+ * was on keeps rendering when it goes off, it just stops being checked — and
60
+ * the profile it was put on stays written, unread, until the flag comes back.
61
+ */
14
62
  export class WardleyViewExtension extends ViewExtensionProvider {
15
63
  constructor() {
16
64
  super(...arguments);
@@ -18,24 +66,79 @@ export class WardleyViewExtension extends ViewExtensionProvider {
18
66
  }
19
67
  effect() {
20
68
  super.effect();
69
+ // Defines the senior button and its menu — tooling-only custom elements.
21
70
  effects();
22
71
  extendTemplateCategory(wardleyTemplateCategory);
23
72
  }
24
73
  setup(context) {
25
74
  super.setup(context);
26
- context.register(WardleyView);
27
- context.register(WardleyElementRendererExtension);
28
- context.register(WardleyNodeView);
29
- context.register(WardleyNodeRendererExtension);
30
75
  if (this.isEdgeless(context.scope)) {
31
- context.register(WardleyInteraction);
76
+ context.register(ValidationRuleExtension(WARDLEY_RULES));
77
+ context.register(ValidationProfileExtension(WARDLEY_PROFILES));
78
+ // Map quality (PF13.9): the four nudges the tool cannot judge, and
79
+ // nothing else — the on-demand check-up Wardley used to expose beside
80
+ // them was dropped on the PO recette of 02/08/2026. The engine keeps the
81
+ // capability (PF5.14 and its two families), Wardley simply declares no
82
+ // rule that uses it.
83
+ context.register(QualityNudgeExtension(WARDLEY_NUDGES));
84
+ // A1–A3, the level-3 criteria (PF14.1). Registered HERE, beside the rules
85
+ // and for the same reason: a criterion is tooling, so Wardley switched
86
+ // off contributes none and `map.audit` finds nothing to ask about — while
87
+ // `ai-audit` independently decides whether the command exists at all.
88
+ context.register(AuditCriterionExtension(WARDLEY_AUDIT_CRITERIA));
89
+ // The Validation dropdown on a selected map's contextual toolbar. A
90
+ // SECOND module on the same element, through the `custom:` flavour slot
91
+ // (the pattern `gfx/mindmap` uses on `custom:affine:surface:shape`):
92
+ // `wardleyToolbarExtension` is registered always-on because a stored map
93
+ // must keep its axes and labels, while choosing how hard to check it is
94
+ // tooling and belongs here. The config itself names no framework — it
95
+ // reads roles and profiles — so a second framework registers the very
96
+ // same object on its own flavour.
97
+ context.register(ToolbarModuleExtension({
98
+ id: BlockFlavourIdentifier('custom:affine:surface:wardley'),
99
+ config: validationToolbarConfig,
100
+ }));
101
+ // The four natures — Wardley's type-3 qualification (MF3, ADR 0007).
102
+ // Seeded on the SAME mechanism a host uses for its own taxonomy: the
103
+ // library ships one real pack, and a client's private extension is a
104
+ // second pack with another `packId` that merges with this one, with no
105
+ // library release. All packs must land in this one DI scope — `getAll`
106
+ // never merges across scopes.
107
+ context.register(UniverseTagDefsExtension(WARDLEY_TAG_DEFS));
108
+ // The qualification dropdown on the selected component's toolbar. A
109
+ // THIRD module on the same element, through a free `custom:` flavour
110
+ // slot. Generic in shape — it names no framework and builds its sections
111
+ // from the seeded packs — but parameterized by this framework's role
112
+ // vocabulary, because ADR 0007 § 2bis deliberately keeps roles out of DI
113
+ // and there is therefore no way to look up "which vocabulary governs this
114
+ // element". Registered on the NODE and on the GROUP: one click on a
115
+ // Wardley component selects the group that holds the circle and its
116
+ // label, and the role lives on the circle.
117
+ for (const flavour of [
118
+ 'custom:affine:surface:wardleyNode',
119
+ 'custom:affine:surface:group',
120
+ ]) {
121
+ context.register(ToolbarModuleExtension({
122
+ id: BlockFlavourIdentifier(flavour),
123
+ config: tagsToolbarConfig(WARDLEY_ROLES),
124
+ }));
125
+ }
126
+ // The reversed reading (MF3): what the map says about a component, on
127
+ // demand. One declaration — the roles, the nature tag, the dependency
128
+ // edge and the map's own zones, all of them already stated elsewhere in
129
+ // this framework — is the whole of what makes the generic engine able to
130
+ // read a Wardley map. The CLICK that triggers it is registered once by
131
+ // the surface and gated by the presence of this profile, so it goes with
132
+ // the flag without either side naming the other.
133
+ context.register(ReadingProfileExtension(WARDLEY_READING));
32
134
  context.register(wardleySeniorTool);
33
- context.register(wardleyToolbarExtension);
34
- context.register(wardleyNodeToolbarExtension);
35
- // Edgeless-scoped chords (w+c, w+l, ...). Registering here inherits
36
- // both the wardley flag gating and the edgeless-only availability;
37
- // the installer is ShortcutKeymapExtension('edgeless') in root.
38
- context.register(ShortcutExtension(wardleyShortcuts));
135
+ // The 13 Wardley commands, ONE registration for both faces: the
136
+ // enumerable registry the sub-menu renders from, and — through
137
+ // `toShortcutDescriptor` — the edgeless chords (w+c, w+l, ...).
138
+ // Registering here inherits both the wardley flag gating and the
139
+ // edgeless-only availability; the keymap installer is
140
+ // ShortcutKeymapExtension('edgeless') in root.
141
+ context.register(CommandExtension(wardleyCommands, wardleyCommandIcons));
39
142
  }
40
143
  }
41
144
  }
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-wardley",
3
3
  "description": "Labre wardley framework for @formicoidea/labre-core.",
4
- "version": "0.31.0",
4
+ "version": "0.32.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -27,7 +27,7 @@
27
27
  "dist"
28
28
  ],
29
29
  "dependencies": {
30
- "@formicoidea/labre-core": "0.31.0",
30
+ "@formicoidea/labre-core": "0.32.0",
31
31
  "lit": "^3.2.0"
32
32
  }
33
33
  }
@@ -1,20 +0,0 @@
1
- import type { WardleyBackgroundElementModel } from '@formicoidea/labre-core/model';
2
- /** The editable label fields of a Wardley background. */
3
- export type WardleyLabelField = 'xAxisTitle' | 'yAxisTitle' | 'evolutionStart' | 'evolutionEnd' | 'visibilityHigh' | 'visibilityLow' | 'phase0' | 'phase1' | 'phase2' | 'phase3';
4
- /** A label's hit box in element-local coordinates (axis-aligned, padded). */
5
- export interface WardleyLabelHit {
6
- field: WardleyLabelField;
7
- minX: number;
8
- minY: number;
9
- maxX: number;
10
- maxY: number;
11
- }
12
- /**
13
- * Compute the clickable boxes of every *visible* Wardley label, in the
14
- * element's local space. Positions are derived from the SAME constants the
15
- * renderer uses (`MARGIN` / `OFFSETS` / `FONTS`), so the hit boxes track the
16
- * drawn text. Boxes are padded so double-clicking is forgiving.
17
- */
18
- export declare function getWardleyLabelHits(model: WardleyBackgroundElementModel, w: number, h: number): WardleyLabelHit[];
19
- /** First label whose (padded) box contains the local point, or null. */
20
- export declare function hitTestWardleyLabel(hits: WardleyLabelHit[], lx: number, ly: number): WardleyLabelHit | null;
@@ -1,72 +0,0 @@
1
- import { FONTS, MARGIN, OFFSETS } from './consts.js';
2
- /** Rough per-character advance — only used to size generous hit boxes. */
3
- const approxTextWidth = (text, fontSize) => Math.max(fontSize, text.length * fontSize * 0.6);
4
- /**
5
- * Compute the clickable boxes of every *visible* Wardley label, in the
6
- * element's local space. Positions are derived from the SAME constants the
7
- * renderer uses (`MARGIN` / `OFFSETS` / `FONTS`), so the hit boxes track the
8
- * drawn text. Boxes are padded so double-clicking is forgiving.
9
- */
10
- export function getWardleyLabelHits(model, w, h) {
11
- const px0 = MARGIN.left;
12
- const px1 = w - MARGIN.right;
13
- const py0 = MARGIN.top;
14
- const py1 = h - MARGIN.bottom;
15
- const ex = (r) => px0 + r * (px1 - px0);
16
- const pad = 6;
17
- const hits = [];
18
- // Horizontal label anchored on its text baseline.
19
- const addH = (field, text, fontSize, ax, baseline, align) => {
20
- const tw = approxTextWidth(text, fontSize);
21
- const minX = align === 'right' ? ax - tw : ax;
22
- const maxX = align === 'right' ? ax : ax + tw;
23
- hits.push({
24
- field,
25
- minX: minX - pad,
26
- maxX: maxX + pad,
27
- minY: baseline - fontSize - pad,
28
- maxY: baseline + fontSize * 0.3 + pad,
29
- });
30
- };
31
- // Vertical label (drawn rotated -90°), centered on (ax, ay).
32
- const addV = (field, text, fontSize, ax, ay) => {
33
- const tw = approxTextWidth(text, fontSize);
34
- hits.push({
35
- field,
36
- minX: ax - fontSize - pad,
37
- maxX: ax + fontSize * 0.4 + pad,
38
- minY: ay - tw / 2 - pad,
39
- maxY: ay + tw / 2 + pad,
40
- });
41
- };
42
- if (model.showColumnLabels) {
43
- addH('phase0', model.phase0, FONTS.phase, ex(0) + OFFSETS.phasePad, py1 + OFFSETS.phaseBaseline, 'left');
44
- addH('phase1', model.phase1, FONTS.phase, ex(0.175) + OFFSETS.phasePad, py1 + OFFSETS.phaseBaseline, 'left');
45
- addH('phase2', model.phase2, FONTS.phase, ex(0.4) + OFFSETS.phasePad, py1 + OFFSETS.phaseBaseline, 'left');
46
- addH('phase3', model.phase3, FONTS.phase, ex(0.7) + OFFSETS.phasePad, py1 + OFFSETS.phaseBaseline, 'left');
47
- }
48
- if (model.showXAxis) {
49
- addH('xAxisTitle', model.xAxisTitle, FONTS.axis, px1 - OFFSETS.evolutionPadRight, py1 + OFFSETS.phaseBaseline, 'right');
50
- }
51
- if (model.showCornerLabels) {
52
- addH('evolutionStart', model.evolutionStart, FONTS.direction, px0 + OFFSETS.directionPadLeft, py0 + OFFSETS.directionTop, 'left');
53
- addH('evolutionEnd', model.evolutionEnd, FONTS.direction, px1 - OFFSETS.directionPadRight, py0 + OFFSETS.directionTop, 'right');
54
- }
55
- if (model.showYAxis) {
56
- addV('yAxisTitle', model.yAxisTitle, FONTS.axis, px0 - OFFSETS.yHug, (py0 + py1) / 2);
57
- }
58
- if (model.showVisibilityLabels) {
59
- addV('visibilityHigh', model.visibilityHigh, FONTS.visibility, px0 - OFFSETS.yHug, py0 + OFFSETS.visibleTop);
60
- addV('visibilityLow', model.visibilityLow, FONTS.visibility, px0 - OFFSETS.yHug, py1 - OFFSETS.invisibleBottom);
61
- }
62
- return hits;
63
- }
64
- /** First label whose (padded) box contains the local point, or null. */
65
- export function hitTestWardleyLabel(hits, lx, ly) {
66
- for (const hit of hits) {
67
- if (lx >= hit.minX && lx <= hit.maxX && ly >= hit.minY && ly <= hit.maxY) {
68
- return hit;
69
- }
70
- }
71
- return null;
72
- }
@@ -1,2 +0,0 @@
1
- import type { ShortcutDescriptor } from '@formicoidea/labre-core/std';
2
- export declare const wardleyShortcuts: ShortcutDescriptor[];
package/dist/shortcuts.js DELETED
@@ -1,37 +0,0 @@
1
- import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
2
- import { activateWardleyConnector, createWardleyBackground, createWardleyInertia, createWardleyNode, createWardleyPipeline, WARDLEY_SHORTCUT_SOURCE, } from './actions.js';
3
- /**
4
- * Wardley canvas chords: press `w`, then the action key. Edgeless-scoped and
5
- * registered by {@link WardleyViewExtension}, so they only exist when the
6
- * edgeless editor is mounted AND the `wardley` block flag is enabled. The
7
- * host can rebind or disable each id via `ShortcutOverrides`.
8
- *
9
- * This is the reference pattern for other frameworks: same shape with their
10
- * own prefix letter, listed in `FRAMEWORK_SHORTCUT_GROUPS` (manifest) and
11
- * registered via `ShortcutExtension` in their view extension (binding).
12
- */
13
- const wardleyShortcut = (id, key, run) => ({
14
- id: `wardley.${id}`,
15
- labelKey: `com.labre.keyboardShortcuts.wardley.${id}`,
16
- defaultKeys: { mac: ['w', key], other: ['w', key] },
17
- scope: 'edgeless',
18
- owner: 'wardley',
19
- handler: std => ctx => {
20
- const gfx = std.get(GfxControllerIdentifier);
21
- // Never fire while editing text on the canvas.
22
- if (gfx.selection.editing)
23
- return false;
24
- ctx.get('defaultState').event.preventDefault();
25
- run(gfx);
26
- return true;
27
- },
28
- });
29
- export const wardleyShortcuts = [
30
- wardleyShortcut('addComponent', 'c', gfx => createWardleyNode(gfx, 'component', WARDLEY_SHORTCUT_SOURCE)),
31
- wardleyShortcut('linkTool', 'l', gfx => activateWardleyConnector(gfx, 'link', WARDLEY_SHORTCUT_SOURCE)),
32
- wardleyShortcut('evolutionArrow', 'a', gfx => activateWardleyConnector(gfx, 'arrow', WARDLEY_SHORTCUT_SOURCE)),
33
- wardleyShortcut('addInertia', 'i', gfx => createWardleyInertia(gfx, WARDLEY_SHORTCUT_SOURCE)),
34
- wardleyShortcut('addPipeline', 'p', gfx => createWardleyPipeline(gfx, WARDLEY_SHORTCUT_SOURCE)),
35
- wardleyShortcut('addMethod', 'm', gfx => createWardleyNode(gfx, 'method', WARDLEY_SHORTCUT_SOURCE)),
36
- wardleyShortcut('addBackground', 'b', gfx => createWardleyBackground(gfx, 'classic', WARDLEY_SHORTCUT_SOURCE)),
37
- ];