@formicoidea/labre-framework-cynefin 0.36.0 → 0.38.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 +15 -40
- package/dist/estuarine/reading.d.ts +34 -0
- package/dist/estuarine/reading.js +41 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +5 -0
- package/dist/presets.d.ts +56 -0
- package/dist/presets.js +71 -0
- package/dist/templates/index.js +68 -43
- package/dist/translations.js +10 -1
- package/dist/view.js +8 -1
- package/package.json +2 -2
package/dist/actions.js
CHANGED
|
@@ -1,73 +1,48 @@
|
|
|
1
1
|
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { ShapeStyle, TextFitMode } from '@formicoidea/labre-core/model';
|
|
3
2
|
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
4
3
|
import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
|
|
5
|
-
import {
|
|
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';
|
|
4
|
+
import { CYNEFIN_H, CYNEFIN_W, cynefinBackgroundProps, ESTUARINE_MAP_H, ESTUARINE_MAP_W, estuarineHexagonBox, estuarineHexagonProps, estuarineMapProps, } from './presets.js';
|
|
8
5
|
/**
|
|
9
6
|
* Creation actions for the Cynefin / Estuarine toolbox — lifted out of
|
|
10
7
|
* `toolbar/menu.ts` by PF3. This is also the framework that emitted NO
|
|
11
8
|
* telemetry at all before the switchover: routing every surface through
|
|
12
9
|
* `runCommand` fixes that for free (`docs/adr/0008`).
|
|
10
|
+
*
|
|
11
|
+
* What each artefact IS lives in `presets.ts`, which the palette reads too: the
|
|
12
|
+
* templates used to restate these props and drifted from them.
|
|
13
13
|
*/
|
|
14
|
-
/** Estuarine map default size (REF aspect, scaled up so it reads on canvas). */
|
|
15
|
-
const MAP_SCALE = 1.2;
|
|
16
14
|
const gfxOf = (std) => std.get(GfxControllerIdentifier);
|
|
17
15
|
function finish(gfx, id) {
|
|
18
16
|
gfx.doc.captureSync();
|
|
19
17
|
gfx.tool.setTool(DefaultTool);
|
|
20
18
|
gfx.selection.set({ elements: [id], editing: false });
|
|
21
19
|
}
|
|
20
|
+
/** The box an artefact of the given size is born in, centred on the viewport. */
|
|
21
|
+
function centered(gfx, width, height) {
|
|
22
|
+
const { centerX, centerY } = gfx.viewport;
|
|
23
|
+
return new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize();
|
|
24
|
+
}
|
|
22
25
|
export function createCynefin(std) {
|
|
23
26
|
const gfx = gfxOf(std);
|
|
24
27
|
if (!gfx.surface)
|
|
25
28
|
return;
|
|
26
|
-
const {
|
|
27
|
-
const id = gfx.surface.addElement({
|
|
28
|
-
type: 'cynefin',
|
|
29
|
-
xywh: new Bound(centerX - CYN_W / 2, centerY - CYN_H / 2, CYN_W, CYN_H).serialize(),
|
|
30
|
-
});
|
|
29
|
+
const id = gfx.surface.addElement(cynefinBackgroundProps({ xywh: centered(gfx, CYNEFIN_W, CYNEFIN_H) }));
|
|
31
30
|
finish(gfx, id);
|
|
32
31
|
}
|
|
33
32
|
export function createEstuarineMap(std) {
|
|
34
33
|
const gfx = gfxOf(std);
|
|
35
34
|
if (!gfx.surface)
|
|
36
35
|
return;
|
|
37
|
-
const
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
const id = gfx.surface.addElement({
|
|
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,
|
|
46
|
-
xywh: new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize(),
|
|
47
|
-
});
|
|
36
|
+
const id = gfx.surface.addElement(estuarineMapProps({
|
|
37
|
+
xywh: centered(gfx, ESTUARINE_MAP_W, ESTUARINE_MAP_H),
|
|
38
|
+
}));
|
|
48
39
|
finish(gfx, id);
|
|
49
40
|
}
|
|
50
41
|
export function createConstraintHexagon(std) {
|
|
51
42
|
const gfx = gfxOf(std);
|
|
52
43
|
if (!gfx.surface)
|
|
53
44
|
return;
|
|
54
|
-
const { centerX
|
|
55
|
-
const id = gfx.surface.addElement({
|
|
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,
|
|
60
|
-
shapeType: 'polygon',
|
|
61
|
-
vertices: HEX_VERTICES,
|
|
62
|
-
filled: true,
|
|
63
|
-
fillColor: HEX_FILL,
|
|
64
|
-
strokeColor: HEX_STROKE,
|
|
65
|
-
strokeWidth: 2,
|
|
66
|
-
shapeStyle: ShapeStyle.General,
|
|
67
|
-
roughness: 0,
|
|
68
|
-
// hexi constraints behave like post-its: fixed hex, text shrinks
|
|
69
|
-
textFitMode: TextFitMode.Contained,
|
|
70
|
-
xywh: new Bound(cx - HEX_SIZE / 2, cy - HEX_SIZE / 2, HEX_SIZE, HEX_SIZE).serialize(),
|
|
71
|
-
});
|
|
45
|
+
const { centerX, centerY } = gfx.viewport;
|
|
46
|
+
const id = gfx.surface.addElement(estuarineHexagonProps({ xywh: estuarineHexagonBox(centerX, centerY) }));
|
|
72
47
|
finish(gfx, id);
|
|
73
48
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import type { ReadingProfile } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
/**
|
|
3
|
+
* What Estuarine lets the tool read of a constraint (MF3, the reversed
|
|
4
|
+
* reading).
|
|
5
|
+
*
|
|
6
|
+
* One profile, one role, and nothing else: a hexagon is a plain polygon on the
|
|
7
|
+
* canvas, so the role is the only place that says what it is — and saying it,
|
|
8
|
+
* with the link to the record beside it, is the whole of what a reading can
|
|
9
|
+
* honestly offer here.
|
|
10
|
+
*
|
|
11
|
+
* Registered from the FLAG-GATED view extension, like every other profile:
|
|
12
|
+
* reading a board is tooling (ADR 0009).
|
|
13
|
+
*
|
|
14
|
+
* ## No relation, no nature, no phase — and no verdict
|
|
15
|
+
*
|
|
16
|
+
* Estuarine declares no edge role: an arrow drawn between two hexagons is a
|
|
17
|
+
* plain connector and carries no verb, so there is no sentence to read off it.
|
|
18
|
+
* It declares no type-3 tag pack either. And the map's own energy/time plane is
|
|
19
|
+
* NOT read as a phase in this pass: the three reference curves are not zones
|
|
20
|
+
* with an id, and reading a "position" off them would be the engine inventing a
|
|
21
|
+
* fact the declaration does not carry.
|
|
22
|
+
*
|
|
23
|
+
* ## Cynefin gets nothing, and that is ADR 0013
|
|
24
|
+
*
|
|
25
|
+
* The two frameworks share this package and share nothing here. Cynefin
|
|
26
|
+
* declares no role at all — its four domains are the participants' judgement
|
|
27
|
+
* about their own context, and there is no external truth to check — so a free
|
|
28
|
+
* element on a Cynefin board carries no role and has no reading. A CONSTRAINT
|
|
29
|
+
* hexagon dropped on such a board does, because the hexagon is an Estuarine
|
|
30
|
+
* artefact wherever it is drawn. See the amendment of 2026-09-07 to
|
|
31
|
+
* `docs/adr/0013`: reading is not validation, and this profile carries no rule
|
|
32
|
+
* and no verdict.
|
|
33
|
+
*/
|
|
34
|
+
export declare const ESTUARINE_READING: ReadingProfile;
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { ESTUARINE_ROLE, ESTUARINE_ROLES } from './roles.js';
|
|
2
|
+
/**
|
|
3
|
+
* What Estuarine lets the tool read of a constraint (MF3, the reversed
|
|
4
|
+
* reading).
|
|
5
|
+
*
|
|
6
|
+
* One profile, one role, and nothing else: a hexagon is a plain polygon on the
|
|
7
|
+
* canvas, so the role is the only place that says what it is — and saying it,
|
|
8
|
+
* with the link to the record beside it, is the whole of what a reading can
|
|
9
|
+
* honestly offer here.
|
|
10
|
+
*
|
|
11
|
+
* Registered from the FLAG-GATED view extension, like every other profile:
|
|
12
|
+
* reading a board is tooling (ADR 0009).
|
|
13
|
+
*
|
|
14
|
+
* ## No relation, no nature, no phase — and no verdict
|
|
15
|
+
*
|
|
16
|
+
* Estuarine declares no edge role: an arrow drawn between two hexagons is a
|
|
17
|
+
* plain connector and carries no verb, so there is no sentence to read off it.
|
|
18
|
+
* It declares no type-3 tag pack either. And the map's own energy/time plane is
|
|
19
|
+
* NOT read as a phase in this pass: the three reference curves are not zones
|
|
20
|
+
* with an id, and reading a "position" off them would be the engine inventing a
|
|
21
|
+
* fact the declaration does not carry.
|
|
22
|
+
*
|
|
23
|
+
* ## Cynefin gets nothing, and that is ADR 0013
|
|
24
|
+
*
|
|
25
|
+
* The two frameworks share this package and share nothing here. Cynefin
|
|
26
|
+
* declares no role at all — its four domains are the participants' judgement
|
|
27
|
+
* about their own context, and there is no external truth to check — so a free
|
|
28
|
+
* element on a Cynefin board carries no role and has no reading. A CONSTRAINT
|
|
29
|
+
* hexagon dropped on such a board does, because the hexagon is an Estuarine
|
|
30
|
+
* artefact wherever it is drawn. See the amendment of 2026-09-07 to
|
|
31
|
+
* `docs/adr/0013`: reading is not validation, and this profile carries no rule
|
|
32
|
+
* and no verdict.
|
|
33
|
+
*/
|
|
34
|
+
export const ESTUARINE_READING = {
|
|
35
|
+
id: 'estuarine',
|
|
36
|
+
framework: 'cynefin-estuarine',
|
|
37
|
+
roles: ESTUARINE_ROLES,
|
|
38
|
+
// Not the map: a reading is about an artefact, never about the frame it is
|
|
39
|
+
// measured against.
|
|
40
|
+
appliesTo: ESTUARINE_ROLE.constraint,
|
|
41
|
+
};
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
1
|
export { cynefinEstuarineCommandIcons, cynefinEstuarineCommands, } from './commands.js';
|
|
2
2
|
export { cynefinEstuarineTranslationEntries } from './translations.js';
|
|
3
|
+
export { ESTUARINE_ROLE, ESTUARINE_ROLES, type EstuarineRoleId, } from './estuarine/roles.js';
|
|
4
|
+
export { ESTUARINE_READING } from './estuarine/reading.js';
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,7 @@
|
|
|
1
1
|
export { cynefinEstuarineCommandIcons, cynefinEstuarineCommands, } from './commands.js';
|
|
2
2
|
export { cynefinEstuarineTranslationEntries } from './translations.js';
|
|
3
|
+
// Estuarine's role vocabulary and its reading profile: DATA a host — and the
|
|
4
|
+
// library's own cross-framework guard test — can read without an editor.
|
|
5
|
+
// Cynefin contributes neither, having no role of its own (ADR 0013).
|
|
6
|
+
export { ESTUARINE_ROLE, ESTUARINE_ROLES, } from './estuarine/roles.js';
|
|
7
|
+
export { ESTUARINE_READING } from './estuarine/reading.js';
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* What a Cynefin / Estuarine artefact IS, as props — the ONE description the
|
|
3
|
+
* creation actions and the templates both read.
|
|
4
|
+
*
|
|
5
|
+
* Split out of `actions.ts` for the reason Wardley's and BPMN's `presets.ts`
|
|
6
|
+
* give: the palette used to restate these props by hand and drifted the moment
|
|
7
|
+
* the toolbox moved. The hexagon lost the `textFitMode` that #52 gave it, and
|
|
8
|
+
* the estuarine background was drawn at scale 1 while the button drew it at
|
|
9
|
+
* 1.2. Derived from one table, neither can happen again.
|
|
10
|
+
*/
|
|
11
|
+
/** Estuarine map default scale (REF aspect, scaled up so it reads on canvas). */
|
|
12
|
+
export declare const MAP_SCALE = 1.2;
|
|
13
|
+
/** The box an estuarine map is born in — the size the toolbox draws. */
|
|
14
|
+
export declare const ESTUARINE_MAP_W: number;
|
|
15
|
+
export declare const ESTUARINE_MAP_H: number;
|
|
16
|
+
/** The box a Cynefin frame is born in. */
|
|
17
|
+
export declare const CYNEFIN_W = 1080;
|
|
18
|
+
export declare const CYNEFIN_H = 777;
|
|
19
|
+
/**
|
|
20
|
+
* The Cynefin frame, as props.
|
|
21
|
+
*
|
|
22
|
+
* NO role, deliberately and for good: the framework is out of the validation
|
|
23
|
+
* perimeter (PO, 26/08/2026 — see `estuarine/roles.ts`). Stated here once so
|
|
24
|
+
* the neutrality is a decision rather than an omission on either side.
|
|
25
|
+
*/
|
|
26
|
+
export declare function cynefinBackgroundProps(box: {
|
|
27
|
+
xywh: string;
|
|
28
|
+
}): Record<string, unknown> & {
|
|
29
|
+
type: string;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* The estuarine map, as props.
|
|
33
|
+
*
|
|
34
|
+
* The map is this framework's ROOT INSTANCE: the role is what makes the Map
|
|
35
|
+
* quality checklist reachable on it (WS4).
|
|
36
|
+
*/
|
|
37
|
+
export declare function estuarineMapProps(box: {
|
|
38
|
+
xywh: string;
|
|
39
|
+
}): Record<string, unknown> & {
|
|
40
|
+
type: string;
|
|
41
|
+
};
|
|
42
|
+
/**
|
|
43
|
+
* A hexi constraint, as props.
|
|
44
|
+
*
|
|
45
|
+
* A hexagon is a plain polygon on the canvas: nothing about its geometry says
|
|
46
|
+
* "constraint". The role is the only place that does. The fit mode is the other
|
|
47
|
+
* half of the notation — a constraint behaves like a post-it, fixed hex, text
|
|
48
|
+
* shrinks — and it is exactly what the palette had lost.
|
|
49
|
+
*/
|
|
50
|
+
export declare function estuarineHexagonProps(box: {
|
|
51
|
+
xywh: string;
|
|
52
|
+
}): Record<string, unknown> & {
|
|
53
|
+
type: string;
|
|
54
|
+
};
|
|
55
|
+
/** The box a hexagon is born in, centred on a point. */
|
|
56
|
+
export declare function estuarineHexagonBox(cx: number, cy: number): string;
|
package/dist/presets.js
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { ShapeStyle, TextFitMode } from '@formicoidea/labre-core/model';
|
|
2
|
+
import { REF_H as CYN_H, REF_W as CYN_W } from './cynefin/consts.js';
|
|
3
|
+
import { HEX_FILL, HEX_SIZE, HEX_STROKE, HEX_VERTICES, REF_H as EST_H, REF_W as EST_W, } from './estuarine/consts.js';
|
|
4
|
+
import { ESTUARINE_ROLE } from './estuarine/roles.js';
|
|
5
|
+
/**
|
|
6
|
+
* What a Cynefin / Estuarine artefact IS, as props — the ONE description the
|
|
7
|
+
* creation actions and the templates both read.
|
|
8
|
+
*
|
|
9
|
+
* Split out of `actions.ts` for the reason Wardley's and BPMN's `presets.ts`
|
|
10
|
+
* give: the palette used to restate these props by hand and drifted the moment
|
|
11
|
+
* the toolbox moved. The hexagon lost the `textFitMode` that #52 gave it, and
|
|
12
|
+
* the estuarine background was drawn at scale 1 while the button drew it at
|
|
13
|
+
* 1.2. Derived from one table, neither can happen again.
|
|
14
|
+
*/
|
|
15
|
+
/** Estuarine map default scale (REF aspect, scaled up so it reads on canvas). */
|
|
16
|
+
export const MAP_SCALE = 1.2;
|
|
17
|
+
/** The box an estuarine map is born in — the size the toolbox draws. */
|
|
18
|
+
export const ESTUARINE_MAP_W = EST_W * MAP_SCALE;
|
|
19
|
+
export const ESTUARINE_MAP_H = EST_H * MAP_SCALE;
|
|
20
|
+
/** The box a Cynefin frame is born in. */
|
|
21
|
+
export const CYNEFIN_W = CYN_W;
|
|
22
|
+
export const CYNEFIN_H = CYN_H;
|
|
23
|
+
/**
|
|
24
|
+
* The Cynefin frame, as props.
|
|
25
|
+
*
|
|
26
|
+
* NO role, deliberately and for good: the framework is out of the validation
|
|
27
|
+
* perimeter (PO, 26/08/2026 — see `estuarine/roles.ts`). Stated here once so
|
|
28
|
+
* the neutrality is a decision rather than an omission on either side.
|
|
29
|
+
*/
|
|
30
|
+
export function cynefinBackgroundProps(box) {
|
|
31
|
+
return { type: 'cynefin', xywh: box.xywh };
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* The estuarine map, as props.
|
|
35
|
+
*
|
|
36
|
+
* The map is this framework's ROOT INSTANCE: the role is what makes the Map
|
|
37
|
+
* quality checklist reachable on it (WS4).
|
|
38
|
+
*/
|
|
39
|
+
export function estuarineMapProps(box) {
|
|
40
|
+
return { type: 'estuarine', role: ESTUARINE_ROLE.map, xywh: box.xywh };
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* A hexi constraint, as props.
|
|
44
|
+
*
|
|
45
|
+
* A hexagon is a plain polygon on the canvas: nothing about its geometry says
|
|
46
|
+
* "constraint". The role is the only place that does. The fit mode is the other
|
|
47
|
+
* half of the notation — a constraint behaves like a post-it, fixed hex, text
|
|
48
|
+
* shrinks — and it is exactly what the palette had lost.
|
|
49
|
+
*/
|
|
50
|
+
export function estuarineHexagonProps(box) {
|
|
51
|
+
return {
|
|
52
|
+
type: 'shape',
|
|
53
|
+
role: ESTUARINE_ROLE.constraint,
|
|
54
|
+
shapeType: 'polygon',
|
|
55
|
+
// A fresh array of fresh pairs: this goes into a document, and two hexagons
|
|
56
|
+
// sharing one literal would be two elements sharing one array.
|
|
57
|
+
vertices: HEX_VERTICES.map(([x, y]) => [x, y]),
|
|
58
|
+
filled: true,
|
|
59
|
+
fillColor: HEX_FILL,
|
|
60
|
+
strokeColor: HEX_STROKE,
|
|
61
|
+
strokeWidth: 2,
|
|
62
|
+
shapeStyle: ShapeStyle.General,
|
|
63
|
+
roughness: 0,
|
|
64
|
+
textFitMode: TextFitMode.Contained,
|
|
65
|
+
xywh: box.xywh,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** The box a hexagon is born in, centred on a point. */
|
|
69
|
+
export function estuarineHexagonBox(cx, cy) {
|
|
70
|
+
return `[${cx - HEX_SIZE / 2},${cy - HEX_SIZE / 2},${HEX_SIZE},${HEX_SIZE}]`;
|
|
71
|
+
}
|
package/dist/templates/index.js
CHANGED
|
@@ -1,8 +1,30 @@
|
|
|
1
|
-
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
1
|
+
import { makeTemplateSnapshot, surfaceText, templateFromCommand, } from '@formicoidea/labre-core/gfx/template';
|
|
2
2
|
import { FontFamily, ShapeStyle, TextAlign } from '@formicoidea/labre-core/model';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
3
|
+
import { cynefinEstuarineCommands } from '../commands.js';
|
|
4
|
+
import { HEX_SIZE } from '../estuarine/consts.js';
|
|
5
|
+
import { CYNEFIN_H, CYNEFIN_W, cynefinBackgroundProps, ESTUARINE_MAP_H, ESTUARINE_MAP_W, estuarineHexagonBox, estuarineHexagonProps, estuarineMapProps, MAP_SCALE, } from '../presets.js';
|
|
6
|
+
/**
|
|
7
|
+
* The Cynefin / Estuarine palettes — DERIVED from the toolbox, one template per
|
|
8
|
+
* command.
|
|
9
|
+
*
|
|
10
|
+
* Every single-artefact entry below is what its command actually draws, run
|
|
11
|
+
* once against a recording surface. Hand-written, they had drifted exactly as
|
|
12
|
+
* far as a copy drifts: the hexagons had lost the `textFitMode` #52 gave them,
|
|
13
|
+
* and the estuarine background was drawn at scale 1 (690×801) where the button
|
|
14
|
+
* draws it at 1.2 (828×961). Derived, neither can happen again — and
|
|
15
|
+
* `templates-parity.unit.spec.ts` re-runs each command and compares.
|
|
16
|
+
*
|
|
17
|
+
* The two COMPOSITIONS stay hand-written: a sorting board and a constraint map
|
|
18
|
+
* are arrangements no single command draws. They are built on the same presets,
|
|
19
|
+
* and the same test checks their composition.
|
|
20
|
+
*/
|
|
21
|
+
/** The command a derived template is the picture of. Throws rather than skips. */
|
|
22
|
+
function byId(id) {
|
|
23
|
+
const command = cynefinEstuarineCommands.find(entry => entry.id === id);
|
|
24
|
+
if (!command)
|
|
25
|
+
throw new Error(`[cynefin-estuarine] templates: no "${id}"`);
|
|
26
|
+
return command;
|
|
27
|
+
}
|
|
6
28
|
function sticky(x, y, text) {
|
|
7
29
|
return {
|
|
8
30
|
type: 'shape',
|
|
@@ -22,24 +44,31 @@ function sticky(x, y, text) {
|
|
|
22
44
|
xywh: `[${x},${y},200,70]`,
|
|
23
45
|
};
|
|
24
46
|
}
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
47
|
+
/**
|
|
48
|
+
* A constraint on the shipped map, placed by its CENTRE in the map's reference
|
|
49
|
+
* space (690×801) — the frame the drawing was laid out in — carried onto the
|
|
50
|
+
* box the map is actually born at.
|
|
51
|
+
*/
|
|
52
|
+
const hexAt = (cx, cy) => estuarineHexagonProps({
|
|
53
|
+
xywh: estuarineHexagonBox(cx * MAP_SCALE, cy * MAP_SCALE),
|
|
54
|
+
});
|
|
55
|
+
const CAPTION_W = 120;
|
|
56
|
+
const CAPTION_H = 24;
|
|
57
|
+
/** Room between a hexagon's bottom edge and the name under it. */
|
|
58
|
+
const CAPTION_GAP = 8;
|
|
59
|
+
/**
|
|
60
|
+
* The name UNDER the hexagon centred on the same point.
|
|
61
|
+
*
|
|
62
|
+
* The offset is `HEX_SIZE`, never a literal: this used to be a `+64` computed
|
|
63
|
+
* for a 60-wide hexagon, and #55 doubled the hexagon to 120 without touching
|
|
64
|
+
* it — so every caption ended up drawn INSIDE the shape it names. Derived, the
|
|
65
|
+
* caption follows the hexagon whatever size it is given next.
|
|
66
|
+
*
|
|
67
|
+
* Neutral, and staying so: a name is nobody's artefact.
|
|
68
|
+
*/
|
|
69
|
+
function caption(cx, cy, str) {
|
|
70
|
+
const x = cx * MAP_SCALE - CAPTION_W / 2;
|
|
71
|
+
const y = cy * MAP_SCALE + HEX_SIZE / 2 + CAPTION_GAP;
|
|
43
72
|
return {
|
|
44
73
|
type: 'text',
|
|
45
74
|
text: surfaceText(str),
|
|
@@ -47,9 +76,10 @@ function caption(x, y, str) {
|
|
|
47
76
|
fontFamily: FontFamily.Inter,
|
|
48
77
|
fontSize: 16,
|
|
49
78
|
textAlign: TextAlign.Center,
|
|
50
|
-
xywh: `[${x},${y}
|
|
79
|
+
xywh: `[${x},${y},${CAPTION_W},${CAPTION_H}]`,
|
|
51
80
|
};
|
|
52
81
|
}
|
|
82
|
+
/** A hand-composed template — what is left once the artefacts are derived. */
|
|
53
83
|
function tpl(name, preview, elements) {
|
|
54
84
|
return {
|
|
55
85
|
name,
|
|
@@ -59,41 +89,36 @@ function tpl(name, preview, elements) {
|
|
|
59
89
|
};
|
|
60
90
|
}
|
|
61
91
|
const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
62
|
-
// Cynefin's background carries NO role, deliberately and for good: the
|
|
63
|
-
// framework is out of the validation perimeter (PO, 26/08/2026). See
|
|
64
|
-
// `estuarine/roles.ts`.
|
|
65
|
-
const cynefinBg = (xywh) => ({ type: 'cynefin', xywh });
|
|
66
|
-
const estuarineBg = (xywh) => ({
|
|
67
|
-
type: 'estuarine',
|
|
68
|
-
role: ESTUARINE_ROLE.map,
|
|
69
|
-
xywh,
|
|
70
|
-
});
|
|
71
92
|
export const cynefinTemplateCategory = {
|
|
72
93
|
name: 'Cynefin',
|
|
73
94
|
templates: [
|
|
74
95
|
tpl('Decision sorting', `<svg ${ATTRS} fill="none"><rect x="8" y="10" width="119" height="60" rx="4" stroke="#2a9d99" stroke-width="1.5"/><path d="M67 10 V70 M8 40 H127" stroke="#9aa0a6"/><rect x="18" y="18" width="34" height="14" rx="2" fill="#fff3b0"/><rect x="83" y="18" width="34" height="14" rx="2" fill="#fff3b0"/><rect x="18" y="48" width="34" height="14" rx="2" fill="#fff3b0"/><rect x="83" y="48" width="34" height="14" rx="2" fill="#fff3b0"/></svg>`, {
|
|
75
|
-
bg:
|
|
96
|
+
bg: cynefinBackgroundProps({
|
|
97
|
+
xywh: `[0,0,${CYNEFIN_W},${CYNEFIN_H}]`,
|
|
98
|
+
}),
|
|
76
99
|
s1: sticky(190, 175, 'Probe & learn'),
|
|
77
100
|
s2: sticky(690, 175, 'Expert analysis'),
|
|
78
101
|
s3: sticky(190, 505, 'Act now'),
|
|
79
102
|
s4: sticky(690, 505, 'Known issue'),
|
|
80
103
|
}),
|
|
81
|
-
|
|
104
|
+
templateFromCommand(byId('cynefin-estuarine.addCynefin'), `<svg ${ATTRS} fill="none"><rect x="14" y="12" width="107" height="56" rx="4" stroke="#2a9d99" stroke-width="1.6"/><path d="M67 12 V68 M14 40 H121" stroke="#9aa0a6"/></svg>`, 'Cynefin framework'),
|
|
82
105
|
],
|
|
83
106
|
};
|
|
84
107
|
export const estuarineTemplateCategory = {
|
|
85
108
|
name: 'Estuarine',
|
|
86
109
|
templates: [
|
|
87
110
|
tpl('Constraint map', `<svg ${ATTRS} fill="none"><path d="M20 12 V70 M20 70 H120" stroke="#941253" stroke-width="2"/><g fill="#34c724" stroke="#1f1f1f"><path d="M44 28 l6 4 l0 8 l-6 4 l-6 -4 l0 -8 z"/><path d="M74 40 l6 4 l0 8 l-6 4 l-6 -4 l0 -8 z"/><path d="M56 52 l6 4 l0 8 l-6 4 l-6 -4 l0 -8 z"/></g></svg>`, {
|
|
88
|
-
bg:
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
111
|
+
bg: estuarineMapProps({
|
|
112
|
+
xywh: `[0,0,${ESTUARINE_MAP_W},${ESTUARINE_MAP_H}]`,
|
|
113
|
+
}),
|
|
114
|
+
h1: hexAt(210, 280),
|
|
115
|
+
c1: caption(210, 280, 'Policy'),
|
|
116
|
+
h2: hexAt(390, 420),
|
|
117
|
+
c2: caption(390, 420, 'Habit'),
|
|
118
|
+
h3: hexAt(290, 580),
|
|
119
|
+
c3: caption(290, 580, 'Budget'),
|
|
95
120
|
}),
|
|
96
|
-
|
|
97
|
-
|
|
121
|
+
templateFromCommand(byId('cynefin-estuarine.addEstuarineMap'), `<svg ${ATTRS} fill="none"><path d="M24 10 V70 M24 70 H120" stroke="#941253" stroke-width="2.4"/><path d="M30 52 q40 -30 84 -34" stroke="#5ecc44" stroke-width="2" fill="none"/></svg>`, 'Estuarine map'),
|
|
122
|
+
templateFromCommand(byId('cynefin-estuarine.addConstraintHexagon'), `<svg ${ATTRS} fill="none"><path d="M67 24 l18 11 l0 22 l-18 11 l-18 -11 l0 -22 z" fill="#34c724" stroke="#1f1f1f" stroke-width="2"/></svg>`, 'Hexagon constraint'),
|
|
98
123
|
],
|
|
99
124
|
};
|
package/dist/translations.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { collectTranslationKeys, commandCategoryTranslationEntries, commandTranslationEntries, mergeTranslationEntries, } from '@formicoidea/labre-core/std';
|
|
2
2
|
import { cynefinEstuarineCommands } from './commands.js';
|
|
3
3
|
import { ESTUARINE_NUDGES } from './estuarine/nudges.js';
|
|
4
|
+
import { ESTUARINE_READING } from './estuarine/reading.js';
|
|
4
5
|
import { ESTUARINE_ROLES } from './estuarine/roles.js';
|
|
5
6
|
/**
|
|
6
7
|
* THIS framework's contribution to the translation-key manifest.
|
|
@@ -25,4 +26,12 @@ export const cynefinEstuarineTranslationEntries = mergeTranslationEntries(comman
|
|
|
25
26
|
// registry names no framework category in the bundled distribution, so a
|
|
26
27
|
// host that composed core's manifest alone drew translated entries under
|
|
27
28
|
// English headers (#183).
|
|
28
|
-
commandCategoryTranslationEntries(cynefinEstuarineCommands), collectTranslationKeys('role', ESTUARINE_ROLES), collectTranslationKeys('nudge', ESTUARINE_NUDGES)
|
|
29
|
+
commandCategoryTranslationEntries(cynefinEstuarineCommands), collectTranslationKeys('role', ESTUARINE_ROLES), collectTranslationKeys('nudge', ESTUARINE_NUDGES),
|
|
30
|
+
// LAST, and the order is load-bearing: a reading profile carries the
|
|
31
|
+
// framework's own `roles`, so walking it reaches every role key the line
|
|
32
|
+
// above already named. `mergeTranslationEntries` keeps the FIRST
|
|
33
|
+
// occurrence, which is what makes each key report the source it comes from.
|
|
34
|
+
// This profile declares no relation, so today it contributes no key of its
|
|
35
|
+
// own — walking it costs nothing and means the day it gains one the
|
|
36
|
+
// manifest already names it.
|
|
37
|
+
collectTranslationKeys('reading', ESTUARINE_READING));
|
package/dist/view.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { QualityNudgeExtension, ValidationFrameworkExtension, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { QualityNudgeExtension, ReadingProfileExtension, ValidationFrameworkExtension, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
|
|
3
3
|
import { extendTemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
4
4
|
import { ToolbarModuleExtension } from '@formicoidea/labre-core/shared/services';
|
|
@@ -13,6 +13,7 @@ import { EstuarineRendererExtension } from './estuarine/element-renderer.js';
|
|
|
13
13
|
import { EstuarineInteraction, EstuarineView } from './estuarine/element-view.js';
|
|
14
14
|
import { EstuarineGhostManager, EstuarineGhostOverlay, } from './estuarine/ghost-overlay.js';
|
|
15
15
|
import { ESTUARINE_NUDGES } from './estuarine/nudges.js';
|
|
16
|
+
import { ESTUARINE_READING } from './estuarine/reading.js';
|
|
16
17
|
import { ESTUARINE_ROLE, ESTUARINE_ROLES } from './estuarine/roles.js';
|
|
17
18
|
import { estuarineToolbarExtension } from './estuarine/toolbar/config.js';
|
|
18
19
|
import { cynefinTemplateCategory, estuarineTemplateCategory, } from './templates/index.js';
|
|
@@ -104,6 +105,12 @@ export class CynefinEstuarineViewExtension extends ViewExtensionProvider {
|
|
|
104
105
|
id: BlockFlavourIdentifier('custom:affine:surface:estuarine'),
|
|
105
106
|
config: validationToolbarConfig,
|
|
106
107
|
}));
|
|
108
|
+
// The reversed reading (MF3): what the board says about a constraint, on
|
|
109
|
+
// demand. Reading is not validation — this profile carries no rule and no
|
|
110
|
+
// verdict — which is why it can exist on a package ADR 0013 keeps out of
|
|
111
|
+
// the validation perimeter (see that ADR's amendment of 2026-09-07). A
|
|
112
|
+
// free element on a Cynefin board carries no role and stays unreadable.
|
|
113
|
+
context.register(ReadingProfileExtension(ESTUARINE_READING));
|
|
107
114
|
context.register(cynefinEstuarineSeniorTool);
|
|
108
115
|
context.register(CommandExtension(cynefinEstuarineCommands, cynefinEstuarineCommandIcons));
|
|
109
116
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@formicoidea/labre-framework-cynefin",
|
|
3
3
|
"description": "Labre cynefin-estuarine framework for @formicoidea/labre-core.",
|
|
4
|
-
"version": "0.
|
|
4
|
+
"version": "0.38.1",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dist"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@formicoidea/labre-core": "0.
|
|
34
|
+
"@formicoidea/labre-core": "0.38.1",
|
|
35
35
|
"@preact/signals-core": "^1.8.0",
|
|
36
36
|
"lit": "^3.2.0"
|
|
37
37
|
}
|