@formicoidea/labre-framework-cynefin 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.
- package/LICENSE +384 -0
- package/dist/commands.js +5 -1
- package/dist/cynefin/consts.d.ts +55 -12
- package/dist/cynefin/consts.js +281 -34
- package/dist/cynefin/element-renderer.d.ts +16 -0
- package/dist/cynefin/element-renderer.js +60 -23
- package/dist/cynefin/element-view.d.ts +11 -2
- package/dist/cynefin/element-view.js +17 -2
- package/dist/cynefin/labels.d.ts +25 -0
- package/dist/cynefin/labels.js +40 -0
- package/dist/cynefin/toolbar/config.d.ts +2 -0
- package/dist/cynefin/toolbar/config.js +22 -3
- package/dist/estuarine/consts.d.ts +29 -4
- package/dist/estuarine/consts.js +44 -4
- package/dist/estuarine/element-renderer.js +14 -8
- package/dist/estuarine/element-view.d.ts +10 -2
- package/dist/estuarine/element-view.js +17 -2
- package/dist/estuarine/labels.d.ts +39 -0
- package/dist/estuarine/labels.js +49 -0
- package/dist/estuarine/toolbar/config.d.ts +2 -0
- package/dist/estuarine/toolbar/config.js +28 -4
- package/dist/templates/index.d.ts +39 -0
- package/dist/templates/index.js +123 -16
- package/dist/toolbar/palette.d.ts +31 -0
- package/dist/toolbar/palette.js +105 -0
- package/dist/toolbar/senior-button.d.ts +2 -0
- package/dist/toolbar/senior-button.js +6 -2
- package/dist/translations.js +52 -1
- package/dist/view.js +7 -0
- package/package.json +2 -2
package/dist/cynefin/consts.js
CHANGED
|
@@ -2,9 +2,28 @@
|
|
|
2
2
|
* Visual constants for the Liminal Cynefin diagram, reproduced from the official
|
|
3
3
|
* SVG (viewBox 0 0 1080 777). All geometry is authored in that fixed reference
|
|
4
4
|
* space and scaled uniformly to the element bounds by the renderer.
|
|
5
|
+
*
|
|
6
|
+
* ## i18n
|
|
7
|
+
*
|
|
8
|
+
* Every word painted on the diagram — domain headings and subheadings, the
|
|
9
|
+
* Probe/Sense/Respond decision lines, the teal annotations, the small
|
|
10
|
+
* exaptation sub-labels and the two central markers — carries a `…Key`
|
|
11
|
+
* alongside its English text, resolved by `element-renderer.ts` through the
|
|
12
|
+
* `CanvasRenderer` it is handed at paint time (the 4th argument every
|
|
13
|
+
* `ElementRenderer` receives, `renderer.std`). The letters "A" and "C" on the
|
|
14
|
+
* two markers are notation, not words, and are deliberately NOT keyed (PO
|
|
15
|
+
* decision) — only their spelled-out NAMES ("Aporia", "Confusion") are.
|
|
5
16
|
*/
|
|
6
17
|
export const REF_W = 1080;
|
|
7
18
|
export const REF_H = 777;
|
|
19
|
+
/**
|
|
20
|
+
* Type size of a domain heading, in reference units.
|
|
21
|
+
*
|
|
22
|
+
* Named because two things read it: the renderer, which draws the word, and the
|
|
23
|
+
* hit test that offers it to a double-click. A size that lived only in the
|
|
24
|
+
* renderer's font string would let the target drift away from the word.
|
|
25
|
+
*/
|
|
26
|
+
export const HEADING_SIZE = 30;
|
|
8
27
|
export const COLORS = {
|
|
9
28
|
boundary: '#333333',
|
|
10
29
|
teal: '#2a9d99',
|
|
@@ -84,94 +103,322 @@ export const DASH_RECTS = [
|
|
|
84
103
|
[998, 314, 11.9, 168.9],
|
|
85
104
|
[1019, 310, 11.8, 167.3],
|
|
86
105
|
];
|
|
106
|
+
/* ── i18n: headings, subheadings and decision lines ───────────────────── */
|
|
107
|
+
const HEADING_COMPLEX = [
|
|
108
|
+
'com.labre.cynefin-estuarine.cynefin.heading.complex',
|
|
109
|
+
'Complex',
|
|
110
|
+
];
|
|
111
|
+
const HEADING_COMPLICATED = [
|
|
112
|
+
'com.labre.cynefin-estuarine.cynefin.heading.complicated',
|
|
113
|
+
'Complicated',
|
|
114
|
+
];
|
|
115
|
+
const HEADING_CHAOTIC = [
|
|
116
|
+
'com.labre.cynefin-estuarine.cynefin.heading.chaotic',
|
|
117
|
+
'Chaotic',
|
|
118
|
+
];
|
|
119
|
+
const HEADING_CLEAR = [
|
|
120
|
+
'com.labre.cynefin-estuarine.cynefin.heading.clear',
|
|
121
|
+
'Clear',
|
|
122
|
+
];
|
|
123
|
+
const SUBHEADING_ADAPTIVE = [
|
|
124
|
+
'com.labre.cynefin-estuarine.cynefin.subheading.adaptive-system',
|
|
125
|
+
'Adaptive system',
|
|
126
|
+
];
|
|
127
|
+
/** Shared by Complicated and Clear — the same word, one key. */
|
|
128
|
+
const SUBHEADING_ORDERED = [
|
|
129
|
+
'com.labre.cynefin-estuarine.cynefin.subheading.ordered-system',
|
|
130
|
+
'Ordered system',
|
|
131
|
+
];
|
|
132
|
+
const SUBHEADING_UNORDERED = [
|
|
133
|
+
'com.labre.cynefin-estuarine.cynefin.subheading.un-ordered-system',
|
|
134
|
+
'Un-ordered system',
|
|
135
|
+
];
|
|
136
|
+
/**
|
|
137
|
+
* The ten distinct decision sentences (two are shared verbatim between two
|
|
138
|
+
* domains — "Sense how the context reacts", "Sense the context with
|
|
139
|
+
* analytical methods" — one key each, reused rather than duplicated).
|
|
140
|
+
*
|
|
141
|
+
* The fallback is the FULL sentence (`lead` + `rest` concatenated): the
|
|
142
|
+
* renderer draws `lead` bold and `rest` roman as two `fillText` calls only
|
|
143
|
+
* while NO host answered (byte-identical to before these keys existed); once
|
|
144
|
+
* a host resolves the key, the whole sentence is drawn as one run — a
|
|
145
|
+
* translation is not guaranteed to keep the same first word, so splitting it
|
|
146
|
+
* at the English boundary would be a guess.
|
|
147
|
+
*/
|
|
148
|
+
const LINE_PROBE = [
|
|
149
|
+
'com.labre.cynefin-estuarine.cynefin.line.probe',
|
|
150
|
+
'Probe the context with parallel experiments',
|
|
151
|
+
];
|
|
152
|
+
const LINE_SENSE_REACTS = [
|
|
153
|
+
'com.labre.cynefin-estuarine.cynefin.line.sense-reacts',
|
|
154
|
+
'Sense how the context reacts',
|
|
155
|
+
];
|
|
156
|
+
const LINE_RESPOND_AMPLIFY = [
|
|
157
|
+
'com.labre.cynefin-estuarine.cynefin.line.respond-amplify',
|
|
158
|
+
'Respond by amplifying positive experiments',
|
|
159
|
+
];
|
|
160
|
+
const LINE_SENSE_ANALYTICAL = [
|
|
161
|
+
'com.labre.cynefin-estuarine.cynefin.line.sense-analytical',
|
|
162
|
+
'Sense the context with analytical methods',
|
|
163
|
+
];
|
|
164
|
+
const LINE_ANALYSE = [
|
|
165
|
+
'com.labre.cynefin-estuarine.cynefin.line.analyse',
|
|
166
|
+
'Analyse observations',
|
|
167
|
+
];
|
|
168
|
+
const LINE_RESPOND_MANY_SOLUTIONS = [
|
|
169
|
+
'com.labre.cynefin-estuarine.cynefin.line.respond-many-solutions',
|
|
170
|
+
'Respond by applying one of many good solutions',
|
|
171
|
+
];
|
|
172
|
+
const LINE_ACT = [
|
|
173
|
+
'com.labre.cynefin-estuarine.cynefin.line.act',
|
|
174
|
+
'Act on the context to stabilize (it or yourself)',
|
|
175
|
+
];
|
|
176
|
+
const LINE_RESPOND_REACT = [
|
|
177
|
+
'com.labre.cynefin-estuarine.cynefin.line.respond-react',
|
|
178
|
+
'Respond by re-acting',
|
|
179
|
+
];
|
|
180
|
+
const LINE_CATEGORIZE = [
|
|
181
|
+
'com.labre.cynefin-estuarine.cynefin.line.categorize',
|
|
182
|
+
'Categorize observations',
|
|
183
|
+
];
|
|
184
|
+
const LINE_RESPOND_PRACTICES = [
|
|
185
|
+
'com.labre.cynefin-estuarine.cynefin.line.respond-practices',
|
|
186
|
+
'Respond by applying tried and true practices',
|
|
187
|
+
];
|
|
87
188
|
export const DOMAINS = [
|
|
88
189
|
{
|
|
89
|
-
heading:
|
|
190
|
+
heading: HEADING_COMPLEX,
|
|
191
|
+
prop: 'complexTitle',
|
|
90
192
|
x: 37,
|
|
91
193
|
hy: 31,
|
|
92
|
-
subheading:
|
|
194
|
+
subheading: SUBHEADING_ADAPTIVE,
|
|
93
195
|
sy: 53,
|
|
94
196
|
lines: [
|
|
95
|
-
{
|
|
96
|
-
|
|
97
|
-
|
|
197
|
+
{
|
|
198
|
+
lead: 'Probe',
|
|
199
|
+
rest: ' the context with parallel experiments',
|
|
200
|
+
wording: LINE_PROBE,
|
|
201
|
+
y: 71,
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
lead: 'Sense',
|
|
205
|
+
rest: ' how the context reacts',
|
|
206
|
+
wording: LINE_SENSE_REACTS,
|
|
207
|
+
y: 90,
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
lead: 'Respond',
|
|
211
|
+
rest: ' by amplifying positive experiments',
|
|
212
|
+
wording: LINE_RESPOND_AMPLIFY,
|
|
213
|
+
y: 109,
|
|
214
|
+
},
|
|
98
215
|
],
|
|
99
216
|
},
|
|
100
217
|
{
|
|
101
|
-
heading:
|
|
218
|
+
heading: HEADING_COMPLICATED,
|
|
219
|
+
prop: 'complicatedTitle',
|
|
102
220
|
x: 779,
|
|
103
221
|
hy: 31,
|
|
104
|
-
subheading:
|
|
222
|
+
subheading: SUBHEADING_ORDERED,
|
|
105
223
|
sy: 53,
|
|
106
224
|
lines: [
|
|
107
|
-
{
|
|
108
|
-
|
|
225
|
+
{
|
|
226
|
+
lead: 'Sense',
|
|
227
|
+
rest: ' the context with analytical methods',
|
|
228
|
+
wording: LINE_SENSE_ANALYTICAL,
|
|
229
|
+
y: 71,
|
|
230
|
+
},
|
|
231
|
+
{ lead: 'Analyse', rest: ' observations', wording: LINE_ANALYSE, y: 90 },
|
|
109
232
|
{
|
|
110
233
|
lead: 'Respond',
|
|
111
234
|
rest: ' by applying one of many good solutions',
|
|
235
|
+
wording: LINE_RESPOND_MANY_SOLUTIONS,
|
|
112
236
|
y: 109,
|
|
113
237
|
},
|
|
114
238
|
],
|
|
115
239
|
},
|
|
116
240
|
{
|
|
117
|
-
heading:
|
|
241
|
+
heading: HEADING_CHAOTIC,
|
|
242
|
+
prop: 'chaoticTitle',
|
|
118
243
|
x: 37,
|
|
119
244
|
hy: 587,
|
|
120
|
-
subheading:
|
|
245
|
+
subheading: SUBHEADING_UNORDERED,
|
|
121
246
|
sy: 609,
|
|
122
247
|
lines: [
|
|
123
248
|
{
|
|
124
249
|
lead: 'Act',
|
|
125
250
|
rest: ' on the context to stabilize (it or yourself)',
|
|
251
|
+
wording: LINE_ACT,
|
|
126
252
|
y: 627,
|
|
127
253
|
},
|
|
128
|
-
{
|
|
129
|
-
|
|
254
|
+
{
|
|
255
|
+
lead: 'Sense',
|
|
256
|
+
rest: ' how the context reacts',
|
|
257
|
+
wording: LINE_SENSE_REACTS,
|
|
258
|
+
y: 646,
|
|
259
|
+
},
|
|
260
|
+
{
|
|
261
|
+
lead: 'Respond',
|
|
262
|
+
rest: ' by re-acting',
|
|
263
|
+
wording: LINE_RESPOND_REACT,
|
|
264
|
+
y: 665,
|
|
265
|
+
},
|
|
130
266
|
],
|
|
131
267
|
},
|
|
132
268
|
{
|
|
133
|
-
heading:
|
|
269
|
+
heading: HEADING_CLEAR,
|
|
270
|
+
prop: 'clearTitle',
|
|
134
271
|
x: 779,
|
|
135
272
|
hy: 587,
|
|
136
|
-
subheading:
|
|
273
|
+
subheading: SUBHEADING_ORDERED,
|
|
137
274
|
sy: 609,
|
|
138
275
|
lines: [
|
|
139
|
-
{
|
|
140
|
-
|
|
276
|
+
{
|
|
277
|
+
lead: 'Sense',
|
|
278
|
+
rest: ' the context with analytical methods',
|
|
279
|
+
wording: LINE_SENSE_ANALYTICAL,
|
|
280
|
+
y: 627,
|
|
281
|
+
},
|
|
282
|
+
{
|
|
283
|
+
lead: 'Categorize',
|
|
284
|
+
rest: ' observations',
|
|
285
|
+
wording: LINE_CATEGORIZE,
|
|
286
|
+
y: 646,
|
|
287
|
+
},
|
|
141
288
|
{
|
|
142
289
|
lead: 'Respond',
|
|
143
290
|
rest: ' by applying tried and true practices',
|
|
291
|
+
wording: LINE_RESPOND_PRACTICES,
|
|
144
292
|
y: 665,
|
|
145
293
|
},
|
|
146
294
|
],
|
|
147
295
|
},
|
|
148
296
|
];
|
|
149
|
-
|
|
297
|
+
/* ── i18n: teal annotations and small exaptation sub-labels ────────────── */
|
|
298
|
+
const TEAL_ITERATE = [
|
|
299
|
+
'com.labre.cynefin-estuarine.cynefin.teal.iterate',
|
|
300
|
+
'iterate',
|
|
301
|
+
];
|
|
302
|
+
const TEAL_STRATEGY_BY_DESIGN = [
|
|
303
|
+
'com.labre.cynefin-estuarine.cynefin.teal.strategy-by-design',
|
|
304
|
+
'strategy by design',
|
|
305
|
+
];
|
|
306
|
+
const TEAL_RADICAL_INNOVATION = [
|
|
307
|
+
'com.labre.cynefin-estuarine.cynefin.teal.radical-innovation',
|
|
308
|
+
'radical innovation',
|
|
309
|
+
];
|
|
310
|
+
const TEAL_BY_DESIGN = [
|
|
311
|
+
'com.labre.cynefin-estuarine.cynefin.teal.by-design',
|
|
312
|
+
'by design',
|
|
313
|
+
];
|
|
314
|
+
const TEAL_EXTREME_REPURPOSING = [
|
|
315
|
+
'com.labre.cynefin-estuarine.cynefin.teal.extreme-repurposing',
|
|
316
|
+
'extreme repurposing',
|
|
317
|
+
];
|
|
318
|
+
const TEAL_GOOD_PRACTICE = [
|
|
319
|
+
'com.labre.cynefin-estuarine.cynefin.teal.good-practice',
|
|
320
|
+
'good practice',
|
|
321
|
+
];
|
|
322
|
+
const TEAL_BEST_PRACTICE = [
|
|
323
|
+
'com.labre.cynefin-estuarine.cynefin.teal.best-practice',
|
|
324
|
+
'best practice',
|
|
325
|
+
];
|
|
326
|
+
/** Teal annotation labels (centered): [wording, x, y]. */
|
|
150
327
|
export const TEAL_LABELS = [
|
|
151
|
-
[
|
|
152
|
-
[
|
|
153
|
-
[
|
|
154
|
-
[
|
|
155
|
-
[
|
|
156
|
-
[
|
|
157
|
-
[
|
|
158
|
-
[
|
|
159
|
-
];
|
|
160
|
-
|
|
328
|
+
[TEAL_ITERATE, 510, 16],
|
|
329
|
+
[TEAL_ITERATE, 533, 230],
|
|
330
|
+
[TEAL_STRATEGY_BY_DESIGN, 257, 225],
|
|
331
|
+
[TEAL_RADICAL_INNOVATION, 268, 390],
|
|
332
|
+
[TEAL_BY_DESIGN, 268, 406],
|
|
333
|
+
[TEAL_EXTREME_REPURPOSING, 217, 496],
|
|
334
|
+
[TEAL_GOOD_PRACTICE, 814, 196],
|
|
335
|
+
[TEAL_BEST_PRACTICE, 751, 459],
|
|
336
|
+
];
|
|
337
|
+
const SMALL_DISPOSITIONAL = [
|
|
338
|
+
'com.labre.cynefin-estuarine.cynefin.small.dispositional-exaptation',
|
|
339
|
+
'dispositional exaptation',
|
|
340
|
+
];
|
|
341
|
+
const SMALL_STIMULATED = [
|
|
342
|
+
'com.labre.cynefin-estuarine.cynefin.small.stimulated-exaptation',
|
|
343
|
+
'stimulated exaptation',
|
|
344
|
+
];
|
|
345
|
+
const SMALL_STRESS_BASED = [
|
|
346
|
+
'com.labre.cynefin-estuarine.cynefin.small.stress-based-exaptation',
|
|
347
|
+
'stress-based exaptation',
|
|
348
|
+
];
|
|
349
|
+
/** Small exaptation sub-labels (centered): [wording, x, y]. */
|
|
161
350
|
export const SMALL_LABELS = [
|
|
162
|
-
[
|
|
163
|
-
[
|
|
164
|
-
[
|
|
351
|
+
[SMALL_DISPOSITIONAL, 257, 239],
|
|
352
|
+
[SMALL_STIMULATED, 268, 419],
|
|
353
|
+
[SMALL_STRESS_BASED, 217, 510],
|
|
354
|
+
];
|
|
355
|
+
/* ── i18n: the two central markers ──────────────────────────────────── */
|
|
356
|
+
const MARKER_APORIA_NAME = [
|
|
357
|
+
'com.labre.cynefin-estuarine.cynefin.marker.aporia',
|
|
358
|
+
'Aporia',
|
|
359
|
+
];
|
|
360
|
+
const MARKER_CONFUSION_NAME = [
|
|
361
|
+
'com.labre.cynefin-estuarine.cynefin.marker.confusion',
|
|
362
|
+
'Confusion',
|
|
363
|
+
];
|
|
364
|
+
const MARKER_APORIA_NOTE = [
|
|
365
|
+
'com.labre.cynefin-estuarine.cynefin.marker.aporia-note',
|
|
366
|
+
'prepare to exit',
|
|
165
367
|
];
|
|
166
368
|
export const MARKERS = [
|
|
167
369
|
{
|
|
168
370
|
letter: 'A',
|
|
169
371
|
lx: 444,
|
|
170
372
|
ly: 334,
|
|
171
|
-
name:
|
|
373
|
+
name: MARKER_APORIA_NAME,
|
|
172
374
|
nx: 447,
|
|
173
375
|
ny: 349,
|
|
174
|
-
note: {
|
|
376
|
+
note: { wording: MARKER_APORIA_NOTE, x: 449, y: 366 },
|
|
377
|
+
},
|
|
378
|
+
{
|
|
379
|
+
letter: 'C',
|
|
380
|
+
lx: 531,
|
|
381
|
+
ly: 419,
|
|
382
|
+
name: MARKER_CONFUSION_NAME,
|
|
383
|
+
nx: 529,
|
|
384
|
+
ny: 436,
|
|
175
385
|
},
|
|
176
|
-
|
|
386
|
+
];
|
|
387
|
+
/**
|
|
388
|
+
* Every wording this file paints, for `translations.ts`'s manifest
|
|
389
|
+
* contribution. Deduplicated by key (`iterate` and the two shared lines are
|
|
390
|
+
* each listed once), which is what `mergeTranslationEntries` needs to see one
|
|
391
|
+
* entry per key rather than two identical ones.
|
|
392
|
+
*/
|
|
393
|
+
export const CYNEFIN_CANVAS_WORDINGS = [
|
|
394
|
+
HEADING_COMPLEX,
|
|
395
|
+
HEADING_COMPLICATED,
|
|
396
|
+
HEADING_CHAOTIC,
|
|
397
|
+
HEADING_CLEAR,
|
|
398
|
+
SUBHEADING_ADAPTIVE,
|
|
399
|
+
SUBHEADING_ORDERED,
|
|
400
|
+
SUBHEADING_UNORDERED,
|
|
401
|
+
LINE_PROBE,
|
|
402
|
+
LINE_SENSE_REACTS,
|
|
403
|
+
LINE_RESPOND_AMPLIFY,
|
|
404
|
+
LINE_SENSE_ANALYTICAL,
|
|
405
|
+
LINE_ANALYSE,
|
|
406
|
+
LINE_RESPOND_MANY_SOLUTIONS,
|
|
407
|
+
LINE_ACT,
|
|
408
|
+
LINE_RESPOND_REACT,
|
|
409
|
+
LINE_CATEGORIZE,
|
|
410
|
+
LINE_RESPOND_PRACTICES,
|
|
411
|
+
TEAL_ITERATE,
|
|
412
|
+
TEAL_STRATEGY_BY_DESIGN,
|
|
413
|
+
TEAL_RADICAL_INNOVATION,
|
|
414
|
+
TEAL_BY_DESIGN,
|
|
415
|
+
TEAL_EXTREME_REPURPOSING,
|
|
416
|
+
TEAL_GOOD_PRACTICE,
|
|
417
|
+
TEAL_BEST_PRACTICE,
|
|
418
|
+
SMALL_DISPOSITIONAL,
|
|
419
|
+
SMALL_STIMULATED,
|
|
420
|
+
SMALL_STRESS_BASED,
|
|
421
|
+
MARKER_APORIA_NAME,
|
|
422
|
+
MARKER_CONFUSION_NAME,
|
|
423
|
+
MARKER_APORIA_NOTE,
|
|
177
424
|
];
|
|
@@ -7,6 +7,22 @@ import type { CynefinElementModel } from '@formicoidea/labre-core/model';
|
|
|
7
7
|
* (heading + Probe/Sense/Respond decisions), the teal annotation labels and the
|
|
8
8
|
* central Aporia (A) / Confusion (C) markers. Drawn in the fixed reference space
|
|
9
9
|
* and scaled uniformly to the element bounds.
|
|
10
|
+
*
|
|
11
|
+
* ## i18n
|
|
12
|
+
*
|
|
13
|
+
* Every word above is a `ChromeWording` (`./consts.ts`), resolved through the
|
|
14
|
+
* `CanvasRenderer` this function is handed as its 4th argument — the same
|
|
15
|
+
* `renderer.std` `framework-background/renderer.ts` reads for a declared
|
|
16
|
+
* background. `renderer` is optional (absent in a bare unit test that calls
|
|
17
|
+
* this function with three arguments), in which case every wording reads its
|
|
18
|
+
* English fallback, byte-identical to before these keys existed.
|
|
19
|
+
*
|
|
20
|
+
* The three-line decisions are the one wrinkle: with no host they are drawn as
|
|
21
|
+
* they always were — a bold lead word plus the roman remainder, two
|
|
22
|
+
* `fillText` calls — because that split is known to be exactly the English
|
|
23
|
+
* text. Once a host answers, the WHOLE sentence is drawn as one run: a
|
|
24
|
+
* translation is not guaranteed to keep the same first word, so guessing where
|
|
25
|
+
* to split it would be inventing typography the translator never asked for.
|
|
10
26
|
*/
|
|
11
27
|
export declare const cynefin: ElementRenderer<CynefinElementModel>;
|
|
12
28
|
export declare const CynefinRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
-
import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { ElementRendererExtension, taggedPath2D, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { TranslationProvider, translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
2
3
|
import { FONT_FAMILY, refScale } from '../utils.js';
|
|
3
|
-
import { COLORS, DARK_BACK_PATHS, DARK_FRONT_PATHS, DASH_RECTS, DOMAINS, HATCHES, MARKERS, REF_H, REF_W, SMALL_LABELS, TEAL_LABELS, TEAL_PATH, TEAL_WIDTH, } from './consts.js';
|
|
4
|
+
import { COLORS, DARK_BACK_PATHS, DARK_FRONT_PATHS, DASH_RECTS, DOMAINS, HATCHES, HEADING_SIZE, MARKERS, REF_H, REF_W, SMALL_LABELS, TEAL_LABELS, TEAL_PATH, TEAL_WIDTH, } from './consts.js';
|
|
5
|
+
import { cynefinHeadingText } from './labels.js';
|
|
4
6
|
/**
|
|
5
7
|
* Canvas renderer for the Liminal Cynefin diagram — reproduces the official SVG:
|
|
6
8
|
* the dark hand-drawn boundary, the teal "iterate" curve, the dashed
|
|
@@ -8,8 +10,29 @@ import { COLORS, DARK_BACK_PATHS, DARK_FRONT_PATHS, DASH_RECTS, DOMAINS, HATCHES
|
|
|
8
10
|
* (heading + Probe/Sense/Respond decisions), the teal annotation labels and the
|
|
9
11
|
* central Aporia (A) / Confusion (C) markers. Drawn in the fixed reference space
|
|
10
12
|
* and scaled uniformly to the element bounds.
|
|
13
|
+
*
|
|
14
|
+
* ## i18n
|
|
15
|
+
*
|
|
16
|
+
* Every word above is a `ChromeWording` (`./consts.ts`), resolved through the
|
|
17
|
+
* `CanvasRenderer` this function is handed as its 4th argument — the same
|
|
18
|
+
* `renderer.std` `framework-background/renderer.ts` reads for a declared
|
|
19
|
+
* background. `renderer` is optional (absent in a bare unit test that calls
|
|
20
|
+
* this function with three arguments), in which case every wording reads its
|
|
21
|
+
* English fallback, byte-identical to before these keys existed.
|
|
22
|
+
*
|
|
23
|
+
* The three-line decisions are the one wrinkle: with no host they are drawn as
|
|
24
|
+
* they always were — a bold lead word plus the roman remainder, two
|
|
25
|
+
* `fillText` calls — because that split is known to be exactly the English
|
|
26
|
+
* text. Once a host answers, the WHOLE sentence is drawn as one run: a
|
|
27
|
+
* translation is not guaranteed to keep the same first word, so guessing where
|
|
28
|
+
* to split it would be inventing typography the translator never asked for.
|
|
11
29
|
*/
|
|
12
|
-
export const cynefin = (model, ctx, matrix) => {
|
|
30
|
+
export const cynefin = (model, ctx, matrix, renderer) => {
|
|
31
|
+
const tr = (wording) => renderer ? translateKey(renderer.std, ...wording) : wording[1];
|
|
32
|
+
// The host's catalogue, or `null` with no host — used to decide whether a
|
|
33
|
+
// decision line is drawn bold-lead/roman-rest (English, exactly as before)
|
|
34
|
+
// or as one translated run. See the docstring above.
|
|
35
|
+
const hasHost = Boolean(renderer?.std.getOptional(TranslationProvider));
|
|
13
36
|
const [, , w, h] = model.deserializedXYWH;
|
|
14
37
|
const cx = w / 2;
|
|
15
38
|
const cy = h / 2;
|
|
@@ -26,7 +49,7 @@ export const cynefin = (model, ctx, matrix) => {
|
|
|
26
49
|
for (const [d, lw, miter] of DARK_BACK_PATHS) {
|
|
27
50
|
ctx.lineJoin = miter ? 'miter' : 'round';
|
|
28
51
|
ctx.lineWidth = lw;
|
|
29
|
-
ctx.stroke(
|
|
52
|
+
ctx.stroke(taggedPath2D(d));
|
|
30
53
|
}
|
|
31
54
|
ctx.lineJoin = 'round';
|
|
32
55
|
// ── Dashed boundary (oriented square pavings) ───────────────────────
|
|
@@ -42,14 +65,14 @@ export const cynefin = (model, ctx, matrix) => {
|
|
|
42
65
|
if (model.showLiminalLine) {
|
|
43
66
|
ctx.strokeStyle = COLORS.teal;
|
|
44
67
|
ctx.lineWidth = TEAL_WIDTH;
|
|
45
|
-
ctx.stroke(
|
|
68
|
+
ctx.stroke(taggedPath2D(TEAL_PATH));
|
|
46
69
|
}
|
|
47
70
|
// ── Dark boundary strokes (over the teal curve) ─────────────────────
|
|
48
71
|
ctx.strokeStyle = COLORS.boundary;
|
|
49
72
|
for (const [d, lw, miter] of DARK_FRONT_PATHS) {
|
|
50
73
|
ctx.lineJoin = miter ? 'miter' : 'round';
|
|
51
74
|
ctx.lineWidth = lw;
|
|
52
|
-
ctx.stroke(
|
|
75
|
+
ctx.stroke(taggedPath2D(d));
|
|
53
76
|
}
|
|
54
77
|
ctx.lineJoin = 'round';
|
|
55
78
|
// ── Cliff hatching ──────────────────────────────────────────────────
|
|
@@ -65,52 +88,66 @@ export const cynefin = (model, ctx, matrix) => {
|
|
|
65
88
|
if (model.showTitles) {
|
|
66
89
|
ctx.textAlign = 'left';
|
|
67
90
|
ctx.fillStyle = COLORS.heading;
|
|
68
|
-
ctx.font = `700
|
|
69
|
-
|
|
70
|
-
|
|
91
|
+
ctx.font = `700 ${HEADING_SIZE}px ${FONT_FAMILY}`;
|
|
92
|
+
// The user's own word when they have renamed this domain, the catalogue's
|
|
93
|
+
// otherwise — the same precedence a declared background applies.
|
|
94
|
+
for (const d of DOMAINS) {
|
|
95
|
+
ctx.fillText(cynefinHeadingText(model, d, tr), d.x, d.hy);
|
|
96
|
+
}
|
|
71
97
|
ctx.textAlign = 'center';
|
|
72
98
|
for (const m of MARKERS) {
|
|
73
99
|
ctx.fillStyle = COLORS.body;
|
|
74
100
|
ctx.font = `700 38px ${FONT_FAMILY}`;
|
|
101
|
+
// The letter itself is notation, not a word — never translated.
|
|
75
102
|
ctx.fillText(m.letter, m.lx, m.ly);
|
|
76
103
|
ctx.font = `13.5px ${FONT_FAMILY}`;
|
|
77
|
-
ctx.fillText(m.name, m.nx, m.ny);
|
|
104
|
+
ctx.fillText(tr(m.name), m.nx, m.ny);
|
|
78
105
|
}
|
|
79
106
|
}
|
|
80
107
|
// ── Explanatory text: subheadings, decisions, annotations, notes ────
|
|
81
108
|
if (model.showDescriptions) {
|
|
82
|
-
// Subheadings (h2) +
|
|
109
|
+
// Subheadings (h2) + decision lines
|
|
83
110
|
ctx.textAlign = 'left';
|
|
84
111
|
for (const d of DOMAINS) {
|
|
85
112
|
ctx.fillStyle = COLORS.heading;
|
|
86
113
|
ctx.font = `700 15px ${FONT_FAMILY}`;
|
|
87
|
-
ctx.fillText(d.subheading, d.x, d.sy);
|
|
114
|
+
ctx.fillText(tr(d.subheading), d.x, d.sy);
|
|
88
115
|
ctx.fillStyle = COLORS.body;
|
|
89
|
-
for (const { lead, rest, y } of d.lines) {
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
116
|
+
for (const { lead, rest, wording, y } of d.lines) {
|
|
117
|
+
if (hasHost) {
|
|
118
|
+
// A translated sentence: one run, no assumed bold-lead boundary.
|
|
119
|
+
ctx.font = `13.5px ${FONT_FAMILY}`;
|
|
120
|
+
ctx.fillText(tr(wording), d.x, y);
|
|
121
|
+
}
|
|
122
|
+
else {
|
|
123
|
+
// No host: exactly the bold-lead / roman-rest split that shipped
|
|
124
|
+
// before these keys existed.
|
|
125
|
+
ctx.font = `700 13.5px ${FONT_FAMILY}`;
|
|
126
|
+
ctx.fillText(lead, d.x, y);
|
|
127
|
+
const leadW = ctx.measureText(lead).width;
|
|
128
|
+
ctx.font = `13.5px ${FONT_FAMILY}`;
|
|
129
|
+
ctx.fillText(rest, d.x + leadW, y);
|
|
130
|
+
}
|
|
95
131
|
}
|
|
96
132
|
}
|
|
97
133
|
ctx.textAlign = 'center';
|
|
98
134
|
// Teal annotation labels
|
|
99
135
|
ctx.fillStyle = COLORS.teal;
|
|
100
136
|
ctx.font = `700 15px ${FONT_FAMILY}`;
|
|
101
|
-
for (const [
|
|
102
|
-
ctx.fillText(
|
|
137
|
+
for (const [wording, x, y] of TEAL_LABELS)
|
|
138
|
+
ctx.fillText(tr(wording), x, y);
|
|
103
139
|
// Small exaptation sub-labels
|
|
104
140
|
ctx.fillStyle = COLORS.body;
|
|
105
141
|
ctx.font = `10.5px ${FONT_FAMILY}`;
|
|
106
|
-
for (const [
|
|
107
|
-
ctx.fillText(
|
|
142
|
+
for (const [wording, x, y] of SMALL_LABELS) {
|
|
143
|
+
ctx.fillText(tr(wording), x, y);
|
|
144
|
+
}
|
|
108
145
|
// Marker notes ("prepare to exit")
|
|
109
146
|
ctx.fillStyle = COLORS.teal;
|
|
110
147
|
ctx.font = `700 15px ${FONT_FAMILY}`;
|
|
111
148
|
for (const m of MARKERS) {
|
|
112
149
|
if (m.note)
|
|
113
|
-
ctx.fillText(m.note.
|
|
150
|
+
ctx.fillText(tr(m.note.wording), m.note.x, m.note.y);
|
|
114
151
|
}
|
|
115
152
|
}
|
|
116
153
|
};
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
import type { EditableBackgroundLabel } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { FrameworkBackgroundView } from '@formicoidea/labre-core/blocks/surface';
|
|
1
3
|
import type { CynefinElementModel } from '@formicoidea/labre-core/model';
|
|
2
|
-
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
4
|
/**
|
|
4
5
|
* View for the Cynefin background. Registering it ensures `gfx.view.get(model)`
|
|
5
6
|
* returns a view (required so move / select interactions work).
|
|
7
|
+
*
|
|
8
|
+
* Extends {@link FrameworkBackgroundView} rather than
|
|
9
|
+
* `DeclaredBackgroundView`: this diagram is a figurative reproduction of the
|
|
10
|
+
* official SVG and has no `FrameworkBackgroundDef` behind it, so it says where
|
|
11
|
+
* its labels are in its own reference space. The gesture itself — where a
|
|
12
|
+
* double-click is allowed to land, the in-place `<input>`, the commit that
|
|
13
|
+
* refuses to write an untouched value — is the shared one (issue #355).
|
|
6
14
|
*/
|
|
7
|
-
export declare class CynefinView extends
|
|
15
|
+
export declare class CynefinView extends FrameworkBackgroundView<CynefinElementModel> {
|
|
8
16
|
static type: string;
|
|
17
|
+
protected labelAt(lx: number, ly: number, w: number, h: number): EditableBackgroundLabel | null;
|
|
9
18
|
}
|
|
10
19
|
/**
|
|
11
20
|
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
@@ -1,10 +1,25 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { FrameworkBackgroundView, hitTestBackgroundLabel, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
3
|
+
import { GfxViewInteractionExtension } from '@formicoidea/labre-core/std/gfx';
|
|
4
|
+
import { cynefinLabelHits } from './labels.js';
|
|
2
5
|
/**
|
|
3
6
|
* View for the Cynefin background. Registering it ensures `gfx.view.get(model)`
|
|
4
7
|
* returns a view (required so move / select interactions work).
|
|
8
|
+
*
|
|
9
|
+
* Extends {@link FrameworkBackgroundView} rather than
|
|
10
|
+
* `DeclaredBackgroundView`: this diagram is a figurative reproduction of the
|
|
11
|
+
* official SVG and has no `FrameworkBackgroundDef` behind it, so it says where
|
|
12
|
+
* its labels are in its own reference space. The gesture itself — where a
|
|
13
|
+
* double-click is allowed to land, the in-place `<input>`, the commit that
|
|
14
|
+
* refuses to write an untouched value — is the shared one (issue #355).
|
|
5
15
|
*/
|
|
6
|
-
export class CynefinView extends
|
|
16
|
+
export class CynefinView extends FrameworkBackgroundView {
|
|
7
17
|
static { this.type = 'cynefin'; }
|
|
18
|
+
labelAt(lx, ly, w, h) {
|
|
19
|
+
const translate = (wording) => translateKey(this.gfx.std, ...wording);
|
|
20
|
+
const hit = hitTestBackgroundLabel(cynefinLabelHits(this.model, w, h, translate), lx, ly);
|
|
21
|
+
return hit ? { prop: hit.prop, text: hit.text } : null;
|
|
22
|
+
}
|
|
8
23
|
}
|
|
9
24
|
/**
|
|
10
25
|
* Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import type { BackgroundLabelHit } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import type { CynefinElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import type { DomainBlock } from './consts.js';
|
|
5
|
+
/** Resolves a wording against the host's catalogue, or falls back to English. */
|
|
6
|
+
export type CynefinWordingResolver = (wording: ChromeWording) => string;
|
|
7
|
+
/**
|
|
8
|
+
* What a domain heading SAYS: the user's own word, else the vocabulary.
|
|
9
|
+
*
|
|
10
|
+
* The very precedence `backgroundLabelText` applies to a declared background —
|
|
11
|
+
* user text wins over the catalogue, the catalogue over the shipped English —
|
|
12
|
+
* spelled out here because this diagram has no declaration to apply it for it.
|
|
13
|
+
* An empty string is a real answer (the user erased the word), which is why the
|
|
14
|
+
* test is against `undefined` and not against falsiness.
|
|
15
|
+
*/
|
|
16
|
+
export declare function cynefinHeadingText(model: CynefinElementModel, block: DomainBlock, translate: CynefinWordingResolver): string;
|
|
17
|
+
/**
|
|
18
|
+
* Clickable boxes of the four domain headings, in ELEMENT-LOCAL coordinates.
|
|
19
|
+
*
|
|
20
|
+
* Built from the same reference positions and the same uniform letterbox fit
|
|
21
|
+
* the renderer draws them with, so the target tracks the word at any size, and
|
|
22
|
+
* empty while the headings are hidden — a label that is not painted may not be
|
|
23
|
+
* aimed at either.
|
|
24
|
+
*/
|
|
25
|
+
export declare function cynefinLabelHits(model: CynefinElementModel, w: number, h: number, translate: CynefinWordingResolver): BackgroundLabelHit[];
|