@formicoidea/labre-framework-cynefin 0.41.0 → 0.43.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/LICENSE +384 -0
- package/dist/commands.js +5 -1
- package/dist/cynefin/consts.d.ts +19 -0
- package/dist/cynefin/consts.js +12 -0
- package/dist/cynefin/crop.d.ts +6 -0
- package/dist/cynefin/crop.js +45 -0
- package/dist/cynefin/element-renderer.js +12 -8
- package/dist/cynefin/element-view.d.ts +41 -4
- package/dist/cynefin/element-view.js +59 -5
- package/dist/cynefin/labels.d.ts +25 -0
- package/dist/cynefin/labels.js +40 -0
- package/dist/estuarine/consts.d.ts +112 -21
- package/dist/estuarine/consts.js +101 -29
- package/dist/estuarine/element-renderer.d.ts +7 -0
- package/dist/estuarine/element-renderer.js +56 -24
- package/dist/estuarine/element-view.d.ts +10 -2
- package/dist/estuarine/element-view.js +17 -2
- package/dist/estuarine/labels.d.ts +40 -0
- package/dist/estuarine/labels.js +50 -0
- package/dist/presets.d.ts +13 -2
- package/dist/presets.js +13 -2
- package/dist/templates/index.js +17 -10
- 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 +9 -0
- package/dist/utils.d.ts +6 -8
- package/dist/utils.js +6 -7
- package/dist/view.js +7 -0
- package/package.json +2 -2
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
|
|
1
|
+
import { ElementRendererExtension, taggedPath2D, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
3
3
|
import { FONT_FAMILY } from '../utils.js';
|
|
4
|
-
import { ARROWHEADS, AXIS_LABELS, AXIS_WIDTH, COLORS, COUNTERFACTUAL_PATH, COUNTERFACTUAL_WIDTH, E_AXIS, LABEL_LETTER_SPACING, LABELS, LIMINAL_PATH, LIMINAL_WIDTH, REF_H, REF_W, T_AXIS, VOLATILE_PATH, VOLATILE_WIDTH, } from './consts.js';
|
|
4
|
+
import { ARROWHEADS, AXIS_LABELS, AXIS_WIDTH, COLORS, COUNTERFACTUAL_PATH, COUNTERFACTUAL_WIDTH, E_AXIS, LABEL_LETTER_SPACING, LABELS, LIMINAL_PATH, LIMINAL_WIDTH, REF_H, REF_W, REF_X, REF_Y, T_AXIS, VOLATILE_PATH, VOLATILE_WIDTH, } from './consts.js';
|
|
5
|
+
import { estuarineLegendText } from './labels.js';
|
|
5
6
|
/**
|
|
6
7
|
* The three reference curves are drawn as a permanent GHOST (PO arbitration,
|
|
7
8
|
* 26/08/2026): dashed, translucent, never solid again.
|
|
@@ -40,21 +41,21 @@ export function estuarineCurves() {
|
|
|
40
41
|
return (_curves ??= [
|
|
41
42
|
{
|
|
42
43
|
key: 'liminal',
|
|
43
|
-
path:
|
|
44
|
+
path: taggedPath2D(LIMINAL_PATH),
|
|
44
45
|
color: COLORS.liminal,
|
|
45
46
|
width: LIMINAL_WIDTH,
|
|
46
47
|
visibleProp: 'showLiminal',
|
|
47
48
|
},
|
|
48
49
|
{
|
|
49
50
|
key: 'volatile',
|
|
50
|
-
path:
|
|
51
|
+
path: taggedPath2D(VOLATILE_PATH),
|
|
51
52
|
color: COLORS.volatile,
|
|
52
53
|
width: VOLATILE_WIDTH,
|
|
53
54
|
visibleProp: 'showVolatile',
|
|
54
55
|
},
|
|
55
56
|
{
|
|
56
57
|
key: 'counterfactual',
|
|
57
|
-
path:
|
|
58
|
+
path: taggedPath2D(COUNTERFACTUAL_PATH),
|
|
58
59
|
color: COLORS.counterfactual,
|
|
59
60
|
width: COUNTERFACTUAL_WIDTH,
|
|
60
61
|
visibleProp: 'showCounterfactual',
|
|
@@ -81,6 +82,10 @@ export function estuarineFit(w, h) {
|
|
|
81
82
|
export function applyEstuarineTransform(ctx, w, h) {
|
|
82
83
|
const fit = estuarineFit(w, h);
|
|
83
84
|
ctx.scale(fit.sx, fit.sy);
|
|
85
|
+
// The crop: authored (REF_X, REF_Y) is the element's own origin. Applied
|
|
86
|
+
// INSIDE the scale so the translation is expressed in authored units, which
|
|
87
|
+
// is the space the `Path2D` this transform is meant for is written in.
|
|
88
|
+
ctx.translate(-REF_X, -REF_Y);
|
|
84
89
|
return fit;
|
|
85
90
|
}
|
|
86
91
|
/**
|
|
@@ -96,6 +101,13 @@ export function applyEstuarineTransform(ctx, w, h) {
|
|
|
96
101
|
* arrowheads and every word. Their POSITION follows the stretch — an axis
|
|
97
102
|
* ends where the map now ends — while their SHAPE does not, because a
|
|
98
103
|
* stretched arrowhead or a squashed letter is a defect, never a feature.
|
|
104
|
+
*
|
|
105
|
+
* One rule keeps that split honest, and it is the shared primitive's own
|
|
106
|
+
* (`drawAxis` / `backgroundPoint`): **an anchor is a RATIO of the frame, and
|
|
107
|
+
* every offset from an anchor is in FIXED units.** A shape must never take
|
|
108
|
+
* its anchor from one and its offset from the other — that mix is exactly
|
|
109
|
+
* what tore the arrowheads off their axes and slid the `e` / `t` letters
|
|
110
|
+
* around on a stretched map.
|
|
99
111
|
* - **Stretched reference space**, entered by {@link applyEstuarineTransform}:
|
|
100
112
|
* the three curves, which are boundaries across the plane and must cover
|
|
101
113
|
* whatever plane the user has made.
|
|
@@ -110,10 +122,10 @@ export const estuarine = (model, ctx, matrix, renderer) => {
|
|
|
110
122
|
.rotateSelf(model.rotate)
|
|
111
123
|
.translateSelf(-cx, -cy));
|
|
112
124
|
const fit = estuarineFit(w, h);
|
|
113
|
-
/** Authored x → element x
|
|
114
|
-
const ax = (x) => x * fit.sx;
|
|
125
|
+
/** Authored x → element x, through the cropped window (see `./consts.ts`). */
|
|
126
|
+
const ax = (x) => (x - REF_X) * fit.sx;
|
|
115
127
|
/** Authored y → element y. */
|
|
116
|
-
const ay = (y) => y * fit.sy;
|
|
128
|
+
const ay = (y) => (y - REF_Y) * fit.sy;
|
|
117
129
|
ctx.lineCap = 'round';
|
|
118
130
|
ctx.lineJoin = 'round';
|
|
119
131
|
// ── Axes ────────────────────────────────────────────────────────────
|
|
@@ -129,24 +141,37 @@ export const estuarine = (model, ctx, matrix, renderer) => {
|
|
|
129
141
|
ctx.moveTo(ax(T_AXIS.x1), ay(T_AXIS.y));
|
|
130
142
|
ctx.lineTo(ax(T_AXIS.x2), ay(T_AXIS.y));
|
|
131
143
|
ctx.stroke();
|
|
132
|
-
// Each head is
|
|
133
|
-
//
|
|
134
|
-
//
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
144
|
+
// Each head is welded to the END OF ITS AXIS — the one proportional anchor —
|
|
145
|
+
// and built entirely from fixed offsets around that point, exactly as the
|
|
146
|
+
// shared primitive's `drawAxis` builds one. Anchor by ratio, offset by fixed
|
|
147
|
+
// units, never a mix of the two on the two ends of one shape: that is the
|
|
148
|
+
// rule the old formula broke by pinning the tip proportionally and then
|
|
149
|
+
// rebuilding the base from it at the isotropic scale.
|
|
150
|
+
const k = fit.strokeScale;
|
|
151
|
+
for (const head of ARROWHEADS) {
|
|
152
|
+
const [dx, dy] = head.dir;
|
|
153
|
+
// Across the axis, same fixed units — the base's half-width.
|
|
154
|
+
const [px, py] = [-dy, dx];
|
|
155
|
+
const endX = ax(head.at[0]);
|
|
156
|
+
const endY = ay(head.at[1]);
|
|
157
|
+
// The base sits SHORT of the axis end, so the stroke dies inside the
|
|
158
|
+
// triangle and the two never come apart however the map is pulled.
|
|
159
|
+
const baseX = endX - dx * head.overlap * k;
|
|
160
|
+
const baseY = endY - dy * head.overlap * k;
|
|
161
|
+
const spreadX = px * head.halfWidth * k;
|
|
162
|
+
const spreadY = py * head.halfWidth * k;
|
|
139
163
|
ctx.beginPath();
|
|
140
|
-
ctx.moveTo(
|
|
141
|
-
ctx.lineTo(
|
|
142
|
-
ctx.lineTo(
|
|
164
|
+
ctx.moveTo(endX + dx * head.tip * k, endY + dy * head.tip * k);
|
|
165
|
+
ctx.lineTo(baseX - spreadX, baseY - spreadY);
|
|
166
|
+
ctx.lineTo(baseX + spreadX, baseY + spreadY);
|
|
143
167
|
ctx.closePath();
|
|
144
168
|
ctx.fill();
|
|
145
169
|
}
|
|
146
170
|
// Uppercase legend (centre-anchored, alphabetic baseline, letter-spaced).
|
|
147
171
|
// Anchored proportionally, typed isotropically — never inside the stretch.
|
|
148
172
|
const hasSpacing = 'letterSpacing' in ctx;
|
|
149
|
-
const legend = (
|
|
173
|
+
const legend = (key) => {
|
|
174
|
+
const l = LABELS[key];
|
|
150
175
|
ctx.fillStyle = l.color;
|
|
151
176
|
ctx.font = `600 ${l.size * fit.strokeScale}px ${FONT_FAMILY}`;
|
|
152
177
|
ctx.textAlign = 'center';
|
|
@@ -154,7 +179,9 @@ export const estuarine = (model, ctx, matrix, renderer) => {
|
|
|
154
179
|
if (hasSpacing) {
|
|
155
180
|
ctx.letterSpacing = `${LABEL_LETTER_SPACING * fit.strokeScale}px`;
|
|
156
181
|
}
|
|
157
|
-
|
|
182
|
+
// The user's own word when they have renamed this legend, the catalogue's
|
|
183
|
+
// otherwise — the same precedence a declared background applies.
|
|
184
|
+
ctx.fillText(estuarineLegendText(model, key, tr), ax(l.x), ay(l.y));
|
|
158
185
|
if (hasSpacing)
|
|
159
186
|
ctx.letterSpacing = '0px';
|
|
160
187
|
};
|
|
@@ -173,16 +200,21 @@ export const estuarine = (model, ctx, matrix, renderer) => {
|
|
|
173
200
|
ctx.setLineDash([...GHOST_DASH]);
|
|
174
201
|
ctx.stroke(curve.path);
|
|
175
202
|
ctx.restore();
|
|
176
|
-
legend(
|
|
203
|
+
legend(curve.key);
|
|
177
204
|
}
|
|
178
205
|
// ── Italic e / t axis letters ───────────────────────────────────────
|
|
206
|
+
// A letter names an axis, so it rides the END of that axis (proportional)
|
|
207
|
+
// at a FIXED gap from it — the same anchor-plus-offset a declared background
|
|
208
|
+
// writes as `backgroundPoint`. The glyph and the gap then scale together, so
|
|
209
|
+
// the letter sits the same distance from its axis at every ratio.
|
|
179
210
|
if (model.showAxisLabels) {
|
|
180
211
|
ctx.fillStyle = COLORS.axisLabel;
|
|
181
|
-
ctx.font = `italic 700 ${AXIS_LABELS.size *
|
|
212
|
+
ctx.font = `italic 700 ${AXIS_LABELS.size * k}px Georgia, serif`;
|
|
182
213
|
ctx.textAlign = 'left';
|
|
183
214
|
ctx.textBaseline = 'alphabetic';
|
|
184
|
-
|
|
185
|
-
|
|
215
|
+
for (const letter of [AXIS_LABELS.e, AXIS_LABELS.t]) {
|
|
216
|
+
ctx.fillText(letter.text, ax(letter.at[0]) + letter.dx * k, ay(letter.at[1]) + letter.dy * k);
|
|
217
|
+
}
|
|
186
218
|
}
|
|
187
219
|
};
|
|
188
220
|
export const EstuarineRendererExtension = ElementRendererExtension('estuarine', estuarine);
|
|
@@ -1,11 +1,19 @@
|
|
|
1
|
+
import type { EditableBackgroundLabel } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { FrameworkBackgroundView } from '@formicoidea/labre-core/blocks/surface';
|
|
1
3
|
import type { EstuarineElementModel } from '@formicoidea/labre-core/model';
|
|
2
|
-
import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
|
|
3
4
|
/**
|
|
4
5
|
* View for the Estuarine 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`: the map is a reproduction of the official SVG with
|
|
10
|
+
* a stretch of its own and no `FrameworkBackgroundDef` behind it, so it says
|
|
11
|
+
* where its three legends are through the very fit the renderer anchors them
|
|
12
|
+
* with. The gesture itself is the shared one (issue #355).
|
|
6
13
|
*/
|
|
7
|
-
export declare class EstuarineView extends
|
|
14
|
+
export declare class EstuarineView extends FrameworkBackgroundView<EstuarineElementModel> {
|
|
8
15
|
static type: string;
|
|
16
|
+
protected labelAt(lx: number, ly: number, w: number, h: number): EditableBackgroundLabel | null;
|
|
9
17
|
}
|
|
10
18
|
/**
|
|
11
19
|
* 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 { estuarineFit } from './element-renderer.js';
|
|
5
|
+
import { estuarineLabelHits } from './labels.js';
|
|
2
6
|
/**
|
|
3
7
|
* View for the Estuarine background. Registering it ensures `gfx.view.get(model)`
|
|
4
8
|
* returns a view (required so move / select interactions work).
|
|
9
|
+
*
|
|
10
|
+
* Extends {@link FrameworkBackgroundView} rather than
|
|
11
|
+
* `DeclaredBackgroundView`: the map is a reproduction of the official SVG with
|
|
12
|
+
* a stretch of its own and no `FrameworkBackgroundDef` behind it, so it says
|
|
13
|
+
* where its three legends are through the very fit the renderer anchors them
|
|
14
|
+
* with. The gesture itself is the shared one (issue #355).
|
|
5
15
|
*/
|
|
6
|
-
export class EstuarineView extends
|
|
16
|
+
export class EstuarineView extends FrameworkBackgroundView {
|
|
7
17
|
static { this.type = 'estuarine'; }
|
|
18
|
+
labelAt(lx, ly, w, h) {
|
|
19
|
+
const translate = (wording) => translateKey(this.gfx.std, ...wording);
|
|
20
|
+
const hit = hitTestBackgroundLabel(estuarineLabelHits(this.model, estuarineFit(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,40 @@
|
|
|
1
|
+
import type { BackgroundLabelHit } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import type { EstuarineElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import { LABELS } from './consts.js';
|
|
5
|
+
/** Resolves a wording against the host's catalogue, or falls back to English. */
|
|
6
|
+
export type EstuarineWordingResolver = (wording: ChromeWording) => string;
|
|
7
|
+
/** The three curve legends, in the order the renderer paints them. */
|
|
8
|
+
export type EstuarineLegendKey = keyof typeof LABELS;
|
|
9
|
+
/**
|
|
10
|
+
* Only the factors the legends need out of `EstuarineFit`.
|
|
11
|
+
*
|
|
12
|
+
* Taken as a parameter rather than imported: the fit is computed in
|
|
13
|
+
* `./element-renderer.ts`, which reads this module to know what a legend says,
|
|
14
|
+
* and a module cycle to save two fields would be a poor trade.
|
|
15
|
+
*/
|
|
16
|
+
export interface EstuarineLegendFit {
|
|
17
|
+
sx: number;
|
|
18
|
+
sy: number;
|
|
19
|
+
/** The one undeformed factor — type is never stretched. */
|
|
20
|
+
strokeScale: number;
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* What a legend SAYS: the user's own word, else the vocabulary.
|
|
24
|
+
*
|
|
25
|
+
* The very precedence `backgroundLabelText` applies to a declared background —
|
|
26
|
+
* user text wins over the catalogue, the catalogue over the shipped English —
|
|
27
|
+
* spelled out here because this map has no declaration to apply it for it. An
|
|
28
|
+
* empty string is a real answer (the user erased the word), which is why the
|
|
29
|
+
* test is against `undefined` and not against falsiness.
|
|
30
|
+
*/
|
|
31
|
+
export declare function estuarineLegendText(model: EstuarineElementModel, key: EstuarineLegendKey, translate: EstuarineWordingResolver): string;
|
|
32
|
+
/**
|
|
33
|
+
* Clickable boxes of the visible legends, in ELEMENT-LOCAL coordinates.
|
|
34
|
+
*
|
|
35
|
+
* Anchored through the same fit the renderer anchors them with: proportionally
|
|
36
|
+
* in both directions — through the same cropped window (`REF_X` / `REF_Y`) —
|
|
37
|
+
* and typed isotropically. A legend whose curve is switched off is not painted
|
|
38
|
+
* and is therefore not aimable either.
|
|
39
|
+
*/
|
|
40
|
+
export declare function estuarineLabelHits(model: EstuarineElementModel, fit: EstuarineLegendFit, translate: EstuarineWordingResolver): BackgroundLabelHit[];
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { backgroundTextHitBox } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
+
import { LABEL_LETTER_SPACING, LABELS, REF_X, REF_Y } from './consts.js';
|
|
3
|
+
/**
|
|
4
|
+
* What a legend SAYS: the user's own word, else the vocabulary.
|
|
5
|
+
*
|
|
6
|
+
* The very precedence `backgroundLabelText` applies to a declared background —
|
|
7
|
+
* user text wins over the catalogue, the catalogue over the shipped English —
|
|
8
|
+
* spelled out here because this map has no declaration to apply it for it. An
|
|
9
|
+
* empty string is a real answer (the user erased the word), which is why the
|
|
10
|
+
* test is against `undefined` and not against falsiness.
|
|
11
|
+
*/
|
|
12
|
+
export function estuarineLegendText(model, key, translate) {
|
|
13
|
+
const label = LABELS[key];
|
|
14
|
+
const own = model[label.prop];
|
|
15
|
+
return own === undefined || own === null
|
|
16
|
+
? translate(label.wording)
|
|
17
|
+
: String(own);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Clickable boxes of the visible legends, in ELEMENT-LOCAL coordinates.
|
|
21
|
+
*
|
|
22
|
+
* Anchored through the same fit the renderer anchors them with: proportionally
|
|
23
|
+
* in both directions — through the same cropped window (`REF_X` / `REF_Y`) —
|
|
24
|
+
* and typed isotropically. A legend whose curve is switched off is not painted
|
|
25
|
+
* and is therefore not aimable either.
|
|
26
|
+
*/
|
|
27
|
+
export function estuarineLabelHits(model, fit, translate) {
|
|
28
|
+
const props = model;
|
|
29
|
+
const hits = [];
|
|
30
|
+
for (const key of Object.keys(LABELS)) {
|
|
31
|
+
const label = LABELS[key];
|
|
32
|
+
if (!props[label.visibleProp])
|
|
33
|
+
continue;
|
|
34
|
+
const text = estuarineLegendText(model, key, translate);
|
|
35
|
+
const box = backgroundTextHitBox(text, label.size * fit.strokeScale, (label.x - REF_X) * fit.sx, (label.y - REF_Y) * fit.sy, 'center');
|
|
36
|
+
// A legend is letter-spaced, so it is wider than the shared estimate: half
|
|
37
|
+
// of the extra width on each side, since the word is centred on its anchor.
|
|
38
|
+
const spread = (Math.max(text.length - 1, 0) * LABEL_LETTER_SPACING * fit.strokeScale) /
|
|
39
|
+
2;
|
|
40
|
+
hits.push({
|
|
41
|
+
id: key,
|
|
42
|
+
prop: label.prop,
|
|
43
|
+
text,
|
|
44
|
+
...box,
|
|
45
|
+
minX: box.minX - spread,
|
|
46
|
+
maxX: box.maxX + spread,
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return hits;
|
|
50
|
+
}
|
package/dist/presets.d.ts
CHANGED
|
@@ -8,9 +8,20 @@
|
|
|
8
8
|
* the estuarine background was drawn at scale 1 while the button drew it at
|
|
9
9
|
* 1.2. Derived from one table, neither can happen again.
|
|
10
10
|
*/
|
|
11
|
-
/**
|
|
11
|
+
/**
|
|
12
|
+
* Estuarine map default scale: how many canvas units one reference unit of the
|
|
13
|
+
* drawing is worth when the map is born. The DRAWING's size, in other words —
|
|
14
|
+
* which is why it is this number, and not the birth box, that is the constant.
|
|
15
|
+
*/
|
|
12
16
|
export declare const MAP_SCALE = 1.2;
|
|
13
|
-
/**
|
|
17
|
+
/**
|
|
18
|
+
* The box an estuarine map is born in — the size the toolbox draws.
|
|
19
|
+
*
|
|
20
|
+
* Derived from the CROPPED reference box (`estuarine/consts.ts`), so the board
|
|
21
|
+
* is born just big enough to hold its drawing plus a thin margin. It shrank
|
|
22
|
+
* when the crop tightened (828 × 961.2 → 790.8 × 943.8) and the drawing inside
|
|
23
|
+
* it did not change size by a single unit — `MAP_SCALE` is still 1.2.
|
|
24
|
+
*/
|
|
14
25
|
export declare const ESTUARINE_MAP_W: number;
|
|
15
26
|
export declare const ESTUARINE_MAP_H: number;
|
|
16
27
|
/** The box a Cynefin frame is born in. */
|
package/dist/presets.js
CHANGED
|
@@ -12,9 +12,20 @@ import { ESTUARINE_ROLE } from './estuarine/roles.js';
|
|
|
12
12
|
* the estuarine background was drawn at scale 1 while the button drew it at
|
|
13
13
|
* 1.2. Derived from one table, neither can happen again.
|
|
14
14
|
*/
|
|
15
|
-
/**
|
|
15
|
+
/**
|
|
16
|
+
* Estuarine map default scale: how many canvas units one reference unit of the
|
|
17
|
+
* drawing is worth when the map is born. The DRAWING's size, in other words —
|
|
18
|
+
* which is why it is this number, and not the birth box, that is the constant.
|
|
19
|
+
*/
|
|
16
20
|
export const MAP_SCALE = 1.2;
|
|
17
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* The box an estuarine map is born in — the size the toolbox draws.
|
|
23
|
+
*
|
|
24
|
+
* Derived from the CROPPED reference box (`estuarine/consts.ts`), so the board
|
|
25
|
+
* is born just big enough to hold its drawing plus a thin margin. It shrank
|
|
26
|
+
* when the crop tightened (828 × 961.2 → 790.8 × 943.8) and the drawing inside
|
|
27
|
+
* it did not change size by a single unit — `MAP_SCALE` is still 1.2.
|
|
28
|
+
*/
|
|
18
29
|
export const ESTUARINE_MAP_W = EST_W * MAP_SCALE;
|
|
19
30
|
export const ESTUARINE_MAP_H = EST_H * MAP_SCALE;
|
|
20
31
|
/** The box a Cynefin frame is born in. */
|
package/dist/templates/index.js
CHANGED
|
@@ -3,7 +3,7 @@ import { FontFamily, ShapeStyle, TextAlign } from '@formicoidea/labre-core/model
|
|
|
3
3
|
import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
|
|
4
4
|
import { translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
5
5
|
import { cynefinEstuarineCommands } from '../commands.js';
|
|
6
|
-
import { HEX_SIZE } from '../estuarine/consts.js';
|
|
6
|
+
import { HEX_SIZE, REF_X, REF_Y } from '../estuarine/consts.js';
|
|
7
7
|
import { CYNEFIN_H, CYNEFIN_W, cynefinBackgroundProps, ESTUARINE_MAP_H, ESTUARINE_MAP_W, estuarineHexagonBox, estuarineHexagonProps, estuarineMapProps, MAP_SCALE, } from '../presets.js';
|
|
8
8
|
/**
|
|
9
9
|
* The Cynefin / Estuarine palettes — DERIVED from the toolbox, one template per
|
|
@@ -12,8 +12,8 @@ import { CYNEFIN_H, CYNEFIN_W, cynefinBackgroundProps, ESTUARINE_MAP_H, ESTUARIN
|
|
|
12
12
|
* Every single-artefact entry below is what its command actually draws, run
|
|
13
13
|
* once against a recording surface. Hand-written, they had drifted exactly as
|
|
14
14
|
* far as a copy drifts: the hexagons had lost the `textFitMode` #52 gave them,
|
|
15
|
-
* and the estuarine background was drawn at scale 1
|
|
16
|
-
*
|
|
15
|
+
* and the estuarine background was drawn at scale 1 where the button draws it
|
|
16
|
+
* at `MAP_SCALE`. Derived, neither can happen again — and
|
|
17
17
|
* `templates-parity.unit.spec.ts` re-runs each command and compares.
|
|
18
18
|
*
|
|
19
19
|
* The two COMPOSITIONS stay hand-written: a sorting board and a constraint map
|
|
@@ -47,12 +47,19 @@ function sticky(x, y, text) {
|
|
|
47
47
|
};
|
|
48
48
|
}
|
|
49
49
|
/**
|
|
50
|
-
* A constraint on the shipped map, placed by its CENTRE in the map's
|
|
51
|
-
* space (690×801
|
|
52
|
-
*
|
|
50
|
+
* A constraint on the shipped map, placed by its CENTRE in the map's AUTHORED
|
|
51
|
+
* space (the 690×801 the drawing was laid out in), carried onto the box the map
|
|
52
|
+
* is actually born at.
|
|
53
|
+
*
|
|
54
|
+
* `- REF_X` / `- REF_Y` is the map's crop: the board's own origin is authored
|
|
55
|
+
* `(REF_X, REF_Y)`, not `(0, 0)`, so a hexagon placed on a curve stays on that
|
|
56
|
+
* curve. Derived, never restated — tightening the crop moves the drawing and
|
|
57
|
+
* the constraints on it together.
|
|
53
58
|
*/
|
|
59
|
+
const mapX = (cx) => (cx - REF_X) * MAP_SCALE;
|
|
60
|
+
const mapY = (cy) => (cy - REF_Y) * MAP_SCALE;
|
|
54
61
|
const hexAt = (cx, cy) => estuarineHexagonProps({
|
|
55
|
-
xywh: estuarineHexagonBox(cx
|
|
62
|
+
xywh: estuarineHexagonBox(mapX(cx), mapY(cy)),
|
|
56
63
|
});
|
|
57
64
|
const CAPTION_W = 120;
|
|
58
65
|
const CAPTION_H = 24;
|
|
@@ -69,8 +76,8 @@ const CAPTION_GAP = 8;
|
|
|
69
76
|
* Neutral, and staying so: a name is nobody's artefact.
|
|
70
77
|
*/
|
|
71
78
|
function caption(cx, cy, str) {
|
|
72
|
-
const x = cx
|
|
73
|
-
const y = cy
|
|
79
|
+
const x = mapX(cx) - CAPTION_W / 2;
|
|
80
|
+
const y = mapY(cy) + HEX_SIZE / 2 + CAPTION_GAP;
|
|
74
81
|
return {
|
|
75
82
|
type: 'text',
|
|
76
83
|
text: surfaceText(str),
|
|
@@ -171,7 +178,7 @@ function tr(std, wording) {
|
|
|
171
178
|
* A hand-composed template — what is left once the artefacts are derived.
|
|
172
179
|
*
|
|
173
180
|
* The seeds a template writes into the document go through the translation
|
|
174
|
-
* seam at placement (ADR
|
|
181
|
+
* seam at placement (ADR 0023); `content` stays the English build, and
|
|
175
182
|
* `localize` rebuilds the same snapshot with translated seeds.
|
|
176
183
|
*/
|
|
177
184
|
function tpl(name, preview, build, nameKey) {
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { FrameworkPalette, NamedPalette } from '@formicoidea/labre-core/components/color-picker';
|
|
2
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
3
|
+
/**
|
|
4
|
+
* The Cynefin / Estuarine swatches' own names, resolved by
|
|
5
|
+
* `resolvePaletteLabel` (`@labre/affine-components/color-picker`) through each
|
|
6
|
+
* swatch's own {@link NamedPalette.labelWording} — the mechanism
|
|
7
|
+
* `WARDLEY_PALETTE_WORDINGS` established, reused here without either package
|
|
8
|
+
* naming the other.
|
|
9
|
+
*
|
|
10
|
+
* Exported so `../translations.ts` can list them under source `chrome`: a
|
|
11
|
+
* swatch's name is toolbar chrome, re-rendered on every locale switch, never
|
|
12
|
+
* seeded into a document.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_WORDING_ITERATE: ChromeWording;
|
|
15
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL: ChromeWording;
|
|
16
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL_DARK: ChromeWording;
|
|
17
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_WORDING_VOLATILE: ChromeWording;
|
|
18
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_WORDING_AXIS: ChromeWording;
|
|
19
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_WORDINGS: readonly ChromeWording[];
|
|
20
|
+
/**
|
|
21
|
+
* From the default editor palette we keep ONLY the neutrals — the historical
|
|
22
|
+
* colours say nothing in this notation.
|
|
23
|
+
*/
|
|
24
|
+
export declare const CYNEFIN_ESTUARINE_PALETTE_LIST: NamedPalette[];
|
|
25
|
+
/**
|
|
26
|
+
* This pack's page of the colour pickers' carousel (`docs/adr/0027`).
|
|
27
|
+
* Registered from the FLAG-GATED view extension, because offering hues is
|
|
28
|
+
* TOOLING: a board drawn while the flag was on keeps every colour it was
|
|
29
|
+
* painted with when the flag goes off (`docs/adr/0009`).
|
|
30
|
+
*/
|
|
31
|
+
export declare const CYNEFIN_ESTUARINE_FRAMEWORK_PALETTE: FrameworkPalette;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import { neutralPalettes } from '@formicoidea/labre-core/gfx/shape';
|
|
2
|
+
import { COLORS as CYNEFIN_COLORS } from '../cynefin/consts.js';
|
|
3
|
+
import { COLORS as ESTUARINE_COLORS } from '../estuarine/consts.js';
|
|
4
|
+
/**
|
|
5
|
+
* The Cynefin / Estuarine swatches' own names, resolved by
|
|
6
|
+
* `resolvePaletteLabel` (`@labre/affine-components/color-picker`) through each
|
|
7
|
+
* swatch's own {@link NamedPalette.labelWording} — the mechanism
|
|
8
|
+
* `WARDLEY_PALETTE_WORDINGS` established, reused here without either package
|
|
9
|
+
* naming the other.
|
|
10
|
+
*
|
|
11
|
+
* Exported so `../translations.ts` can list them under source `chrome`: a
|
|
12
|
+
* swatch's name is toolbar chrome, re-rendered on every locale switch, never
|
|
13
|
+
* seeded into a document.
|
|
14
|
+
*/
|
|
15
|
+
export const CYNEFIN_ESTUARINE_PALETTE_WORDING_ITERATE = [
|
|
16
|
+
'com.labre.cynefin.palette.iterate-teal',
|
|
17
|
+
'Iterate teal',
|
|
18
|
+
];
|
|
19
|
+
export const CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL = [
|
|
20
|
+
'com.labre.estuarine.palette.liminal-green',
|
|
21
|
+
'Liminal green',
|
|
22
|
+
];
|
|
23
|
+
export const CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL_DARK = [
|
|
24
|
+
'com.labre.estuarine.palette.liminal-green-dark',
|
|
25
|
+
'Liminal green dark',
|
|
26
|
+
];
|
|
27
|
+
export const CYNEFIN_ESTUARINE_PALETTE_WORDING_VOLATILE = [
|
|
28
|
+
'com.labre.estuarine.palette.volatile-red',
|
|
29
|
+
'Volatile red',
|
|
30
|
+
];
|
|
31
|
+
export const CYNEFIN_ESTUARINE_PALETTE_WORDING_AXIS = [
|
|
32
|
+
'com.labre.estuarine.palette.axis-plum',
|
|
33
|
+
'Axis plum',
|
|
34
|
+
];
|
|
35
|
+
export const CYNEFIN_ESTUARINE_PALETTE_WORDINGS = [
|
|
36
|
+
CYNEFIN_ESTUARINE_PALETTE_WORDING_ITERATE,
|
|
37
|
+
CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL,
|
|
38
|
+
CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL_DARK,
|
|
39
|
+
CYNEFIN_ESTUARINE_PALETTE_WORDING_VOLATILE,
|
|
40
|
+
CYNEFIN_ESTUARINE_PALETTE_WORDING_AXIS,
|
|
41
|
+
];
|
|
42
|
+
/**
|
|
43
|
+
* The hues the two boards of this pack are actually drawn in.
|
|
44
|
+
*
|
|
45
|
+
* ONE page for two diagrams, because they are one framework id
|
|
46
|
+
* (`cynefin-estuarine`) and an author works across both. Cynefin contributes
|
|
47
|
+
* the teal of its "iterate" curve — the only colour on a frame whose other
|
|
48
|
+
* inks are the shared notation neutrals — and Estuarine the three its curve
|
|
49
|
+
* legends are drawn in plus the plum of its two axes.
|
|
50
|
+
*
|
|
51
|
+
* Every value is read off the two `COLORS` tables rather than restated, so the
|
|
52
|
+
* shelf and the painted frames cannot drift apart. They are SHORTCUTS, never
|
|
53
|
+
* constraints: nothing on either board reads a colour.
|
|
54
|
+
*/
|
|
55
|
+
const CYNEFIN_ESTUARINE_PALETTES = [
|
|
56
|
+
{
|
|
57
|
+
key: 'Iterate teal',
|
|
58
|
+
value: CYNEFIN_COLORS.teal,
|
|
59
|
+
labelWording: CYNEFIN_ESTUARINE_PALETTE_WORDING_ITERATE,
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
key: 'Liminal green',
|
|
63
|
+
value: ESTUARINE_COLORS.liminal,
|
|
64
|
+
labelWording: CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL,
|
|
65
|
+
},
|
|
66
|
+
// The LIMINAL legend's own darker green — the curve's colour is too light to
|
|
67
|
+
// read as words over the map.
|
|
68
|
+
{
|
|
69
|
+
key: 'Liminal green dark',
|
|
70
|
+
value: ESTUARINE_COLORS.liminalLabel,
|
|
71
|
+
labelWording: CYNEFIN_ESTUARINE_PALETTE_WORDING_LIMINAL_DARK,
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
key: 'Volatile red',
|
|
75
|
+
value: ESTUARINE_COLORS.volatile,
|
|
76
|
+
labelWording: CYNEFIN_ESTUARINE_PALETTE_WORDING_VOLATILE,
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
key: 'Axis plum',
|
|
80
|
+
value: ESTUARINE_COLORS.axis,
|
|
81
|
+
labelWording: CYNEFIN_ESTUARINE_PALETTE_WORDING_AXIS,
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
/**
|
|
85
|
+
* From the default editor palette we keep ONLY the neutrals — the historical
|
|
86
|
+
* colours say nothing in this notation.
|
|
87
|
+
*/
|
|
88
|
+
export const CYNEFIN_ESTUARINE_PALETTE_LIST = [
|
|
89
|
+
...CYNEFIN_ESTUARINE_PALETTES,
|
|
90
|
+
...neutralPalettes(),
|
|
91
|
+
];
|
|
92
|
+
/**
|
|
93
|
+
* This pack's page of the colour pickers' carousel (`docs/adr/0027`).
|
|
94
|
+
* Registered from the FLAG-GATED view extension, because offering hues is
|
|
95
|
+
* TOOLING: a board drawn while the flag was on keeps every colour it was
|
|
96
|
+
* painted with when the flag goes off (`docs/adr/0009`).
|
|
97
|
+
*/
|
|
98
|
+
export const CYNEFIN_ESTUARINE_FRAMEWORK_PALETTE = {
|
|
99
|
+
framework: 'cynefin-estuarine',
|
|
100
|
+
labelWording: [
|
|
101
|
+
'com.labre.framework.cynefin-estuarine',
|
|
102
|
+
'Cynefin / Estuarine',
|
|
103
|
+
],
|
|
104
|
+
palettes: CYNEFIN_ESTUARINE_PALETTE_LIST,
|
|
105
|
+
};
|
|
@@ -7,6 +7,8 @@ export declare class EdgelessCynefinEstuarineSeniorButton extends EdgelessCynefi
|
|
|
7
7
|
enableActiveBackground: boolean;
|
|
8
8
|
type: typeof EmptyTool;
|
|
9
9
|
private _toggleMenu;
|
|
10
|
+
/** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
|
|
11
|
+
private get _armed();
|
|
10
12
|
render(): import("lit-html").TemplateResult<1>;
|
|
11
13
|
}
|
|
12
14
|
export {};
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
3
3
|
import { translateKey } from '@formicoidea/labre-core/shared/services';
|
|
4
|
-
import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
4
|
+
import { armedArtefact, EdgelessToolbarToolMixin, } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
5
5
|
import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
|
|
6
6
|
import { css, html, LitElement } from 'lit';
|
|
7
7
|
import { cynefinToolbarIcon } from './icons.js';
|
|
@@ -62,6 +62,10 @@ export class EdgelessCynefinEstuarineSeniorButton extends EdgelessToolbarToolMix
|
|
|
62
62
|
const menu = this.createPopper('edgeless-cynefin-estuarine-menu', this);
|
|
63
63
|
menu.element.edgeless = this.edgeless;
|
|
64
64
|
}
|
|
65
|
+
/** Lit while one of THIS framework's artefacts is armed. See the Wardley one. */
|
|
66
|
+
get _armed() {
|
|
67
|
+
return armedArtefact(this.gfx)?.owner === 'cynefin-estuarine';
|
|
68
|
+
}
|
|
65
69
|
render() {
|
|
66
70
|
return html `<edgeless-toolbar-button
|
|
67
71
|
class="ce-button"
|
|
@@ -69,7 +73,7 @@ export class EdgelessCynefinEstuarineSeniorButton extends EdgelessToolbarToolMix
|
|
|
69
73
|
? ''
|
|
70
74
|
: translateKey(this.edgeless.std, 'com.labre.framework.cynefin-estuarine', 'Cynefin / Estuarine')}
|
|
71
75
|
.tooltipOffset=${4}
|
|
72
|
-
.active=${!!this.popper}
|
|
76
|
+
.active=${!!this.popper || this._armed}
|
|
73
77
|
@click=${this._toggleMenu}
|
|
74
78
|
>
|
|
75
79
|
<div class="ce-root">
|
package/dist/translations.js
CHANGED
|
@@ -8,6 +8,7 @@ import { ESTUARINE_READING } from './estuarine/reading.js';
|
|
|
8
8
|
import { ESTUARINE_ROLES } from './estuarine/roles.js';
|
|
9
9
|
import { ESTUARINE_TOOLBAR_WORDINGS } from './estuarine/toolbar/config.js';
|
|
10
10
|
import { CYNEFIN_ESTUARINE_TEMPLATE_CATEGORY_WORDINGS, CYNEFIN_ESTUARINE_TEMPLATE_NAME_WORDINGS, CYNEFIN_ESTUARINE_TEMPLATE_SEEDS, } from './templates/index.js';
|
|
11
|
+
import { CYNEFIN_ESTUARINE_PALETTE_WORDINGS } from './toolbar/palette.js';
|
|
11
12
|
/**
|
|
12
13
|
* THIS framework's contribution to the translation-key manifest.
|
|
13
14
|
*
|
|
@@ -76,4 +77,12 @@ ESTUARINE_CANVAS_WORDINGS.map(([key, fallback]) => ({
|
|
|
76
77
|
key,
|
|
77
78
|
fallback,
|
|
78
79
|
source: 'chrome',
|
|
80
|
+
})),
|
|
81
|
+
// The carousel swatches' own names (`toolbar/palette.ts`) — chrome,
|
|
82
|
+
// re-rendered by the colour picker on every locale switch, never seeded
|
|
83
|
+
// into a document. Same treatment as Wardley's and EDGY's.
|
|
84
|
+
CYNEFIN_ESTUARINE_PALETTE_WORDINGS.map(([key, fallback]) => ({
|
|
85
|
+
key,
|
|
86
|
+
fallback,
|
|
87
|
+
source: 'chrome',
|
|
79
88
|
})));
|
package/dist/utils.d.ts
CHANGED
|
@@ -1,11 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Uniform fit of a fixed reference design
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Uniform fit of a fixed reference design into an element, THE one in the
|
|
3
|
+
* library: `@labre/affine-block-surface`'s `refScale`. Re-exported here because
|
|
4
|
+
* both diagrams of this package draw an authored artwork and have always called
|
|
5
|
+
* it by this name — the body used to be copied here, and in two other packages
|
|
6
|
+
* besides (R37, one source of truth).
|
|
5
7
|
*/
|
|
6
|
-
export
|
|
7
|
-
s: number;
|
|
8
|
-
ox: number;
|
|
9
|
-
oy: number;
|
|
10
|
-
};
|
|
8
|
+
export { refScale } from '@formicoidea/labre-core/blocks/surface';
|
|
11
9
|
export declare const FONT_FAMILY = "Inter, sans-serif";
|
package/dist/utils.js
CHANGED
|
@@ -1,10 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Uniform fit of a fixed reference design
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
* Uniform fit of a fixed reference design into an element, THE one in the
|
|
3
|
+
* library: `@labre/affine-block-surface`'s `refScale`. Re-exported here because
|
|
4
|
+
* both diagrams of this package draw an authored artwork and have always called
|
|
5
|
+
* it by this name — the body used to be copied here, and in two other packages
|
|
6
|
+
* besides (R37, one source of truth).
|
|
5
7
|
*/
|
|
6
|
-
export
|
|
7
|
-
const s = Math.min(w / refW, h / refH);
|
|
8
|
-
return { s, ox: (w - refW * s) / 2, oy: (h - refH * s) / 2 };
|
|
9
|
-
}
|
|
8
|
+
export { refScale } from '@formicoidea/labre-core/blocks/surface';
|
|
10
9
|
export const FONT_FAMILY = 'Inter, sans-serif';
|