@formicoidea/labre-framework-cynefin 0.32.0 → 0.34.1

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/actions.js CHANGED
@@ -4,6 +4,7 @@ import { Bound } from '@formicoidea/labre-core/global/gfx';
4
4
  import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
5
5
  import { REF_H as CYN_H, REF_W as CYN_W } from './cynefin/consts.js';
6
6
  import { HEX_FILL, HEX_SIZE, HEX_STROKE, HEX_VERTICES, REF_H as EST_H, REF_W as EST_W, } from './estuarine/consts.js';
7
+ import { ESTUARINE_ROLE } from './estuarine/roles.js';
7
8
  /**
8
9
  * Creation actions for the Cynefin / Estuarine toolbox — lifted out of
9
10
  * `toolbar/menu.ts` by PF3. This is also the framework that emitted NO
@@ -38,6 +39,10 @@ export function createEstuarineMap(std) {
38
39
  const { centerX, centerY } = gfx.viewport;
39
40
  const id = gfx.surface.addElement({
40
41
  type: 'estuarine',
42
+ // The map is this framework's ROOT INSTANCE: the role is what makes the
43
+ // Map quality checklist reachable on it (WS4). Cynefin's background gets
44
+ // none, on purpose — see `estuarine/roles.ts`.
45
+ role: ESTUARINE_ROLE.map,
41
46
  xywh: new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize(),
42
47
  });
43
48
  finish(gfx, id);
@@ -49,6 +54,9 @@ export function createConstraintHexagon(std) {
49
54
  const { centerX: cx, centerY: cy } = gfx.viewport;
50
55
  const id = gfx.surface.addElement({
51
56
  type: 'shape',
57
+ // A hexagon is a plain polygon on the canvas: nothing about its geometry
58
+ // says "constraint". The role is the only place that does.
59
+ role: ESTUARINE_ROLE.constraint,
52
60
  shapeType: 'polygon',
53
61
  vertices: HEX_VERTICES,
54
62
  filled: true,
@@ -0,0 +1,18 @@
1
+ import type { ShortcutManifestEntry } from '@formicoidea/labre-core/std';
2
+ /**
3
+ * The Cynefin / Estuarine commands as SHORTCUT-MANIFEST rows — id, label, chord, scope,
4
+ * owner — and nothing else.
5
+ *
6
+ * DATA ONLY, and that is the whole point (`docs/adr/0008` § Packaging). A
7
+ * `CommandDescriptor` carries its `run`, so a host settings pane that imports
8
+ * the package entry to list names and chords drags the entire action graph —
9
+ * the import/export machinery, the surface and gfx deep paths — into its
10
+ * chunk. This module has type-only imports, so the published bundle exposes it
11
+ * as `./commands-manifest`: a few hundred bytes that reference nothing.
12
+ *
13
+ * GENERATED-SHAPED, hand-committed: `commands-manifest.unit.spec.ts` asserts
14
+ * row-for-row equality with `toShortcutManifestEntry` over {@link cynefinEstuarineCommands},
15
+ * so this file cannot drift from the commands it projects. Add a command and
16
+ * the test says exactly what to add here.
17
+ */
18
+ export declare const cynefinEstuarineCommandsManifest: ShortcutManifestEntry[];
@@ -0,0 +1,42 @@
1
+ /**
2
+ * The Cynefin / Estuarine commands as SHORTCUT-MANIFEST rows — id, label, chord, scope,
3
+ * owner — and nothing else.
4
+ *
5
+ * DATA ONLY, and that is the whole point (`docs/adr/0008` § Packaging). A
6
+ * `CommandDescriptor` carries its `run`, so a host settings pane that imports
7
+ * the package entry to list names and chords drags the entire action graph —
8
+ * the import/export machinery, the surface and gfx deep paths — into its
9
+ * chunk. This module has type-only imports, so the published bundle exposes it
10
+ * as `./commands-manifest`: a few hundred bytes that reference nothing.
11
+ *
12
+ * GENERATED-SHAPED, hand-committed: `commands-manifest.unit.spec.ts` asserts
13
+ * row-for-row equality with `toShortcutManifestEntry` over {@link cynefinEstuarineCommands},
14
+ * so this file cannot drift from the commands it projects. Add a command and
15
+ * the test says exactly what to add here.
16
+ */
17
+ export const cynefinEstuarineCommandsManifest = [
18
+ {
19
+ id: 'cynefin-estuarine.addCynefin',
20
+ owner: 'cynefin-estuarine',
21
+ labelKey: 'com.labre.commands.cynefin-estuarine.addCynefin',
22
+ labelFallback: 'Cynefin framework',
23
+ scope: 'edgeless',
24
+ defaultKeys: { mac: [], other: [] },
25
+ },
26
+ {
27
+ id: 'cynefin-estuarine.addEstuarineMap',
28
+ owner: 'cynefin-estuarine',
29
+ labelKey: 'com.labre.commands.cynefin-estuarine.addEstuarineMap',
30
+ labelFallback: 'Estuarine map',
31
+ scope: 'edgeless',
32
+ defaultKeys: { mac: [], other: [] },
33
+ },
34
+ {
35
+ id: 'cynefin-estuarine.addConstraintHexagon',
36
+ owner: 'cynefin-estuarine',
37
+ labelKey: 'com.labre.commands.cynefin-estuarine.addConstraintHexagon',
38
+ labelFallback: 'Hexagon node',
39
+ scope: 'edgeless',
40
+ defaultKeys: { mac: [], other: [] },
41
+ },
42
+ ];
@@ -19,7 +19,11 @@ export const COLORS = {
19
19
  */
20
20
  export const DARK_BACK_PATHS = [
21
21
  // Main arc: top segment (Complex|Complicated) then left segment (Complex|Chaotic)
22
- ['M 550.1 17 A 296 296 0 0 1 338 328.5 A 448.7 448.7 0 0 1 26 331', 15.5, false],
22
+ [
23
+ 'M 550.1 17 A 296 296 0 0 1 338 328.5 A 448.7 448.7 0 0 1 26 331',
24
+ 15.5,
25
+ false,
26
+ ],
23
27
  // Thin "Confusion" arc sweeping down towards the cliff
24
28
  ['M 649 294 C 644 382, 588 462, 440 506', 5, false],
25
29
  ];
@@ -34,7 +38,11 @@ export const DARK_FRONT_PATHS = [
34
38
  // Thick descending branch with the bottom elbow (right edge of the cliff)
35
39
  ['M 340 332 C 390 440, 437 525, 472 632 Q 479 658, 453 700', 15.5, true],
36
40
  // Thin left line (left edge of the cliff)
37
- ['M 345 356 C 372 440, 408 540, 413 655 C 414 685, 412 710, 412 738', 4, false],
41
+ [
42
+ 'M 345 356 C 372 440, 408 540, 413 655 C 414 685, 412 710, 412 738',
43
+ 4,
44
+ false,
45
+ ],
38
46
  ];
39
47
  /** Cliff hatching: [x1,y1,x2,y2], lineWidth 3. */
40
48
  export const HATCHES = [
@@ -51,14 +59,30 @@ export const HATCHES = [
51
59
  ];
52
60
  /** Dashed Complicated↔Clear boundary, as oriented square pavings: [x,y,size,rotateDeg]. */
53
61
  export const DASH_RECTS = [
54
- [511, 245, 13.8, 206.8], [530, 255.5, 13.7, 205.2], [549.5, 265.5, 13.6, 203.5],
55
- [569.5, 274.5, 13.5, 201.9], [590, 282.5, 13.5, 200.2], [610.5, 289.5, 13.4, 198.6],
56
- [631, 296, 13.3, 196.9], [673.5, 307, 13.2, 193.6], [695, 311.5, 13.1, 192.0],
57
- [716, 315.5, 13.0, 190.3], [738, 319, 12.9, 188.7], [759.5, 322, 12.8, 187.0],
58
- [781, 324.5, 12.8, 185.4], [803, 325.5, 12.7, 183.7], [824.5, 326.5, 12.6, 182.1],
59
- [846, 327.5, 12.5, 180.5], [868, 327.5, 12.4, 178.8], [889.5, 326.5, 12.3, 177.2],
60
- [912, 325.5, 12.2, 175.5], [933, 323.5, 12.1, 173.9], [954.5, 321.5, 12.1, 172.2],
61
- [976, 318, 12.0, 170.6], [998, 314, 11.9, 168.9], [1019, 310, 11.8, 167.3],
62
+ [511, 245, 13.8, 206.8],
63
+ [530, 255.5, 13.7, 205.2],
64
+ [549.5, 265.5, 13.6, 203.5],
65
+ [569.5, 274.5, 13.5, 201.9],
66
+ [590, 282.5, 13.5, 200.2],
67
+ [610.5, 289.5, 13.4, 198.6],
68
+ [631, 296, 13.3, 196.9],
69
+ [673.5, 307, 13.2, 193.6],
70
+ [695, 311.5, 13.1, 192.0],
71
+ [716, 315.5, 13.0, 190.3],
72
+ [738, 319, 12.9, 188.7],
73
+ [759.5, 322, 12.8, 187.0],
74
+ [781, 324.5, 12.8, 185.4],
75
+ [803, 325.5, 12.7, 183.7],
76
+ [824.5, 326.5, 12.6, 182.1],
77
+ [846, 327.5, 12.5, 180.5],
78
+ [868, 327.5, 12.4, 178.8],
79
+ [889.5, 326.5, 12.3, 177.2],
80
+ [912, 325.5, 12.2, 175.5],
81
+ [933, 323.5, 12.1, 173.9],
82
+ [954.5, 321.5, 12.1, 172.2],
83
+ [976, 318, 12.0, 170.6],
84
+ [998, 314, 11.9, 168.9],
85
+ [1019, 310, 11.8, 167.3],
62
86
  ];
63
87
  export const DOMAINS = [
64
88
  {
@@ -82,7 +106,11 @@ export const DOMAINS = [
82
106
  lines: [
83
107
  { lead: 'Sense', rest: ' the context with analytical methods', y: 71 },
84
108
  { lead: 'Analyse', rest: ' observations', y: 90 },
85
- { lead: 'Respond', rest: ' by applying one of many good solutions', y: 109 },
109
+ {
110
+ lead: 'Respond',
111
+ rest: ' by applying one of many good solutions',
112
+ y: 109,
113
+ },
86
114
  ],
87
115
  },
88
116
  {
@@ -92,7 +120,11 @@ export const DOMAINS = [
92
120
  subheading: 'Un-ordered system',
93
121
  sy: 609,
94
122
  lines: [
95
- { lead: 'Act', rest: ' on the context to stabilize (it or yourself)', y: 627 },
123
+ {
124
+ lead: 'Act',
125
+ rest: ' on the context to stabilize (it or yourself)',
126
+ y: 627,
127
+ },
96
128
  { lead: 'Sense', rest: ' how the context reacts', y: 646 },
97
129
  { lead: 'Respond', rest: ' by re-acting', y: 665 },
98
130
  ],
@@ -106,7 +138,11 @@ export const DOMAINS = [
106
138
  lines: [
107
139
  { lead: 'Sense', rest: ' the context with analytical methods', y: 627 },
108
140
  { lead: 'Categorize', rest: ' observations', y: 646 },
109
- { lead: 'Respond', rest: ' by applying tried and true practices', y: 665 },
141
+ {
142
+ lead: 'Respond',
143
+ rest: ' by applying tried and true practices',
144
+ y: 665,
145
+ },
110
146
  ],
111
147
  },
112
148
  ];
@@ -13,7 +13,10 @@ export const cynefin = (model, ctx, matrix) => {
13
13
  const [, , w, h] = model.deserializedXYWH;
14
14
  const cx = w / 2;
15
15
  const cy = h / 2;
16
- ctx.setTransform(matrix.translateSelf(cx, cy).rotateSelf(model.rotate).translateSelf(-cx, -cy));
16
+ ctx.setTransform(matrix
17
+ .translateSelf(cx, cy)
18
+ .rotateSelf(model.rotate)
19
+ .translateSelf(-cx, -cy));
17
20
  const { s, ox, oy } = refScale(w, h, REF_W, REF_H);
18
21
  ctx.translate(ox, oy);
19
22
  ctx.scale(s, s);
@@ -1,27 +1,31 @@
1
- import { type ToolbarContext } from '@formicoidea/labre-core/shared/services';
1
+ import { type ChromeWording, type ToolbarContext } from '@formicoidea/labre-core/shared/services';
2
2
  import { type TemplateResult } from 'lit';
3
3
  export declare const cynefinToolbarConfig: {
4
4
  readonly actions: [{
5
5
  id: string;
6
6
  tooltip: string;
7
+ tooltipWording: ChromeWording | undefined;
7
8
  icon: TemplateResult;
8
9
  active(ctx: ToolbarContext): boolean;
9
10
  run(ctx: ToolbarContext): void;
10
11
  }, {
11
12
  id: string;
12
13
  tooltip: string;
14
+ tooltipWording: ChromeWording | undefined;
13
15
  icon: TemplateResult;
14
16
  active(ctx: ToolbarContext): boolean;
15
17
  run(ctx: ToolbarContext): void;
16
18
  }, {
17
19
  id: string;
18
20
  tooltip: string;
21
+ tooltipWording: ChromeWording | undefined;
19
22
  icon: TemplateResult;
20
23
  active(ctx: ToolbarContext): boolean;
21
24
  run(ctx: ToolbarContext): void;
22
25
  }, {
23
26
  id: string;
24
27
  tooltip: string;
28
+ tooltipWording: ChromeWording | undefined;
25
29
  icon: TemplateResult;
26
30
  active(ctx: ToolbarContext): boolean;
27
31
  run(ctx: ToolbarContext): void;
@@ -1,16 +1,66 @@
1
1
  import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { CynefinElementModel } from '@formicoidea/labre-core/model';
3
- import { ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
3
+ import { BOARD_RESIZE_TOGGLE, ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
4
4
  import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
5
5
  import { html } from 'lit';
6
- const ResizeIcon = html `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M9 5H5v4M15 19h4v-4" /><path d="M5 5l6 6M19 19l-6-6" /></svg>`;
7
- const TitlesIcon = html `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 7h14M9 7v11" /></svg>`;
8
- const DescIcon = html `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 8h14M5 12h14M5 16h9" /></svg>`;
9
- const LiminalIcon = html `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round"><path d="M5 17 C9 6 15 6 19 7" /></svg>`;
6
+ const ResizeIcon = html `<svg
7
+ width="24"
8
+ height="24"
9
+ viewBox="0 0 24 24"
10
+ fill="none"
11
+ stroke="currentColor"
12
+ stroke-width="1.6"
13
+ stroke-linecap="round"
14
+ stroke-linejoin="round"
15
+ >
16
+ <path d="M9 5H5v4M15 19h4v-4" />
17
+ <path d="M5 5l6 6M19 19l-6-6" />
18
+ </svg>`;
19
+ const TitlesIcon = html `<svg
20
+ width="24"
21
+ height="24"
22
+ viewBox="0 0 24 24"
23
+ fill="none"
24
+ stroke="currentColor"
25
+ stroke-width="1.6"
26
+ stroke-linecap="round"
27
+ stroke-linejoin="round"
28
+ >
29
+ <path d="M5 7h14M9 7v11" />
30
+ </svg>`;
31
+ const DescIcon = html `<svg
32
+ width="24"
33
+ height="24"
34
+ viewBox="0 0 24 24"
35
+ fill="none"
36
+ stroke="currentColor"
37
+ stroke-width="1.6"
38
+ stroke-linecap="round"
39
+ stroke-linejoin="round"
40
+ >
41
+ <path d="M5 8h14M5 12h14M5 16h9" />
42
+ </svg>`;
43
+ const LiminalIcon = html `<svg
44
+ width="24"
45
+ height="24"
46
+ viewBox="0 0 24 24"
47
+ fill="none"
48
+ stroke="currentColor"
49
+ stroke-width="1.6"
50
+ stroke-linecap="round"
51
+ stroke-linejoin="round"
52
+ >
53
+ <path d="M5 17 C9 6 15 6 19 7" />
54
+ </svg>`;
10
55
  function booleanToggle(id, tooltip, icon, prop) {
11
56
  return {
12
57
  id,
13
- tooltip,
58
+ tooltip: typeof tooltip === 'string' ? tooltip : tooltip[1],
59
+ // The declared wording, when the caller gave one instead of a literal:
60
+ // `combine` resolves it against the host's catalogue and writes it over
61
+ // the English default above, so a playground with no catalogue reads
62
+ // exactly what it read before (#183).
63
+ tooltipWording: typeof tooltip === 'string' ? undefined : tooltip,
14
64
  icon,
15
65
  active(ctx) {
16
66
  const models = ctx.getSurfaceModelsByType(CynefinElementModel);
@@ -30,7 +80,7 @@ function booleanToggle(id, tooltip, icon, prop) {
30
80
  }
31
81
  export const cynefinToolbarConfig = {
32
82
  actions: [
33
- booleanToggle('a.toggle-resize', 'Enable / lock resizing', ResizeIcon, 'resizeEnabled'),
83
+ booleanToggle('a.toggle-resize', BOARD_RESIZE_TOGGLE, ResizeIcon, 'resizeEnabled'),
34
84
  booleanToggle('b.toggle-titles', 'Show / hide titles', TitlesIcon, 'showTitles'),
35
85
  booleanToggle('c.toggle-descriptions', 'Show / hide explanatory text', DescIcon, 'showDescriptions'),
36
86
  booleanToggle('d.toggle-liminal', 'Show / hide liminal line', LiminalIcon, 'showLiminalLine'),
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Visual constants for the Estuarine framework map, reproduced from the official
3
3
  * SVG (viewBox 0 0 690 801). All geometry is authored in that fixed reference
4
- * space and scaled uniformly to the element bounds by the renderer. The e axis
5
- * is vertical & double-headed (energy), the t axis horizontal & single-headed
4
+ * space; the renderer reads every coordinate below as a RATIO of it and maps it
5
+ * onto the element's real width and height independently, so a stretched map
6
+ * gets a longer time axis and a taller energy axis rather than the same drawing
7
+ * letterboxed (see `EstuarineFit` in `./element-renderer.ts`). The e axis is
8
+ * vertical & double-headed (energy), the t axis horizontal & single-headed
6
9
  * (time only flows one way).
7
10
  */
8
11
  export declare const REF_W = 690;
@@ -33,7 +36,11 @@ export declare const T_AXIS: {
33
36
  };
34
37
  export declare const AXIS_WIDTH = 8;
35
38
  /** Filled arrowhead triangles: [[tipX,tipY],[baseAX,baseAY],[baseBX,baseBY]]. */
36
- export declare const ARROWHEADS: ReadonlyArray<readonly [readonly [number, number], readonly [number, number], readonly [number, number]]>;
39
+ export declare const ARROWHEADS: ReadonlyArray<readonly [
40
+ readonly [number, number],
41
+ readonly [number, number],
42
+ readonly [number, number]
43
+ ]>;
37
44
  /** Liminal: green boundary rising gently then dipping at the right end. */
38
45
  export declare const LIMINAL_PATH = "M 63 193 C 67 192, 78 189, 85 188 C 92 187, 100 186, 107 185 C 114 184, 122 183, 129 183 C 136 183, 144 183, 151 183 C 158 183, 166 183, 173 183 C 180 183, 188 184, 195 185 C 202 186, 210 188, 217 189 C 224 190, 232 192, 239 194 C 246 196, 254 198, 261 201 C 268 204, 276 207, 283 210 C 290 213, 298 217, 305 220 C 312 223, 320 226, 327 230 C 334 234, 342 238, 349 242 C 356 246, 364 250, 371 255 C 378 260, 386 264, 393 269 C 400 274, 408 278, 415 283 C 422 288, 430 292, 437 297 C 444 302, 451 306, 458 310 C 465 314, 473 319, 480 323 C 487 327, 495 332, 502 335 C 509 338, 517 341, 524 343 C 531 345, 539 348, 546 349 C 553 350, 561 350, 568 350 C 575 350, 583 348, 590 346 C 597 344, 605 340, 612 336 C 619 332, 626 325, 633 319 C 640 313, 648 302, 651 298 C 654 294, 654 295, 654 294";
39
46
  export declare const LIMINAL_WIDTH = 4.5;
@@ -1,8 +1,11 @@
1
1
  /**
2
2
  * Visual constants for the Estuarine framework map, reproduced from the official
3
3
  * SVG (viewBox 0 0 690 801). All geometry is authored in that fixed reference
4
- * space and scaled uniformly to the element bounds by the renderer. The e axis
5
- * is vertical & double-headed (energy), the t axis horizontal & single-headed
4
+ * space; the renderer reads every coordinate below as a RATIO of it and maps it
5
+ * onto the element's real width and height independently, so a stretched map
6
+ * gets a longer time axis and a taller energy axis rather than the same drawing
7
+ * letterboxed (see `EstuarineFit` in `./element-renderer.ts`). The e axis is
8
+ * vertical & double-headed (energy), the t axis horizontal & single-headed
6
9
  * (time only flows one way).
7
10
  */
8
11
  export const REF_W = 690;
@@ -26,9 +29,21 @@ export const T_AXIS = { y: 649, x1: 28, x2: 616 };
26
29
  export const AXIS_WIDTH = 8;
27
30
  /** Filled arrowhead triangles: [[tipX,tipY],[baseAX,baseAY],[baseBX,baseBY]]. */
28
31
  export const ARROWHEADS = [
29
- [[43.5, 72], [30, 100], [57, 100]], // e — top
30
- [[43.5, 785], [30, 758], [57, 758]], // e — bottom
31
- [[643, 649], [613, 636], [613, 662]], // t — right
32
+ [
33
+ [43.5, 72],
34
+ [30, 100],
35
+ [57, 100],
36
+ ], // e — top
37
+ [
38
+ [43.5, 785],
39
+ [30, 758],
40
+ [57, 758],
41
+ ], // e — bottom
42
+ [
43
+ [643, 649],
44
+ [613, 636],
45
+ [613, 662],
46
+ ], // t — right
32
47
  ];
33
48
  /** Liminal: green boundary rising gently then dipping at the right end. */
34
49
  export const LIMINAL_PATH = 'M 63 193 C 67 192, 78 189, 85 188 C 92 187, 100 186, 107 185 C 114 184, 122 183, 129 183 C 136 183, 144 183, 151 183 C 158 183, 166 183, 173 183 C 180 183, 188 184, 195 185 C 202 186, 210 188, 217 189 C 224 190, 232 192, 239 194 C 246 196, 254 198, 261 201 C 268 204, 276 207, 283 210 C 290 213, 298 217, 305 220 C 312 223, 320 226, 327 230 C 334 234, 342 238, 349 242 C 356 246, 364 250, 371 255 C 378 260, 386 264, 393 269 C 400 274, 408 278, 415 283 C 422 288, 430 292, 437 297 C 444 302, 451 306, 458 310 C 465 314, 473 319, 480 323 C 487 327, 495 332, 502 335 C 509 338, 517 341, 524 343 C 531 345, 539 348, 546 349 C 553 350, 561 350, 568 350 C 575 350, 583 348, 590 346 C 597 344, 605 340, 612 336 C 619 332, 626 325, 633 319 C 640 313, 648 302, 651 298 C 654 294, 654 295, 654 294';
@@ -41,9 +56,27 @@ export const VOLATILE_PATH = 'M 58 446 C 61 447, 70 451, 76 454 C 82 457, 88 462
41
56
  export const VOLATILE_WIDTH = 5;
42
57
  /** Uppercase legends: anchored centre, alphabetic baseline, with letter-spacing. */
43
58
  export const LABELS = {
44
- counterfactual: { text: 'COUNTER FACTUAL', x: 422, y: 25, size: 20, color: COLORS.label },
45
- liminal: { text: 'LIMINAL', x: 316, y: 192, size: 18, color: COLORS.liminalLabel },
46
- volatile: { text: 'VOLATILE', x: 219, y: 783, size: 20, color: COLORS.volatile },
59
+ counterfactual: {
60
+ text: 'COUNTER FACTUAL',
61
+ x: 422,
62
+ y: 25,
63
+ size: 20,
64
+ color: COLORS.label,
65
+ },
66
+ liminal: {
67
+ text: 'LIMINAL',
68
+ x: 316,
69
+ y: 192,
70
+ size: 18,
71
+ color: COLORS.liminalLabel,
72
+ },
73
+ volatile: {
74
+ text: 'VOLATILE',
75
+ x: 219,
76
+ y: 783,
77
+ size: 20,
78
+ color: COLORS.volatile,
79
+ },
47
80
  };
48
81
  /** Italic Georgia axis letters (left-anchored, alphabetic baseline). */
49
82
  export const AXIS_LABELS = {
@@ -1,11 +1,117 @@
1
1
  import { type ElementRenderer } from '@formicoidea/labre-core/blocks/surface';
2
2
  import type { EstuarineElementModel } from '@formicoidea/labre-core/model';
3
+ /**
4
+ * The three reference curves are drawn as a permanent GHOST (PO arbitration,
5
+ * 26/08/2026): dashed, translucent, never solid again.
6
+ *
7
+ * The reasoning is what an Estuarine line IS. Liminal, Volatile and
8
+ * Counter-factual are not measurements — they are boundaries a group argues
9
+ * itself into, and the printed map is a support for that argument, not a
10
+ * verdict about where the boundary lies. A solid stroke made the tool's own
11
+ * curve look like the answer, and the group's negotiated line (drawn on top,
12
+ * with a brush or a connector) look like an annotation on it. Dashing the
13
+ * reference inverts that: the tool suggests, the group states.
14
+ *
15
+ * The toggles keep their meaning exactly — ON shows the ghost, OFF hides it.
16
+ * What changed is only how ON looks, plus the ~600 ms reveal animation the
17
+ * moment a toggle flips (see `./ghost-overlay.ts`), which exists so a line
18
+ * that comes back at 45 % opacity is still SEEN arriving.
19
+ *
20
+ * The legends stay solid. A label is a name, not a boundary.
21
+ */
22
+ /** Dash pattern of the ghost, in reference-space units. */
23
+ export declare const GHOST_DASH: readonly number[];
24
+ /** Opacity of the permanent ghost. */
25
+ export declare const GHOST_ALPHA = 0.45;
26
+ /** Which `EstuarineElementModel` flag shows a given curve. */
27
+ export type EstuarineCurveVisibility = 'showLiminal' | 'showVolatile' | 'showCounterfactual';
28
+ export interface EstuarineCurve {
29
+ key: 'liminal' | 'volatile' | 'counterfactual';
30
+ path: Path2D;
31
+ color: string;
32
+ width: number;
33
+ visibleProp: EstuarineCurveVisibility;
34
+ }
35
+ export declare function estuarineCurves(): readonly EstuarineCurve[];
36
+ /**
37
+ * How the fixed 690 × 801 reference design maps onto an element of `w × h`.
38
+ *
39
+ * ## Why an Estuarine map STRETCHES (PO recette, 26/08/2026)
40
+ *
41
+ * It used to be fitted uniformly and letterboxed (`refScale`), so widening the
42
+ * background left the drawing at its authored proportions, centred, with short
43
+ * axes floating in empty margins. That is wrong for THIS frame, and the reason
44
+ * is what an Estuarine map is: a coordinate system. The e axis measures energy
45
+ * to change, the t axis measures time, and the three curves are the boundaries
46
+ * a group negotiates ACROSS that plane. Give the user more room and they mean
47
+ * "more plane" — a longer time axis, a taller energy axis — not "the same
48
+ * picture, bigger". So both directions follow the element independently.
49
+ *
50
+ * **Cynefin, right next door, deliberately keeps the uniform letterbox**: its
51
+ * background is a figurative drawing — a cliff, hand-drawn arcs, a hatched
52
+ * fall — and a figurative drawing has proportions that stretching would simply
53
+ * damage. Two frames, two answers, and the difference is not an oversight.
54
+ *
55
+ * ## The two factors
56
+ *
57
+ * `sx` / `sy` stretch positions and paths. `strokeScale` is the ISOTROPIC
58
+ * factor for everything that must not be deformed — stroke widths, arrowhead
59
+ * triangles, font sizes, letter-spacing — taken as the geometric mean of the
60
+ * two, the usual area-preserving stand-in for "one scale" when there are two.
61
+ *
62
+ * At the authored ratio `sx === sy`, so all three collapse to the single old
63
+ * factor and `ox`/`oy` were zero: a map that has not been stretched paints
64
+ * exactly the pixels it painted before this change.
65
+ */
66
+ export interface EstuarineFit {
67
+ /** Reference x → element x. */
68
+ sx: number;
69
+ /** Reference y → element y. */
70
+ sy: number;
71
+ /** The one undeformed factor: widths, arrowheads, type. */
72
+ strokeScale: number;
73
+ /**
74
+ * `lineWidth` correction for a path stroked INSIDE the stretched space.
75
+ *
76
+ * Canvas transforms the pen along with the path, so under `scale(sx, sy)` a
77
+ * nominal width `L` paints somewhere between `L·sx` and `L·sy` depending on
78
+ * the direction of the segment. There is no exact single number for a curve
79
+ * that runs in every direction; we approximate the pen's effective widening
80
+ * by the ARITHMETIC mean of the two factors and divide it out, which lands
81
+ * the ghost on {@link strokeScale} on average and keeps it within the
82
+ * sx/sy spread everywhere else. Exactly `1` at the authored ratio.
83
+ */
84
+ curveLineScale: number;
85
+ }
86
+ export declare function estuarineFit(w: number, h: number): EstuarineFit;
87
+ /**
88
+ * Put `ctx` into the map's STRETCHED reference space, where the authored
89
+ * geometry (a `Path2D` built from `./consts.ts`) lands on the element's real
90
+ * bounds in both directions.
91
+ *
92
+ * The single source of truth for that transform, and it has to stay single:
93
+ * the ghost overlay paints the very same curves one layer above and must land
94
+ * on them to the pixel. Everything that must NOT be stretched — axes,
95
+ * arrowheads, legends — is drawn outside it, in element coordinates, from the
96
+ * same {@link EstuarineFit}.
97
+ */
98
+ export declare function applyEstuarineTransform(ctx: CanvasRenderingContext2D, w: number, h: number): EstuarineFit;
3
99
  /**
4
100
  * Canvas renderer for the Estuarine framework map — reproduces the official SVG:
5
101
  * the e (vertical, double-headed) / t (horizontal, single-headed) axes and the
6
102
  * three reference curves (Liminal / Volatile / Counter-factual), each with its
7
- * legend and individually hideable. Drawn in the fixed reference space and
8
- * scaled uniformly to the element bounds.
103
+ * legend and individually hideable.
104
+ *
105
+ * Two spaces, and which one a mark belongs to is the whole design:
106
+ *
107
+ * - **Element coordinates**, entered by mapping each authored coordinate
108
+ * through {@link EstuarineFit} by hand (`ax` / `ay` below): the axes, their
109
+ * arrowheads and every word. Their POSITION follows the stretch — an axis
110
+ * ends where the map now ends — while their SHAPE does not, because a
111
+ * stretched arrowhead or a squashed letter is a defect, never a feature.
112
+ * - **Stretched reference space**, entered by {@link applyEstuarineTransform}:
113
+ * the three curves, which are boundaries across the plane and must cover
114
+ * whatever plane the user has made.
9
115
  */
10
116
  export declare const estuarine: ElementRenderer<EstuarineElementModel>;
11
117
  export declare const EstuarineRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {