@formicoidea/labre-framework-wardley 0.40.0 → 0.42.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.
@@ -5,6 +5,7 @@ import { lowerWardleyArea } from '../actions.js';
5
5
  import { wardleyCommands } from '../commands.js';
6
6
  import { COLORS } from '../consts.js';
7
7
  import { INERTIA_COLOR, LINK_GREY, LINK_STROKE_WIDTH, NODE_FILL, NODE_STROKE, WARDLEY_RED, } from '../node/consts.js';
8
+ import { WARDLEY_AREA_SIZE, wardleyAreaProps } from '../presets.js';
8
9
  import { WARDLEY_ROLE } from '../roles.js';
9
10
  import { wardleyMaps } from './maps.js';
10
11
  /**
@@ -73,17 +74,36 @@ const arrowPreview = (rightwards) => `<svg ${ATTRS} fill="none"><path d="${right
73
74
  : 'M103 32 H55 V23 L32 40 L55 57 V48 H103 Z'}" fill="#bfbfbf" stroke="${NODE_STROKE}" stroke-width="1.8" stroke-linejoin="round"/></svg>`;
74
75
  /** The zone, in its Peace wash — the pair differs on the outline and on nothing else. */
75
76
  const areaPreview = (outline) => `<svg ${ATTRS} fill="none">${outline}</svg>`;
77
+ /**
78
+ * The three hand-drawn palette swatches' own tile names (`Template.nameKey`):
79
+ * none derives from a command, since `linkTool` / `evolutionArrow` /
80
+ * `addAreaPolygon` activate a tool rather than draw anything (see `tpl` below
81
+ * and its docstring).
82
+ */
83
+ export const WARDLEY_TEMPLATE_NAME_LINK = [
84
+ 'com.labre.wardley.template.link',
85
+ 'Link',
86
+ ];
87
+ export const WARDLEY_TEMPLATE_NAME_EVOLUTION_ARROW = [
88
+ 'com.labre.wardley.template.evolution-arrow',
89
+ 'Evolution arrow',
90
+ ];
91
+ export const WARDLEY_TEMPLATE_NAME_AREA_POLYGON = [
92
+ 'com.labre.wardley.template.area-polygon',
93
+ 'Area (polygon)',
94
+ ];
76
95
  /** A hand-composed template — what is left once the artefacts are derived. */
77
- function tpl(name, preview, elements) {
96
+ function tpl(name, preview, elements, nameKey) {
78
97
  return {
79
98
  name,
80
99
  type: 'template',
81
100
  preview,
101
+ nameKey,
82
102
  content: makeTemplateSnapshot(elements, name),
83
103
  };
84
104
  }
85
105
  /**
86
- * A zone, plus the one thing a snapshot cannot say.
106
+ * A zone card, plus the one thing a snapshot cannot say.
87
107
  *
88
108
  * `createWardleyArea` lowers the zone the moment it exists, to just above the
89
109
  * framework backgrounds it covers — otherwise the wash sits on top of every
@@ -91,14 +111,16 @@ function tpl(name, preview, elements) {
91
111
  * ALREADY ON THE BOARD, which a snapshot knows nothing about, so the panel
92
112
  * replays it on the freshly inserted element instead.
93
113
  */
94
- function areaTemplate(id, preview) {
114
+ function areaTemplate(template) {
95
115
  return {
96
- ...templateFromCommand(byId(id), preview),
116
+ ...template,
97
117
  afterInsert: (std, insertedIds) => lowerWardleyArea(std.get(GfxControllerIdentifier), insertedIds[0]),
98
118
  };
99
119
  }
100
120
  export const wardleyTemplateCategory = {
101
121
  name: 'Wardley',
122
+ // Reuses the senior button's own key — see `TemplateCategory.nameKey`.
123
+ nameKey: 'com.labre.framework.wardley',
102
124
  templates: [
103
125
  ...wardleyMaps,
104
126
  templateFromCommand(byId('wardley.addBackground'), bgPreview(), 'Map background'),
@@ -126,11 +148,21 @@ export const wardleyTemplateCategory = {
126
148
  // artefact to record — the user draws it.
127
149
  tpl('Link', `<svg ${ATTRS} fill="none"><path d="M24 40 H110" stroke="${LINK_GREY}" stroke-width="2.4"/></svg>`, {
128
150
  a: connect({ position: [0, 0] }, { position: [160, 0] }, { typed: false }),
129
- }),
151
+ }, WARDLEY_TEMPLATE_NAME_LINK[0]),
130
152
  tpl('Evolution arrow', `<svg ${ATTRS} fill="none"><path d="M24 40 H100" stroke="${WARDLEY_RED}" stroke-width="2.4" stroke-dasharray="6 4"/><path d="M98 33 L112 40 L98 47 Z" fill="${WARDLEY_RED}"/></svg>`, {
131
153
  a: connect({ position: [0, 0] }, { position: [160, 0] }, { evolution: true, typed: false }),
132
- }),
133
- areaTemplate('wardley.addAreaRect', areaPreview('<rect x="24" y="16" width="87" height="48" rx="2" fill="#c6dbfc" fill-opacity="0.25" stroke="#5b9cf6" stroke-width="1.5"/>')),
134
- areaTemplate('wardley.addAreaPolygon', areaPreview('<path d="M67 12 L110 43 L94 68 H40 L24 43 Z" fill="#c6dbfc" fill-opacity="0.25" stroke="#5b9cf6" stroke-width="1.5" stroke-linejoin="round"/>')),
154
+ }, WARDLEY_TEMPLATE_NAME_EVOLUTION_ARROW[0]),
155
+ areaTemplate(templateFromCommand(byId('wardley.addAreaRect'), areaPreview('<rect x="24" y="16" width="87" height="48" rx="2" fill="#c6dbfc" fill-opacity="0.25" stroke="#5b9cf6" stroke-width="1.5"/>'))),
156
+ // Hand-composed, like the two connector swatches and for the same reason:
157
+ // `wardley.addAreaPolygon` arms the polygon tool (#343) and draws nothing,
158
+ // so there is no action to record. A panel card cannot arm a tool — it
159
+ // inserts a snapshot — so the card keeps offering the zone ready-made, and
160
+ // the author who wants to place the corners uses the sub-menu. The props
161
+ // still come from the pack, so the two zones cannot disagree.
162
+ areaTemplate(tpl(WARDLEY_TEMPLATE_NAME_AREA_POLYGON[1], areaPreview('<path d="M67 12 L110 43 L94 68 H40 L24 43 Z" fill="#c6dbfc" fill-opacity="0.25" stroke="#5b9cf6" stroke-width="1.5" stroke-linejoin="round"/>'), {
163
+ area: wardleyAreaProps('polygon', {
164
+ xywh: `[0,0,${WARDLEY_AREA_SIZE.polygon.w},${WARDLEY_AREA_SIZE.polygon.h}]`,
165
+ }),
166
+ }, WARDLEY_TEMPLATE_NAME_AREA_POLYGON[0])),
135
167
  ],
136
168
  };
@@ -1,2 +1,96 @@
1
1
  import { type Template } from '@formicoidea/labre-core/gfx/template';
2
+ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The seeds the two worked maps write — declared once so the builders below
5
+ * and the manifest (`../translations.ts`) read the very same fallback, never
6
+ * a restated literal. `com.labre.wardley.example.<map>.<slug>`, per
7
+ * `docs/adr/0023`.
8
+ */
9
+ export declare const TEA_SHOP_SEED: {
10
+ readonly title: {
11
+ readonly key: "com.labre.wardley.example.tea-shop.title";
12
+ readonly fallback: "Tea Shop";
13
+ };
14
+ readonly annotations: {
15
+ readonly key: "com.labre.wardley.example.tea-shop.annotations";
16
+ readonly fallback: "Annotations:\n1. Standardising power lets kettles evolve faster\n2. Hot water is obvious and well known";
17
+ };
18
+ readonly business: {
19
+ readonly key: "com.labre.wardley.example.tea-shop.business";
20
+ readonly fallback: "Business";
21
+ };
22
+ readonly public: {
23
+ readonly key: "com.labre.wardley.example.tea-shop.public";
24
+ readonly fallback: "Public";
25
+ };
26
+ readonly cupOfTea: {
27
+ readonly key: "com.labre.wardley.example.tea-shop.cup-of-tea";
28
+ readonly fallback: "Cup of Tea";
29
+ };
30
+ readonly cup: {
31
+ readonly key: "com.labre.wardley.example.tea-shop.cup";
32
+ readonly fallback: "Cup";
33
+ };
34
+ readonly tea: {
35
+ readonly key: "com.labre.wardley.example.tea-shop.tea";
36
+ readonly fallback: "Tea";
37
+ };
38
+ readonly hotWater: {
39
+ readonly key: "com.labre.wardley.example.tea-shop.hot-water";
40
+ readonly fallback: "Hot Water";
41
+ };
42
+ readonly water: {
43
+ readonly key: "com.labre.wardley.example.tea-shop.water";
44
+ readonly fallback: "Water";
45
+ };
46
+ readonly kettle: {
47
+ readonly key: "com.labre.wardley.example.tea-shop.kettle";
48
+ readonly fallback: "Kettle";
49
+ };
50
+ readonly electricKettle: {
51
+ readonly key: "com.labre.wardley.example.tea-shop.electric-kettle";
52
+ readonly fallback: "Electric Kettle";
53
+ };
54
+ readonly power: {
55
+ readonly key: "com.labre.wardley.example.tea-shop.power";
56
+ readonly fallback: "Power";
57
+ };
58
+ readonly limitedBy: {
59
+ readonly key: "com.labre.wardley.example.tea-shop.limited-by";
60
+ readonly fallback: "limited by";
61
+ };
62
+ };
63
+ export declare const KODAK_INERTIA_SEED: {
64
+ readonly title: {
65
+ readonly key: "com.labre.wardley.example.kodak-inertia.title";
66
+ readonly fallback: "Wardley map of Kodak's 2005 inertia to digital";
67
+ };
68
+ readonly user: {
69
+ readonly key: "com.labre.wardley.example.kodak-inertia.user";
70
+ readonly fallback: "User";
71
+ };
72
+ readonly captureAMoment: {
73
+ readonly key: "com.labre.wardley.example.kodak-inertia.capture-a-moment";
74
+ readonly fallback: "Capture a moment";
75
+ };
76
+ readonly filmCamera: {
77
+ readonly key: "com.labre.wardley.example.kodak-inertia.film-camera";
78
+ readonly fallback: "Film camera";
79
+ };
80
+ readonly digitalCamera: {
81
+ readonly key: "com.labre.wardley.example.kodak-inertia.digital-camera";
82
+ readonly fallback: "Digital camera";
83
+ };
84
+ readonly photographicFilm: {
85
+ readonly key: "com.labre.wardley.example.kodak-inertia.photographic-film";
86
+ readonly fallback: "Photographic film";
87
+ };
88
+ readonly digitalStorage: {
89
+ readonly key: "com.labre.wardley.example.kodak-inertia.digital-storage";
90
+ readonly fallback: "Digital storage";
91
+ };
92
+ };
93
+ /** The two worked maps' own tile names (`Template.nameKey`). */
94
+ export declare const WARDLEY_TEMPLATE_NAME_TEA_SHOP: ChromeWording;
95
+ export declare const WARDLEY_TEMPLATE_NAME_KODAK_INERTIA: ChromeWording;
2
96
  export declare const wardleyMaps: Template[];
@@ -1,5 +1,6 @@
1
1
  import { makeTemplateSnapshot, surfaceText, surfaceYMap, } from '@formicoidea/labre-core/gfx/template';
2
2
  import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
3
+ import { translateKey, } from '@formicoidea/labre-core/shared/services';
3
4
  import { COLORS } from '../consts.js';
4
5
  import { INERTIA_COLOR, LINK_GREY, LINK_STROKE_WIDTH, NODE_FILL, NODE_SIZE, NODE_STROKE, WARDLEY_RED, } from '../node/consts.js';
5
6
  import { backgroundPlot, backgroundZoneBoundaries, } from '@formicoidea/labre-core/blocks/surface';
@@ -217,14 +218,119 @@ function title(str) {
217
218
  }
218
219
  const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
219
220
  const mapPreview = (extra) => `<svg ${ATTRS} fill="none"><path d="M22 12 V64 H120" stroke="${COLORS.axis}" stroke-width="2"/>${extra}</svg>`;
220
- function tpl(name, preview, elements) {
221
+ /**
222
+ * The seeds the two worked maps write — declared once so the builders below
223
+ * and the manifest (`../translations.ts`) read the very same fallback, never
224
+ * a restated literal. `com.labre.wardley.example.<map>.<slug>`, per
225
+ * `docs/adr/0023`.
226
+ */
227
+ export const TEA_SHOP_SEED = {
228
+ title: {
229
+ key: 'com.labre.wardley.example.tea-shop.title',
230
+ fallback: 'Tea Shop',
231
+ },
232
+ annotations: {
233
+ key: 'com.labre.wardley.example.tea-shop.annotations',
234
+ fallback: 'Annotations:\n1. Standardising power lets kettles evolve faster\n2. Hot water is obvious and well known',
235
+ },
236
+ business: {
237
+ key: 'com.labre.wardley.example.tea-shop.business',
238
+ fallback: 'Business',
239
+ },
240
+ public: {
241
+ key: 'com.labre.wardley.example.tea-shop.public',
242
+ fallback: 'Public',
243
+ },
244
+ cupOfTea: {
245
+ key: 'com.labre.wardley.example.tea-shop.cup-of-tea',
246
+ fallback: 'Cup of Tea',
247
+ },
248
+ cup: { key: 'com.labre.wardley.example.tea-shop.cup', fallback: 'Cup' },
249
+ tea: { key: 'com.labre.wardley.example.tea-shop.tea', fallback: 'Tea' },
250
+ hotWater: {
251
+ key: 'com.labre.wardley.example.tea-shop.hot-water',
252
+ fallback: 'Hot Water',
253
+ },
254
+ water: { key: 'com.labre.wardley.example.tea-shop.water', fallback: 'Water' },
255
+ kettle: {
256
+ key: 'com.labre.wardley.example.tea-shop.kettle',
257
+ fallback: 'Kettle',
258
+ },
259
+ electricKettle: {
260
+ key: 'com.labre.wardley.example.tea-shop.electric-kettle',
261
+ fallback: 'Electric Kettle',
262
+ },
263
+ power: { key: 'com.labre.wardley.example.tea-shop.power', fallback: 'Power' },
264
+ limitedBy: {
265
+ key: 'com.labre.wardley.example.tea-shop.limited-by',
266
+ fallback: 'limited by',
267
+ },
268
+ };
269
+ export const KODAK_INERTIA_SEED = {
270
+ title: {
271
+ key: 'com.labre.wardley.example.kodak-inertia.title',
272
+ fallback: "Wardley map of Kodak's 2005 inertia to digital",
273
+ },
274
+ user: {
275
+ key: 'com.labre.wardley.example.kodak-inertia.user',
276
+ fallback: 'User',
277
+ },
278
+ captureAMoment: {
279
+ key: 'com.labre.wardley.example.kodak-inertia.capture-a-moment',
280
+ fallback: 'Capture a moment',
281
+ },
282
+ filmCamera: {
283
+ key: 'com.labre.wardley.example.kodak-inertia.film-camera',
284
+ fallback: 'Film camera',
285
+ },
286
+ digitalCamera: {
287
+ key: 'com.labre.wardley.example.kodak-inertia.digital-camera',
288
+ fallback: 'Digital camera',
289
+ },
290
+ photographicFilm: {
291
+ key: 'com.labre.wardley.example.kodak-inertia.photographic-film',
292
+ fallback: 'Photographic film',
293
+ },
294
+ digitalStorage: {
295
+ key: 'com.labre.wardley.example.kodak-inertia.digital-storage',
296
+ fallback: 'Digital storage',
297
+ },
298
+ };
299
+ /**
300
+ * One seed's resolved text — the host's catalogue when `std` is a real
301
+ * inserting editor, the English fallback when building the module's own
302
+ * `content` (no editor exists yet at that point).
303
+ */
304
+ function seedText(std, def) {
305
+ return std ? translateKey(std, def.key, def.fallback) : def.fallback;
306
+ }
307
+ /**
308
+ * A shipped map — what is left once the artefacts are derived.
309
+ *
310
+ * `build` takes the OPTIONAL inserting editor: called with none, at module
311
+ * load, for the English `content`; called again with the real `std` as
312
+ * {@link Template.localize}, so the two builds read the very same layout and
313
+ * differ only in the words a seed resolves to (`docs/adr/0023`).
314
+ */
315
+ function tpl(name, preview, build, nameKey) {
221
316
  return {
222
317
  name,
223
318
  type: 'template',
224
319
  preview,
225
- content: makeTemplateSnapshot(elements, name),
320
+ nameKey,
321
+ content: makeTemplateSnapshot(build(), name),
322
+ localize: std => makeTemplateSnapshot(build(std), name),
226
323
  };
227
324
  }
325
+ /** The two worked maps' own tile names (`Template.nameKey`). */
326
+ export const WARDLEY_TEMPLATE_NAME_TEA_SHOP = [
327
+ 'com.labre.wardley.template.tea-shop',
328
+ 'Tea Shop',
329
+ ];
330
+ export const WARDLEY_TEMPLATE_NAME_KODAK_INERTIA = [
331
+ 'com.labre.wardley.template.kodak-inertia',
332
+ 'Kodak inertia',
333
+ ];
228
334
  function ann(e, v) {
229
335
  const cx = ex(e);
230
336
  const cy = vy(v);
@@ -254,48 +360,56 @@ function annTxt(e, v, n) {
254
360
  };
255
361
  }
256
362
  // ── Tea Shop (the canonical map) ──────────────────────────────────────
257
- function teaShop() {
363
+ function teaShop(std) {
364
+ const s = (def) => seedText(std, def);
258
365
  return {
259
366
  bg: bg(),
260
- title: title('Tea Shop'),
367
+ title: title(s(TEA_SHOP_SEED.title)),
261
368
  annBox: panel(120, 200, 420, 64),
262
- annText: freeText(132, 208, 400, 'Annotations:\n1. Standardising power lets kettles evolve faster\n2. Hot water is obvious and well known', 13),
369
+ annText: freeText(132, 208, 400, s(TEA_SHOP_SEED.annotations), 13),
263
370
  business: stake(0.62, 0.93),
264
- businessL: lbl(0.62, 0.93, 'Business', {
371
+ businessL: lbl(0.62, 0.93, s(TEA_SHOP_SEED.business), {
265
372
  align: 'center',
266
373
  dy: -28,
267
374
  w: 120,
268
375
  }),
269
376
  businessG: pair('business', 'businessL'),
270
377
  public: stake(0.78, 0.93),
271
- publicL: lbl(0.78, 0.93, 'Public', { align: 'center', dy: -28, w: 120 }),
378
+ publicL: lbl(0.78, 0.93, s(TEA_SHOP_SEED.public), {
379
+ align: 'center',
380
+ dy: -28,
381
+ w: 120,
382
+ }),
272
383
  publicG: pair('public', 'publicL'),
273
384
  cupOfTea: comp(0.62, 0.74),
274
- cupOfTeaL: lbl(0.62, 0.74, 'Cup of Tea', { align: 'right' }),
385
+ cupOfTeaL: lbl(0.62, 0.74, s(TEA_SHOP_SEED.cupOfTea), { align: 'right' }),
275
386
  cupOfTeaG: pair('cupOfTea', 'cupOfTeaL'),
276
387
  cup: comp(0.8, 0.7),
277
- cupL: lbl(0.8, 0.7, 'Cup'),
388
+ cupL: lbl(0.8, 0.7, s(TEA_SHOP_SEED.cup)),
278
389
  cupG: pair('cup', 'cupL'),
279
390
  tea: comp(0.83, 0.6),
280
- teaL: lbl(0.83, 0.6, 'Tea'),
391
+ teaL: lbl(0.83, 0.6, s(TEA_SHOP_SEED.tea)),
281
392
  teaG: pair('tea', 'teaL'),
282
393
  hotWater: comp(0.8, 0.47),
283
- hotWaterL: lbl(0.8, 0.47, 'Hot Water'),
394
+ hotWaterL: lbl(0.8, 0.47, s(TEA_SHOP_SEED.hotWater)),
284
395
  hotWaterG: pair('hotWater', 'hotWaterL'),
285
396
  water: comp(0.81, 0.34),
286
- waterL: lbl(0.81, 0.34, 'Water'),
397
+ waterL: lbl(0.81, 0.34, s(TEA_SHOP_SEED.water)),
287
398
  waterG: pair('water', 'waterL'),
288
399
  kettle: comp(0.36, 0.38),
289
- kettleL: lbl(0.36, 0.38, 'Kettle', { align: 'right', dy: 6 }),
400
+ kettleL: lbl(0.36, 0.38, s(TEA_SHOP_SEED.kettle), {
401
+ align: 'right',
402
+ dy: 6,
403
+ }),
290
404
  kettleG: pair('kettle', 'kettleL'),
291
405
  electric: future(0.56, 0.38),
292
- electricL: lbl(0.56, 0.38, 'Electric Kettle'),
406
+ electricL: lbl(0.56, 0.38, s(TEA_SHOP_SEED.electricKettle)),
293
407
  electricG: pair('electric', 'electricL'),
294
408
  power: comp(0.7, 0.1),
295
- powerL: lbl(0.7, 0.1, 'Power', { align: 'right', dy: 6 }),
409
+ powerL: lbl(0.7, 0.1, s(TEA_SHOP_SEED.power), { align: 'right', dy: 6 }),
296
410
  powerG: pair('power', 'powerL'),
297
411
  powerFut: future(0.88, 0.1),
298
- powerFutL: lbl(0.88, 0.1, 'Power'),
412
+ powerFutL: lbl(0.88, 0.1, s(TEA_SHOP_SEED.power)),
299
413
  powerFutG: pair('powerFut', 'powerFutL'),
300
414
  // ABOVE the link it annotates, not across it. Written on the line it reads
301
415
  // as a label nobody can read — which is the finding W3 raises, and it was
@@ -303,7 +417,7 @@ function teaShop() {
303
417
  //
304
418
  // NEUTRAL: it is a remark about a LINK, not the name of an artefact, so it
305
419
  // travels with nothing and nothing measures it (see `LblOpts.neutral`).
306
- limitedBy: lbl(0.56, 0.43, 'limited by', {
420
+ limitedBy: lbl(0.56, 0.43, s(TEA_SHOP_SEED.limitedBy), {
307
421
  align: 'center',
308
422
  w: 120,
309
423
  size: 13,
@@ -327,7 +441,8 @@ function teaShop() {
327
441
  };
328
442
  }
329
443
  // ── Kodak inertia (2005) ──────────────────────────────────────────────
330
- function kodak() {
444
+ function kodak(std) {
445
+ const s = (def) => seedText(std, def);
331
446
  // The future dependency `capture → storage` is the movement Kodak resisted,
332
447
  // and the inertia bar belongs where that dependency crosses into commodity —
333
448
  // the boundary the capability refused to cross. It used to sit 105 units away
@@ -341,30 +456,36 @@ function kodak() {
341
456
  const [barE, barV] = crossing(CAPTURE, STORAGE, 2);
342
457
  return {
343
458
  bg: bg(),
344
- title: title("Wardley map of Kodak's 2005 inertia to digital"),
459
+ title: title(s(KODAK_INERTIA_SEED.title)),
345
460
  user: stake(0.54, 0.92),
346
- userL: lbl(0.54, 0.92, 'User'),
461
+ userL: lbl(0.54, 0.92, s(KODAK_INERTIA_SEED.user)),
347
462
  userG: pair('user', 'userL'),
348
463
  capture: dot(CAPTURE[0], CAPTURE[1], 3),
349
464
  // To the LEFT, like the other two capability names: to the right of this
350
465
  // node runs the future dependency towards digital storage, and a name
351
466
  // written across the line that carries the whole argument is exactly the
352
467
  // case W3 exists for.
353
- captureL: lbl(CAPTURE[0], CAPTURE[1], 'Capture a moment', {
468
+ captureL: lbl(CAPTURE[0], CAPTURE[1], s(KODAK_INERTIA_SEED.captureAMoment), {
354
469
  align: 'right',
355
470
  }),
356
471
  captureG: pair('capture', 'captureL'),
357
472
  film: comp(0.52, 0.62),
358
- filmL: lbl(0.52, 0.62, 'Film camera', { align: 'right' }),
473
+ filmL: lbl(0.52, 0.62, s(KODAK_INERTIA_SEED.filmCamera), {
474
+ align: 'right',
475
+ }),
359
476
  filmG: pair('film', 'filmL'),
360
477
  digital: future(0.74, 0.62),
361
- digitalL: lbl(0.74, 0.62, 'Digital camera', { color: WARDLEY_RED }),
478
+ digitalL: lbl(0.74, 0.62, s(KODAK_INERTIA_SEED.digitalCamera), {
479
+ color: WARDLEY_RED,
480
+ }),
362
481
  digitalG: pair('digital', 'digitalL'),
363
482
  roll: comp(0.52, 0.4),
364
- rollL: lbl(0.52, 0.4, 'Photographic film', { align: 'right' }),
483
+ rollL: lbl(0.52, 0.4, s(KODAK_INERTIA_SEED.photographicFilm), {
484
+ align: 'right',
485
+ }),
365
486
  rollG: pair('roll', 'rollL'),
366
487
  storage: future(STORAGE[0], STORAGE[1]),
367
- storageL: lbl(STORAGE[0], STORAGE[1], 'Digital storage', {
488
+ storageL: lbl(STORAGE[0], STORAGE[1], s(KODAK_INERTIA_SEED.digitalStorage), {
368
489
  color: WARDLEY_RED,
369
490
  }),
370
491
  storageG: pair('storage', 'storageL'),
@@ -378,6 +499,6 @@ function kodak() {
378
499
  };
379
500
  }
380
501
  export const wardleyMaps = [
381
- tpl('Tea Shop', mapPreview(`<circle cx="78" cy="24" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="50" cy="44" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="86" cy="40" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="92" cy="58" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><path d="M78 24 L50 44 M78 24 L86 40 L92 58" stroke="${LINK_GREY}"/><path d="M50 44 h22" stroke="${WARDLEY_RED}" stroke-dasharray="3 2"/>`), teaShop()),
382
- tpl('Kodak inertia', mapPreview(`<circle cx="56" cy="22" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="54" cy="40" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="86" cy="40" r="3" fill="${NODE_FILL}" stroke="${WARDLEY_RED}"/><rect x="76" y="35" width="2.5" height="11" fill="${INERTIA_COLOR}"/><path d="M57 40 h17" stroke="${WARDLEY_RED}" stroke-dasharray="3 2"/>`), kodak()),
502
+ tpl('Tea Shop', mapPreview(`<circle cx="78" cy="24" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="50" cy="44" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="86" cy="40" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="92" cy="58" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><path d="M78 24 L50 44 M78 24 L86 40 L92 58" stroke="${LINK_GREY}"/><path d="M50 44 h22" stroke="${WARDLEY_RED}" stroke-dasharray="3 2"/>`), teaShop, WARDLEY_TEMPLATE_NAME_TEA_SHOP[0]),
503
+ tpl('Kodak inertia', mapPreview(`<circle cx="56" cy="22" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="54" cy="40" r="3" fill="${NODE_FILL}" stroke="${NODE_STROKE}"/><circle cx="86" cy="40" r="3" fill="${NODE_FILL}" stroke="${WARDLEY_RED}"/><rect x="76" y="35" width="2.5" height="11" fill="${INERTIA_COLOR}"/><path d="M57 40 h17" stroke="${WARDLEY_RED}" stroke-dasharray="3 2"/>`), kodak, WARDLEY_TEMPLATE_NAME_KODAK_INERTIA[0]),
383
504
  ];
@@ -1,5 +1,7 @@
1
- import { type ChromeWording, type ToolbarContext } from '@formicoidea/labre-core/shared/services';
1
+ import { type ChromeWording, type ToolbarContext, type ToolbarModuleConfig } from '@formicoidea/labre-core/shared/services';
2
2
  import { type TemplateResult } from 'lit';
3
+ /** Every wording above, for `translations.ts`'s manifest contribution. */
4
+ export declare const WARDLEY_TOOLBAR_WORDINGS: readonly ChromeWording[];
3
5
  export declare const wardleyToolbarConfig: {
4
6
  readonly actions: [{
5
7
  id: string;
@@ -71,12 +73,37 @@ export declare const wardleyToolbarConfig: {
71
73
  active(ctx: ToolbarContext): boolean;
72
74
  run(ctx: ToolbarContext): void;
73
75
  }];
74
- }, {
75
- readonly id: "d.legend";
76
- readonly tooltipWording: ChromeWording;
77
- readonly icon: TemplateResult<2>;
78
- readonly run: (ctx: ToolbarContext) => void;
79
- }];
76
+ }, import("@formicoidea/labre-core/shared/services").ToolbarActionGenerator];
80
77
  readonly when: (ctx: ToolbarContext) => boolean;
81
78
  };
82
79
  export declare const wardleyToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
80
+ /**
81
+ * The map's flag-gated row, WHOLE: the Legend button and the Validation
82
+ * dropdown, in one module.
83
+ *
84
+ * ## Why they cannot be two modules
85
+ *
86
+ * `renderToolbar` merges exactly four slots per element — `<flavour>`,
87
+ * `custom:<flavour>` and the two `affine:surface:*` wildcards — and
88
+ * `ToolbarModuleExtension` binds by DI variant, so a second module claiming
89
+ * `custom:affine:surface:wardley` would throw
90
+ * `DuplicateServiceDefinitionError` before the editor finished setting up. Two
91
+ * slots, and Wardley has three things to put on a selected map: the axis and
92
+ * label toggles (always-on, `<flavour>`, above), the legend and the level of
93
+ * requirement. The precedent is C4's `c4BoardToolingToolbarConfig`.
94
+ *
95
+ * ## Why the legend moved OUT of the always-on half
96
+ *
97
+ * Because GENERATING one is tooling, and `docs/adr/0009` gates tooling: the
98
+ * flag off takes away the gesture that writes legend elements, exactly as it
99
+ * takes away the sub-menu that draws components. What the gesture already
100
+ * wrote is document CONTENT and keeps being painted — the box is plain shapes,
101
+ * text and `wardleyNode`s with no role, all drawn by the always-on renderers.
102
+ * The axis and label toggles stay always-on for the opposite reason: a stored
103
+ * map must keep its axes whatever the flag says.
104
+ *
105
+ * Sorting keeps the row readable across the merge — `a.` … `d.legend` from the
106
+ * two modules, then `z.validation` — whatever order they were registered in.
107
+ */
108
+ export declare const wardleyBoardToolingToolbarConfig: ToolbarModuleConfig;
109
+ export declare const wardleyBoardToolingToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;