@formicoidea/labre-framework-wardley 0.39.3 → 0.41.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/legend.js CHANGED
@@ -1,5 +1,7 @@
1
1
  import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
2
2
  import { ConnectorElementModel, ConnectorMode, FontFamily, PointStyle, ShapeElementModel, ShapeStyle, StrokeStyle, WardleyNodeElementModel, } from '@formicoidea/labre-core/model';
3
+ import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
4
+ import { translateKey, } from '@formicoidea/labre-core/shared/services';
3
5
  import { Bound } from '@formicoidea/labre-core/global/gfx';
4
6
  import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
5
7
  import { GRADIENT_GREEN, GRADIENT_RED } from './gradient.js';
@@ -20,37 +22,82 @@ const LEGEND_ORDER = [
20
22
  'decelerator',
21
23
  'area',
22
24
  ];
23
- /** Default (editable) descriptions for each legend row. */
25
+ /**
26
+ * Default (editable) descriptions for each legend row — a `ChromeWording` per
27
+ * row so the seed a legend writes into the document is resolved through the
28
+ * host at BUILD time, like every other text `createWardleyLegend` creates.
29
+ * The fallback is the literal that shipped before these keys existed.
30
+ */
24
31
  const LEGEND_DESC = {
25
- component: 'Need / capability (activity, practice, data…)',
26
- anchor: 'Stakeholder (customer, user…)',
27
- market: 'Market (set of actors)',
28
- ecosystem: 'Ecosystem',
29
- method: 'Component + method (color = phase)',
30
- pipeline: 'Pipeline (possible choices for a capability)',
31
- link: 'Need relation (parent → child)',
32
- arrow: 'Evolution / movement (red = future)',
33
- inertia: 'Inertia to change',
34
- porter: "Porter's forces (external competition: R relative, L survival, E establish)",
35
- accelerator: 'Accelerator (speeds evolution up)',
36
- decelerator: 'Decelerator (slows evolution down)',
37
- area: 'Area (zone of the map)',
32
+ component: [
33
+ 'com.labre.wardley.legend.desc.component',
34
+ 'Need / capability (activity, practice, data…)',
35
+ ],
36
+ anchor: [
37
+ 'com.labre.wardley.legend.desc.anchor',
38
+ 'Stakeholder (customer, user…)',
39
+ ],
40
+ market: ['com.labre.wardley.legend.desc.market', 'Market (set of actors)'],
41
+ ecosystem: ['com.labre.wardley.legend.desc.ecosystem', 'Ecosystem'],
42
+ method: [
43
+ 'com.labre.wardley.legend.desc.method',
44
+ 'Component + method (color = phase)',
45
+ ],
46
+ pipeline: [
47
+ 'com.labre.wardley.legend.desc.pipeline',
48
+ 'Pipeline (possible choices for a capability)',
49
+ ],
50
+ link: [
51
+ 'com.labre.wardley.legend.desc.link',
52
+ 'Need relation (parent → child)',
53
+ ],
54
+ arrow: [
55
+ 'com.labre.wardley.legend.desc.arrow',
56
+ 'Evolution / movement (red = future)',
57
+ ],
58
+ inertia: ['com.labre.wardley.legend.desc.inertia', 'Inertia to change'],
59
+ porter: [
60
+ 'com.labre.wardley.legend.desc.porter',
61
+ "Porter's forces (external competition: R relative, L survival, E establish)",
62
+ ],
63
+ accelerator: [
64
+ 'com.labre.wardley.legend.desc.accelerator',
65
+ 'Accelerator (speeds evolution up)',
66
+ ],
67
+ decelerator: [
68
+ 'com.labre.wardley.legend.desc.decelerator',
69
+ 'Decelerator (slows evolution down)',
70
+ ],
71
+ area: ['com.labre.wardley.legend.desc.area', 'Area (zone of the map)'],
38
72
  };
73
+ /** Every {@link LEGEND_DESC} wording, for `translations.ts`'s manifest. */
74
+ export const WARDLEY_LEGEND_DESC_WORDINGS = Object.values(LEGEND_DESC);
39
75
  /** Gradient-meaning block, keyed by variant (caption + 2-colour swatch). */
40
76
  const LEGEND_GRADIENT = {
41
77
  opportunity: {
42
- caption: 'Opportunity gradient: differential value (green) vs operational value (red).',
78
+ caption: [
79
+ 'com.labre.wardley.legend.gradient.opportunity',
80
+ 'Opportunity gradient: differential value (green) vs operational value (red).',
81
+ ],
43
82
  swatch: [GRADIENT_GREEN, GRADIENT_RED],
44
83
  },
45
84
  benefit: {
46
- caption: 'Gradient: investment (red) then benefit (green).',
85
+ caption: [
86
+ 'com.labre.wardley.legend.gradient.benefit',
87
+ 'Gradient: investment (red) then benefit (green).',
88
+ ],
47
89
  swatch: [GRADIENT_RED, GRADIENT_GREEN],
48
90
  },
49
91
  'evolution-gradient': {
50
- caption: "Gradient representing the growth of Wardley's evolution function.",
51
- swatch: ['#9aa0a6', '#cfd2d6'],
92
+ caption: [
93
+ 'com.labre.wardley.legend.gradient.evolution',
94
+ "Gradient representing the growth of Wardley's evolution function.",
95
+ ],
96
+ swatch: [NOTATION_NEUTRALS.divider, NOTATION_NEUTRALS.legendBorder],
52
97
  },
53
98
  };
99
+ /** Every {@link LEGEND_GRADIENT} caption, for `translations.ts`'s manifest. */
100
+ export const WARDLEY_LEGEND_GRADIENT_WORDINGS = Object.values(LEGEND_GRADIENT).map(g => g.caption);
54
101
  /* ── Porter's five forces: the panel under the rows ───────────────────── */
55
102
  /** The panel's own numbers. Model units, like every other measure here. */
56
103
  const PORTER_PANEL = {
@@ -74,19 +121,50 @@ const PORTER_PANEL = {
74
121
  captionFs: 13,
75
122
  captionH: 21,
76
123
  backing: '#e5e7eb',
77
- backingStroke: '#9aa0a6',
124
+ backingStroke: NOTATION_NEUTRALS.divider,
78
125
  };
79
126
  /** The four forces the boxes name, north first and then clockwise. */
80
127
  const PORTER_FORCES = [
81
- 'Threat of new entrants',
82
- 'Bargaining power of customers',
83
- 'Threat of substitutes',
84
- 'Bargaining power of suppliers',
128
+ [
129
+ 'com.labre.wardley.legend.porter.force.new-entrants',
130
+ 'Threat of new entrants',
131
+ ],
132
+ [
133
+ 'com.labre.wardley.legend.porter.force.customers',
134
+ 'Bargaining power of customers',
135
+ ],
136
+ [
137
+ 'com.labre.wardley.legend.porter.force.substitutes',
138
+ 'Threat of substitutes',
139
+ ],
140
+ [
141
+ 'com.labre.wardley.legend.porter.force.suppliers',
142
+ 'Bargaining power of suppliers',
143
+ ],
85
144
  ];
86
- /** What the panel's own glyph reads: the notation, not one force. */
145
+ /** What the panel's own glyph reads: the notation, not one force — untouched, like the Cynefin A/C letters. */
87
146
  const PORTER_PANEL_LETTERS = 'R/L/E';
88
147
  /** What the letters mean, spelled out under the diagram. */
89
- const PORTER_CAPTION = 'R/L/E = Relative competition, or struggLe for survival, or struggle to Establish';
148
+ const PORTER_CAPTION = [
149
+ 'com.labre.wardley.legend.porter.caption',
150
+ 'R/L/E = Relative competition, or struggLe for survival, or struggle to Establish',
151
+ ];
152
+ /** The legend box's own title. */
153
+ export const WARDLEY_LEGEND_TITLE = [
154
+ 'com.labre.wardley.legend.title',
155
+ 'Legend',
156
+ ];
157
+ /** The five-forces panel's own title. */
158
+ export const WARDLEY_LEGEND_PORTER_TITLE = [
159
+ 'com.labre.wardley.legend.porter.title',
160
+ "Porter's five forces",
161
+ ];
162
+ /** Every {@link PORTER_FORCES} wording, plus the caption, for the manifest. */
163
+ export const WARDLEY_LEGEND_PORTER_WORDINGS = [
164
+ WARDLEY_LEGEND_PORTER_TITLE,
165
+ ...PORTER_FORCES,
166
+ PORTER_CAPTION,
167
+ ];
90
168
  /**
91
169
  * How far a porter glyph of the given radius reaches, arrow tips included.
92
170
  *
@@ -144,6 +222,18 @@ export function porterPanelLayout(w) {
144
222
  caption: [p.pad, captionY, inner, p.captionH],
145
223
  };
146
224
  }
225
+ /**
226
+ * Every wording this file writes onto the canvas, for `translations.ts`'s
227
+ * manifest contribution — the auto-legend's own title, its rows' captions,
228
+ * the gradient blocks and the Porter panel, all as `seed` (written into the
229
+ * document once, at the moment a legend is generated).
230
+ */
231
+ export const WARDLEY_LEGEND_WORDINGS = [
232
+ WARDLEY_LEGEND_TITLE,
233
+ ...WARDLEY_LEGEND_DESC_WORDINGS,
234
+ ...WARDLEY_LEGEND_GRADIENT_WORDINGS,
235
+ ...WARDLEY_LEGEND_PORTER_WORDINGS,
236
+ ];
147
237
  /**
148
238
  * Build a "Legend" group from real, editable elements (white rect frame +
149
239
  * "Legend" text + one row of [real component glyph + description text] per
@@ -482,8 +572,8 @@ export function createWardleyLegend(std, bg) {
482
572
  type: 'shape',
483
573
  shapeType: 'rect',
484
574
  filled: true,
485
- fillColor: '#ffffff',
486
- strokeColor: '#cfd2d6',
575
+ fillColor: NOTATION_NEUTRALS.cardFill,
576
+ strokeColor: NOTATION_NEUTRALS.legendBorder,
487
577
  strokeWidth: 1,
488
578
  shapeStyle: ShapeStyle.General,
489
579
  roughness: 0,
@@ -491,13 +581,13 @@ export function createWardleyLegend(std, bg) {
491
581
  xywh: new Bound(x0, y0, W, H).serialize(),
492
582
  }));
493
583
  // Title.
494
- ids.push(text('Legend', x0 + PAD, y0 + PAD, W - PAD * 2, TITLE_FS + 6, TITLE_FS));
584
+ ids.push(text(translateKey(std, ...WARDLEY_LEGEND_TITLE), x0 + PAD, y0 + PAD, W - PAD * 2, TITLE_FS + 6, TITLE_FS));
495
585
  // Rows.
496
586
  let ry = y0 + PAD + TITLE_H;
497
587
  for (const t of rows) {
498
588
  const cyRow = ry + ROW_H / 2;
499
589
  ids.push(...glyph(t, x0 + PAD + GLYPH_W / 2, cyRow));
500
- ids.push(text(LEGEND_DESC[t], x0 + PAD + GLYPH_W + GAP, cyRow - (TEXT_FS + 8) / 2, TEXT_W, TEXT_FS + 8, TEXT_FS));
590
+ ids.push(text(translateKey(std, ...LEGEND_DESC[t]), x0 + PAD + GLYPH_W + GAP, cyRow - (TEXT_FS + 8) / 2, TEXT_W, TEXT_FS + 8, TEXT_FS));
501
591
  ry += ROW_H;
502
592
  }
503
593
  // Gradient meaning block: a separator, then [2-colour swatch | caption].
@@ -507,8 +597,8 @@ export function createWardleyLegend(std, bg) {
507
597
  type: 'shape',
508
598
  shapeType: 'rect',
509
599
  filled: true,
510
- fillColor: '#cfd2d6',
511
- strokeColor: '#cfd2d6',
600
+ fillColor: NOTATION_NEUTRALS.legendBorder,
601
+ strokeColor: NOTATION_NEUTRALS.legendBorder,
512
602
  strokeWidth: 0,
513
603
  shapeStyle: ShapeStyle.General,
514
604
  roughness: 0,
@@ -525,7 +615,7 @@ export function createWardleyLegend(std, bg) {
525
615
  shapeType: 'rect',
526
616
  filled: true,
527
617
  fillColor: col,
528
- strokeColor: '#cfd2d6',
618
+ strokeColor: NOTATION_NEUTRALS.legendBorder,
529
619
  strokeWidth: 0.5,
530
620
  shapeStyle: ShapeStyle.General,
531
621
  roughness: 0,
@@ -533,7 +623,7 @@ export function createWardleyLegend(std, bg) {
533
623
  xywh: new Bound(sx + i * (sw + sgap), cyRow - sw / 2, sw, sw).serialize(),
534
624
  }));
535
625
  });
536
- ids.push(text(grad.caption, x0 + PAD + GLYPH_W + GAP, cyRow - GRAD_ROW_H / 2, TEXT_W, GRAD_ROW_H, TEXT_FS));
626
+ ids.push(text(translateKey(std, ...grad.caption), x0 + PAD + GLYPH_W + GAP, cyRow - GRAD_ROW_H / 2, TEXT_W, GRAD_ROW_H, TEXT_FS));
537
627
  }
538
628
  // Porter's five forces: the panel the PO's reference draws, under everything
539
629
  // else. It is not a row — a row says what a glyph IS, and this says what the
@@ -559,7 +649,7 @@ export function createWardleyLegend(std, bg) {
559
649
  xywh: new Bound(px, py, panel.w, panel.h).serialize(),
560
650
  }));
561
651
  const [tx, ty, tw, th] = panel.title;
562
- ids.push(text("Porter's five forces", px + tx, py + ty, tw, th, p.titleFs));
652
+ ids.push(text(translateKey(std, ...WARDLEY_LEGEND_PORTER_TITLE), px + tx, py + ty, tw, th, p.titleFs));
563
653
  const [cx, cy] = panel.center;
564
654
  ids.push(surface.addElement({
565
655
  type: 'wardleyNode',
@@ -592,7 +682,7 @@ export function createWardleyLegend(std, bg) {
592
682
  type: 'shape',
593
683
  shapeType: 'rect',
594
684
  filled: true,
595
- fillColor: '#ffffff',
685
+ fillColor: NODE_FILL,
596
686
  strokeColor: NODE_STROKE,
597
687
  strokeWidth: 1,
598
688
  shapeStyle: ShapeStyle.General,
@@ -600,10 +690,10 @@ export function createWardleyLegend(std, bg) {
600
690
  radius: 0,
601
691
  xywh: new Bound(px + bx, py + by, bw, bh).serialize(),
602
692
  }));
603
- ids.push(text(box.label, px + bx + 4, py + by + 3, bw - 8, bh - 6, p.boxFs, 'center'));
693
+ ids.push(text(translateKey(std, ...box.label), px + bx + 4, py + by + 3, bw - 8, bh - 6, p.boxFs, 'center'));
604
694
  }
605
695
  const [capX, capY, capW, capH] = panel.caption;
606
- ids.push(text(PORTER_CAPTION, px + capX, py + capY, capW, capH, p.captionFs, 'center'));
696
+ ids.push(text(translateKey(std, ...PORTER_CAPTION), px + capX, py + capY, capW, capH, p.captionFs, 'center'));
607
697
  }
608
698
  // 4. Group everything and select it.
609
699
  const [, result] = std.command.exec(createGroupCommand, { elements: ids });
package/dist/morph.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { type MorphSpec } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { WardleyNodeElementModel } from '@formicoidea/labre-core/model';
3
+ import type { BlockStdScope } from '@formicoidea/labre-core/std';
3
4
  import type { GfxPrimitiveElementModel } from '@formicoidea/labre-core/std/gfx';
4
5
  /**
5
6
  * What a Wardley artefact may BECOME — the declaration behind the "Change type"
@@ -81,8 +82,17 @@ export declare function wardleyNodeOfComponent(model: GfxPrimitiveElementModel):
81
82
  *
82
83
  * Pure and total over every string: the input is a canvas text element somebody
83
84
  * may have typed anything into.
85
+ *
86
+ * `std` is OPTIONAL: "untouched" means equal to the English prompt OR to the
87
+ * host's own resolved wording for `from`'s kind, and the target's prompt is
88
+ * likewise resolved through the host when one is given — mirroring
89
+ * `c4MorphedTypeLine`'s fix for the identical class of gap. With no `std`
90
+ * this behaves exactly as it always has (English only). The morph toolbar's
91
+ * `afterMorph` callback (`MorphSpec`, `packages/affine/blocks/surface`) now
92
+ * hands its callee the inserting editor's `std`, so `rewriteLabel` below
93
+ * passes it straight through.
84
94
  */
85
- export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyMorphKind, rawText: string | null | undefined): string | null;
95
+ export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyMorphKind, rawText: string | null | undefined, std?: BlockStdScope): string | null;
86
96
  /**
87
97
  * Everything the morph owes a Wardley composite beyond the props patch — the
88
98
  * `afterMorph` half of the spec, run inside the generic module's one
@@ -110,7 +120,7 @@ export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyM
110
120
  * LEAVING comes apart before the one it is arriving at is built, so a
111
121
  * market → pipeline never has three dots and a handle at once.
112
122
  */
113
- export declare function wardleyMorphComposite(selected: GfxPrimitiveElementModel, from: WardleyMorphKind, to: WardleyMorphKind): void;
123
+ export declare function wardleyMorphComposite(selected: GfxPrimitiveElementModel, from: WardleyMorphKind, to: WardleyMorphKind, std?: BlockStdScope): void;
114
124
  /**
115
125
  * Wardley's morph declaration, handed to the generic `morphToolbarConfig`.
116
126
  *
package/dist/morph.js CHANGED
@@ -1,7 +1,8 @@
1
1
  import { morphLabel, } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { ConnectorElementModel, GroupElementModel, TextElementModel, WardleyNodeElementModel, } from '@formicoidea/labre-core/model';
3
+ import { translateKey } from '@formicoidea/labre-core/shared/services';
3
4
  import { wardleyCommandIcons, wardleyCommands } from './commands.js';
4
- import { wardleyCanonicalBox, wardleyHandleBox, wardleyHandleProps, wardleyMarketDotBoxes, wardleyMarketDotProps, wardleyMarketLinkPairs, wardleyMarketLinkProps, WARDLEY_NODE_LABEL, wardleyMorphClears, wardleyMorphProps, } from './presets.js';
5
+ import { wardleyCanonicalBox, wardleyHandleBox, wardleyHandleProps, wardleyMarketDotBoxes, wardleyMarketDotProps, wardleyMarketLinkPairs, wardleyMarketLinkProps, WARDLEY_NODE_LABEL, wardleyMorphClears, wardleyMorphProps, wardleyNodeLabelKey, } from './presets.js';
5
6
  import { WARDLEY_ROLE } from './roles.js';
6
7
  /**
7
8
  * What a Wardley artefact may BECOME — the declaration behind the "Change type"
@@ -148,11 +149,26 @@ export function wardleyNodeOfComponent(model) {
148
149
  *
149
150
  * Pure and total over every string: the input is a canvas text element somebody
150
151
  * may have typed anything into.
152
+ *
153
+ * `std` is OPTIONAL: "untouched" means equal to the English prompt OR to the
154
+ * host's own resolved wording for `from`'s kind, and the target's prompt is
155
+ * likewise resolved through the host when one is given — mirroring
156
+ * `c4MorphedTypeLine`'s fix for the identical class of gap. With no `std`
157
+ * this behaves exactly as it always has (English only). The morph toolbar's
158
+ * `afterMorph` callback (`MorphSpec`, `packages/affine/blocks/surface`) now
159
+ * hands its callee the inserting editor's `std`, so `rewriteLabel` below
160
+ * passes it straight through.
151
161
  */
152
- export function wardleyMorphedLabel(from, to, rawText) {
153
- return (rawText ?? '').trim() === WARDLEY_NODE_LABEL[from]
154
- ? WARDLEY_NODE_LABEL[to]
155
- : null;
162
+ export function wardleyMorphedLabel(from, to, rawText, std) {
163
+ const text = (rawText ?? '').trim();
164
+ const fromPrompt = std
165
+ ? translateKey(std, wardleyNodeLabelKey(from), WARDLEY_NODE_LABEL[from])
166
+ : WARDLEY_NODE_LABEL[from];
167
+ if (text !== WARDLEY_NODE_LABEL[from] && text !== fromPrompt)
168
+ return null;
169
+ return std
170
+ ? translateKey(std, wardleyNodeLabelKey(to), WARDLEY_NODE_LABEL[to])
171
+ : WARDLEY_NODE_LABEL[to];
156
172
  }
157
173
  /** The centre of an element, from the box it currently occupies. */
158
174
  function centreOf(model) {
@@ -309,12 +325,12 @@ function removeHandle(group, carrier) {
309
325
  * inside the caller's `captureSync`, so the rewrite is part of the same single
310
326
  * ctrl+z as the kind that made it necessary.
311
327
  */
312
- function rewriteLabel(group, from, to) {
328
+ function rewriteLabel(group, from, to, std) {
313
329
  const label = labelOfComposite(group);
314
330
  if (!label || label.isLocked())
315
331
  return;
316
332
  const text = label.text.toString().trim();
317
- const next = wardleyMorphedLabel(from, to, text);
333
+ const next = wardleyMorphedLabel(from, to, text, std);
318
334
  if (next === null || next === text)
319
335
  return;
320
336
  label.surface.store.transact(() => {
@@ -349,7 +365,7 @@ function rewriteLabel(group, from, to) {
349
365
  * LEAVING comes apart before the one it is arriving at is built, so a
350
366
  * market → pipeline never has three dots and a handle at once.
351
367
  */
352
- export function wardleyMorphComposite(selected, from, to) {
368
+ export function wardleyMorphComposite(selected, from, to, std) {
353
369
  if (!(selected instanceof GroupElementModel))
354
370
  return;
355
371
  const carrier = wardleyNodeOfComponent(selected);
@@ -367,7 +383,7 @@ export function wardleyMorphComposite(selected, from, to) {
367
383
  addMarketGlyph(selected, cx, cy);
368
384
  if (to === 'pipeline')
369
385
  addHandle(selected, carrier, cx, cy);
370
- rewriteLabel(selected, from, to);
386
+ rewriteLabel(selected, from, to, std);
371
387
  }
372
388
  /**
373
389
  * Wardley's morph declaration, handed to the generic `morphToolbarConfig`.
package/dist/natures.js CHANGED
@@ -86,11 +86,14 @@ export const WARDLEY_TAG_DEFS = {
86
86
  packId: 'wardley-core',
87
87
  framework: 'wardley',
88
88
  label: 'Wardley',
89
+ labelKey: 'com.labre.wardley.tag-pack.label',
89
90
  tags: [
90
91
  {
91
92
  id: WARDLEY_NATURE_TAG_ID,
92
93
  label: 'Nature',
94
+ labelKey: 'com.labre.wardley.tag.nature',
93
95
  description: 'What kind of thing this component is: an activity, data, a practice or knowledge.',
96
+ descriptionKey: 'com.labre.wardley.tag.nature.description',
94
97
  cardinality: 'single',
95
98
  appliesTo: [WARDLEY_ROLE.component],
96
99
  // No `order`, on either tag of this pack: an absent order sorts by SEED
@@ -103,29 +106,39 @@ export const WARDLEY_TAG_DEFS = {
103
106
  {
104
107
  id: WARDLEY_NATURE.activity,
105
108
  label: 'Activity',
109
+ labelKey: 'com.labre.wardley.tag.nature.activity',
106
110
  description: 'Something that is DONE — a step, a service, a process.',
111
+ descriptionKey: 'com.labre.wardley.tag.nature.activity.description',
107
112
  },
108
113
  {
109
114
  id: WARDLEY_NATURE.data,
110
115
  label: 'Data',
116
+ labelKey: 'com.labre.wardley.tag.nature.data',
111
117
  description: 'Something that is RECORDED — a dataset, a register.',
118
+ descriptionKey: 'com.labre.wardley.tag.nature.data.description',
112
119
  },
113
120
  {
114
121
  id: WARDLEY_NATURE.practice,
115
122
  label: 'Practice',
123
+ labelKey: 'com.labre.wardley.tag.nature.practice',
116
124
  description: 'A way of doing — a method, a convention, an operating model.',
125
+ descriptionKey: 'com.labre.wardley.tag.nature.practice.description',
117
126
  },
118
127
  {
119
128
  id: WARDLEY_NATURE.knowledge,
120
129
  label: 'Knowledge',
130
+ labelKey: 'com.labre.wardley.tag.nature.knowledge',
121
131
  description: 'Something that is KNOWN — a model, a theory, a rule.',
132
+ descriptionKey: 'com.labre.wardley.tag.nature.knowledge.description',
122
133
  },
123
134
  ],
124
135
  },
125
136
  {
126
137
  id: WARDLEY_COMPETITION_TAG_ID,
127
138
  label: 'Competition',
139
+ labelKey: 'com.labre.wardley.tag.competition',
128
140
  description: "Which of Porter's forces this glyph marks: relative competition, a struggle for survival, or a struggle to establish.",
141
+ descriptionKey: 'com.labre.wardley.tag.competition.description',
129
142
  cardinality: 'single',
130
143
  appliesTo: [WARDLEY_ROLE.porter],
131
144
  values: [
@@ -135,17 +148,23 @@ export const WARDLEY_TAG_DEFS = {
135
148
  // shows: a reader picking a value here is choosing what the circle
136
149
  // will say, not filling in a form about it.
137
150
  label: 'Relative competition (R)',
151
+ labelKey: 'com.labre.wardley.tag.competition.relative',
138
152
  description: 'Established players competing against one another on a market that exists.',
153
+ descriptionKey: 'com.labre.wardley.tag.competition.relative.description',
139
154
  },
140
155
  {
141
156
  id: WARDLEY_COMPETITION.struggle,
142
157
  label: 'Struggle for survival (L)',
158
+ labelKey: 'com.labre.wardley.tag.competition.struggle',
143
159
  description: 'Pressure on a player to stay in a market it is already in.',
160
+ descriptionKey: 'com.labre.wardley.tag.competition.struggle.description',
144
161
  },
145
162
  {
146
163
  id: WARDLEY_COMPETITION.establish,
147
164
  label: 'Struggle to establish (E)',
165
+ labelKey: 'com.labre.wardley.tag.competition.establish',
148
166
  description: 'Pressure on a newcomer trying to get into a market.',
167
+ descriptionKey: 'com.labre.wardley.tag.competition.establish.description',
149
168
  },
150
169
  ],
151
170
  },
@@ -9,8 +9,9 @@
9
9
  */
10
10
  /** Default node diameter (= the map label font size, 18). White fill, thin border. */
11
11
  export declare const NODE_SIZE = 18;
12
- export declare const NODE_FILL = "#ffffff";
13
- export declare const NODE_STROKE = "#1f2328";
12
+ /** The shared notation scale's card fill and ink — Wardley is its reference. */
13
+ export declare const NODE_FILL: string;
14
+ export declare const NODE_STROKE: string;
14
15
  /** Thin border, matching the link / silhouette line weight. */
15
16
  export declare const NODE_STROKE_WIDTH = 1;
16
17
  /**
@@ -53,11 +54,33 @@ export declare const PIPELINE_LABEL = "Pipeline";
53
54
  * to the 1px node border.
54
55
  */
55
56
  export declare const LINK_STROKE_WIDTH = 2;
56
- /** Wardley red ("future"/evolution) — matches the validated arrow icon. */
57
+ /**
58
+ * Wardley red ("future"/evolution) — matches the validated arrow icon.
59
+ *
60
+ * An IDENTITY colour: the legend recognises a stored evolution link by it
61
+ * (`legend.ts`, `el.stroke === WARDLEY_RED`). It stays a literal and must never
62
+ * be derived from `NOTATION_NEUTRALS` or any other scale — a change of value
63
+ * would orphan every link already drawn with it.
64
+ */
57
65
  export declare const WARDLEY_RED = "#d6455d";
58
- /** Dependency link grey. */
66
+ /**
67
+ * Dependency link grey.
68
+ *
69
+ * An IDENTITY colour: the legend recognises a stored dependency link by it
70
+ * (`legend.ts`, `el.stroke === LINK_GREY`). It stays a literal and must never
71
+ * be derived from `NOTATION_NEUTRALS` — a change of value would orphan every
72
+ * link already drawn with it.
73
+ */
59
74
  export declare const LINK_GREY = "#666666";
60
- /** Inertia bar color + size. */
75
+ /**
76
+ * Inertia bar color + size.
77
+ *
78
+ * An IDENTITY colour: the legend recognises a stored inertia bar by it
79
+ * (`legend.ts`, `el.fillColor === INERTIA_COLOR`). It happens to equal the
80
+ * scale's ink today, but it stays a literal and must never be derived from
81
+ * `NOTATION_NEUTRALS`: a change of the shared ink would orphan every inertia bar
82
+ * already on a map.
83
+ */
61
84
  export declare const INERTIA_COLOR = "#1f2328";
62
85
  export declare const INERTIA_SIZE: {
63
86
  w: number;
@@ -75,7 +98,7 @@ export declare const MARKET_DOT_RING = 8;
75
98
  export declare const MARKET_DOT_STROKE_WIDTH = 2;
76
99
  /** Triangle connectors: thin + dark, no arrows. */
77
100
  export declare const MARKET_LINK_WIDTH = 1;
78
- export declare const MARKET_LINK_COLOR = "#1f2328";
101
+ export declare const MARKET_LINK_COLOR: string;
79
102
  export declare const MARKET_LABEL = "Market";
80
103
  /**
81
104
  * Ecosystem node: a single connectable circle drawn as a GLYPH — a double border
@@ -1,3 +1,4 @@
1
+ import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
1
2
  /**
2
3
  * Visual constants for Wardley nodes (component + anchor) and the connector /
3
4
  * inertia presets created from the Wardley menu.
@@ -9,8 +10,9 @@
9
10
  */
10
11
  /** Default node diameter (= the map label font size, 18). White fill, thin border. */
11
12
  export const NODE_SIZE = 18;
12
- export const NODE_FILL = '#ffffff';
13
- export const NODE_STROKE = '#1f2328';
13
+ /** The shared notation scale's card fill and ink — Wardley is its reference. */
14
+ export const NODE_FILL = NOTATION_NEUTRALS.cardFill;
15
+ export const NODE_STROKE = NOTATION_NEUTRALS.ink;
14
16
  /** Thin border, matching the link / silhouette line weight. */
15
17
  export const NODE_STROKE_WIDTH = 1;
16
18
  /**
@@ -50,11 +52,33 @@ export const PIPELINE_LABEL = 'Pipeline';
50
52
  * to the 1px node border.
51
53
  */
52
54
  export const LINK_STROKE_WIDTH = 2;
53
- /** Wardley red ("future"/evolution) — matches the validated arrow icon. */
55
+ /**
56
+ * Wardley red ("future"/evolution) — matches the validated arrow icon.
57
+ *
58
+ * An IDENTITY colour: the legend recognises a stored evolution link by it
59
+ * (`legend.ts`, `el.stroke === WARDLEY_RED`). It stays a literal and must never
60
+ * be derived from `NOTATION_NEUTRALS` or any other scale — a change of value
61
+ * would orphan every link already drawn with it.
62
+ */
54
63
  export const WARDLEY_RED = '#d6455d';
55
- /** Dependency link grey. */
64
+ /**
65
+ * Dependency link grey.
66
+ *
67
+ * An IDENTITY colour: the legend recognises a stored dependency link by it
68
+ * (`legend.ts`, `el.stroke === LINK_GREY`). It stays a literal and must never
69
+ * be derived from `NOTATION_NEUTRALS` — a change of value would orphan every
70
+ * link already drawn with it.
71
+ */
56
72
  export const LINK_GREY = '#666666';
57
- /** Inertia bar color + size. */
73
+ /**
74
+ * Inertia bar color + size.
75
+ *
76
+ * An IDENTITY colour: the legend recognises a stored inertia bar by it
77
+ * (`legend.ts`, `el.fillColor === INERTIA_COLOR`). It happens to equal the
78
+ * scale's ink today, but it stays a literal and must never be derived from
79
+ * `NOTATION_NEUTRALS`: a change of the shared ink would orphan every inertia bar
80
+ * already on a map.
81
+ */
58
82
  export const INERTIA_COLOR = '#1f2328';
59
83
  export const INERTIA_SIZE = { w: 8, h: 44 };
60
84
  /**
package/dist/presets.d.ts CHANGED
@@ -72,6 +72,27 @@ export type WardleyLabelledKind = Exclude<WardleyArtefactKind, 'porter' | 'area'
72
72
  * still carries, and the only string a morph is ever allowed to rewrite.
73
73
  */
74
74
  export declare const WARDLEY_NODE_LABEL: Record<WardleyLabelledKind, string>;
75
+ /**
76
+ * The i18n key {@link WARDLEY_NODE_LABEL} is the English default of —
77
+ * resolved at placement (`createWardleyNode` / `createWardleyPipeline` /
78
+ * `createWardleyMarket`), never afterwards. "Pipeline" is the PO's glossary
79
+ * term for Wardley: the French proposal IS the English word.
80
+ *
81
+ * ## The morph's placeholder detection, translated
82
+ *
83
+ * `wardleyMorphedLabel` (`morph.ts`) decides whether to refresh an artefact's
84
+ * name across a morph, and accepts an OPTIONAL `std`: given one, it
85
+ * recognises the stored text as "untouched" whether it reads the English
86
+ * prompt or the host's own resolved wording, and resolves the TARGET kind's
87
+ * prompt through the host too (the same fix `c4MorphedTypeLine` carries,
88
+ * `gfx/c4/src/type-line.ts`). `MorphSpec.afterMorph`
89
+ * (`packages/affine/blocks/surface`) now hands its callee the morph
90
+ * toolbar's own `std`, so `rewriteLabel` passes it straight through: on a
91
+ * host running a French catalogue, morphing an untouched TRANSLATED node
92
+ * rewrites its name to the target kind's own translated prompt, exactly as a
93
+ * standalone playground already rewrote the English one.
94
+ */
95
+ export declare const wardleyNodeLabelKey: (kind: WardleyLabelledKind) => string;
75
96
  /** A box centred on a point — how every Wardley creation site places a node. */
76
97
  export declare function wardleyCenteredBox(cx: number, cy: number, w: number, h: number): string;
77
98
  /** The canonical box of one kind, centred where the artefact already stands. */
package/dist/presets.js CHANGED
@@ -65,6 +65,27 @@ export const WARDLEY_NODE_LABEL = {
65
65
  accelerator: ACCELERATOR_LABEL,
66
66
  decelerator: DECELERATOR_LABEL,
67
67
  };
68
+ /**
69
+ * The i18n key {@link WARDLEY_NODE_LABEL} is the English default of —
70
+ * resolved at placement (`createWardleyNode` / `createWardleyPipeline` /
71
+ * `createWardleyMarket`), never afterwards. "Pipeline" is the PO's glossary
72
+ * term for Wardley: the French proposal IS the English word.
73
+ *
74
+ * ## The morph's placeholder detection, translated
75
+ *
76
+ * `wardleyMorphedLabel` (`morph.ts`) decides whether to refresh an artefact's
77
+ * name across a morph, and accepts an OPTIONAL `std`: given one, it
78
+ * recognises the stored text as "untouched" whether it reads the English
79
+ * prompt or the host's own resolved wording, and resolves the TARGET kind's
80
+ * prompt through the host too (the same fix `c4MorphedTypeLine` carries,
81
+ * `gfx/c4/src/type-line.ts`). `MorphSpec.afterMorph`
82
+ * (`packages/affine/blocks/surface`) now hands its callee the morph
83
+ * toolbar's own `std`, so `rewriteLabel` passes it straight through: on a
84
+ * host running a French catalogue, morphing an untouched TRANSLATED node
85
+ * rewrites its name to the target kind's own translated prompt, exactly as a
86
+ * standalone playground already rewrote the English one.
87
+ */
88
+ export const wardleyNodeLabelKey = (kind) => `com.labre.wardley.seed.${kind}`;
68
89
  /**
69
90
  * The fill a kind is born with. White for every circle that means "a thing on
70
91
  * the value chain"; the method's grey is the one that carries meaning — it
@@ -1,2 +1,11 @@
1
1
  import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
2
+ import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The two hand-drawn palette swatches' own tile names (`Template.nameKey`):
5
+ * neither derives from a command, since `linkTool` / `evolutionArrow`
6
+ * activate a tool rather than draw anything (see `tpl` below and its
7
+ * docstring).
8
+ */
9
+ export declare const WARDLEY_TEMPLATE_NAME_LINK: ChromeWording;
10
+ export declare const WARDLEY_TEMPLATE_NAME_EVOLUTION_ARROW: ChromeWording;
2
11
  export declare const wardleyTemplateCategory: TemplateCategory;