@formicoidea/labre-framework-cynefin 0.42.0 → 0.45.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,6 @@
1
+ import type { SerializedXYWH } from '@formicoidea/labre-core/global/gfx';
2
+ /**
3
+ * The frame of `xywh` brought back onto the drawing it letterboxes, or `null`
4
+ * when it is already on it.
5
+ */
6
+ export declare function cynefinCroppedXYWH(xywh: SerializedXYWH): SerializedXYWH | null;
@@ -0,0 +1,45 @@
1
+ import { Bound } from '@formicoidea/labre-core/global/gfx';
2
+ import { refScale } from '../utils.js';
3
+ import { REF_H, REF_W } from './consts.js';
4
+ /**
5
+ * Cropping a Cynefin frame back onto its drawing, at the end of a resize.
6
+ *
7
+ * The diagram is a figurative reproduction of the official SVG, so it cannot be
8
+ * stretched: `element-renderer.ts` fits it uniformly and centres what is left
9
+ * over (`refScale`). Dragged off its 1080 × 777 proportion, the element
10
+ * therefore letterboxes — at 1600 × 600 there are FOUR HUNDRED model units of
11
+ * nothing either side of the picture. And a framework background is caught by
12
+ * its border and by nothing else
13
+ * (`model/src/elements/framework-background/hit-test.ts`, a band of `10 / zoom`
14
+ * around the `xywh`), so the frame the user has to aim at ends up hundreds of
15
+ * units away from anything drawn.
16
+ *
17
+ * The answer the PO chose (23/09/2026) is not to stretch the drawing and not to
18
+ * widen the hit test, but to bring the FRAME back to the picture when the user
19
+ * lets go of a resize handle. Nothing is deformed and nothing is truncated: the
20
+ * element simply stops claiming room it does not paint.
21
+ *
22
+ * Why it is safe under rotation: a uniform fit centres what it fits, so
23
+ * `ox + REF_W * s / 2 === w / 2`. The cropped box has the SAME CENTRE as the one
24
+ * it replaces, and a surface element rotates about its centre — so the drawing
25
+ * does not move, at any angle.
26
+ */
27
+ /**
28
+ * Below half a model unit there is nothing to see, and writing anyway would
29
+ * cost an undo entry for a gesture that changed nothing. It is also what makes
30
+ * the crop idempotent: a frame already cropped is left alone.
31
+ */
32
+ const CROP_EPSILON = 0.5;
33
+ /**
34
+ * The frame of `xywh` brought back onto the drawing it letterboxes, or `null`
35
+ * when it is already on it.
36
+ */
37
+ export function cynefinCroppedXYWH(xywh) {
38
+ const bound = Bound.deserialize(xywh);
39
+ if (!(bound.w > 0) || !(bound.h > 0))
40
+ return null;
41
+ const { s, ox, oy } = refScale(bound.w, bound.h, REF_W, REF_H);
42
+ if (ox < CROP_EPSILON && oy < CROP_EPSILON)
43
+ return null;
44
+ return new Bound(bound.x + ox, bound.y + oy, REF_W * s, REF_H * s).serialize();
45
+ }
@@ -25,6 +25,6 @@ import type { CynefinElementModel } from '@formicoidea/labre-core/model';
25
25
  * to split it would be inventing typography the translator never asked for.
26
26
  */
27
27
  export declare const cynefin: ElementRenderer<CynefinElementModel>;
28
- export declare const CynefinRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
28
+ export declare const CynefinRendererExtension: import("@formicoidea/labre-core/_pkgs/store").ExtensionType & {
29
29
  identifier: import("@formicoidea/labre-core/_pkgs/global/di").ServiceIdentifier<ElementRenderer<CynefinElementModel>>;
30
30
  };
@@ -17,7 +17,35 @@ export declare class CynefinView extends FrameworkBackgroundView<CynefinElementM
17
17
  protected labelAt(lx: number, ly: number, w: number, h: number): EditableBackgroundLabel | null;
18
18
  }
19
19
  /**
20
- * Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
21
- * true (toggled from the toolbar). Moving / selecting stays available.
20
+ * Resize gating and, when the handle is let go, the crop.
21
+ *
22
+ * ## Gating
23
+ *
24
+ * The resize handles are hidden unless `model.resizeEnabled` is true (toggled
25
+ * from the toolbar). Moving / selecting stays available.
26
+ *
27
+ * ## The crop
28
+ *
29
+ * The diagram is a fixed drawing fitted uniformly into the element, so dragged
30
+ * off its proportion the element letterboxes: 400 model units of nothing either
31
+ * side at 1600 × 600. `onResizeEnd` brings the frame back onto the picture (see
32
+ * `./crop.ts`) — the drawing is neither deformed nor truncated, the element
33
+ * simply stops claiming room it does not paint.
34
+ *
35
+ * ## Why here, and why one undo step
36
+ *
37
+ * A resize STASHES `xywh` on the way in, writes the model on every move, and
38
+ * commits the stash in `onResizeEnd`, inside the manager's own
39
+ * `store.transact` (`std/gfx/interactivity/manager.ts`). Writing the cropped
40
+ * box BEFORE calling `default` therefore changes what is committed rather than
41
+ * adding a second write: the whole gesture stays one Yjs write, and one undo
42
+ * step. `captureSync` would do the opposite here — it would close the step and
43
+ * make the crop a second one.
44
+ *
45
+ * This seam is also what keeps the crop a GESTURE. A cascade watching `xywh`
46
+ * would fire on a peer's resize too and re-crop, on every screen, a frame
47
+ * nobody on that screen touched; and it would fire on a document being loaded.
48
+ * The readonly guard is belt and braces — `handleElementResize` already refuses
49
+ * to start in a readonly store — but it is the invariant, so it is stated.
22
50
  */
23
- export declare const CynefinInteraction: import("@formicoidea/labre-core/store").ExtensionType;
51
+ export declare const CynefinInteraction: import("@formicoidea/labre-core/_pkgs/store").ExtensionType;
@@ -1,6 +1,7 @@
1
1
  import { FrameworkBackgroundView, hitTestBackgroundLabel, } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { translateKey, } from '@formicoidea/labre-core/shared/services';
3
3
  import { GfxViewInteractionExtension } from '@formicoidea/labre-core/std/gfx';
4
+ import { cynefinCroppedXYWH } from './crop.js';
4
5
  import { cynefinLabelHits } from './labels.js';
5
6
  /**
6
7
  * View for the Cynefin background. Registering it ensures `gfx.view.get(model)`
@@ -22,17 +23,55 @@ export class CynefinView extends FrameworkBackgroundView {
22
23
  }
23
24
  }
24
25
  /**
25
- * Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
26
- * true (toggled from the toolbar). Moving / selecting stays available.
26
+ * Resize gating and, when the handle is let go, the crop.
27
+ *
28
+ * ## Gating
29
+ *
30
+ * The resize handles are hidden unless `model.resizeEnabled` is true (toggled
31
+ * from the toolbar). Moving / selecting stays available.
32
+ *
33
+ * ## The crop
34
+ *
35
+ * The diagram is a fixed drawing fitted uniformly into the element, so dragged
36
+ * off its proportion the element letterboxes: 400 model units of nothing either
37
+ * side at 1600 × 600. `onResizeEnd` brings the frame back onto the picture (see
38
+ * `./crop.ts`) — the drawing is neither deformed nor truncated, the element
39
+ * simply stops claiming room it does not paint.
40
+ *
41
+ * ## Why here, and why one undo step
42
+ *
43
+ * A resize STASHES `xywh` on the way in, writes the model on every move, and
44
+ * commits the stash in `onResizeEnd`, inside the manager's own
45
+ * `store.transact` (`std/gfx/interactivity/manager.ts`). Writing the cropped
46
+ * box BEFORE calling `default` therefore changes what is committed rather than
47
+ * adding a second write: the whole gesture stays one Yjs write, and one undo
48
+ * step. `captureSync` would do the opposite here — it would close the step and
49
+ * make the crop a second one.
50
+ *
51
+ * This seam is also what keeps the crop a GESTURE. A cascade watching `xywh`
52
+ * would fire on a peer's resize too and re-crop, on every screen, a frame
53
+ * nobody on that screen touched; and it would fire on a document being loaded.
54
+ * The readonly guard is belt and braces — `handleElementResize` already refuses
55
+ * to start in a readonly store — but it is the invariant, so it is stated.
27
56
  */
28
57
  export const CynefinInteraction = GfxViewInteractionExtension(CynefinView.type, {
29
- handleResize({ model }) {
58
+ handleResize({ std, model }) {
30
59
  return {
31
60
  beforeResize({ set }) {
32
61
  if (!model.resizeEnabled) {
33
62
  set({ allowedHandlers: [] });
34
63
  }
35
64
  },
65
+ onResizeEnd(context) {
66
+ if (!std.store.readonly) {
67
+ const cropped = cynefinCroppedXYWH(model.xywh);
68
+ // No write at all when the frame is already on the drawing: an
69
+ // undo entry for a gesture that changed nothing is a bug.
70
+ if (cropped)
71
+ model.xywh = cropped;
72
+ }
73
+ context.default(context);
74
+ },
36
75
  };
37
76
  },
38
77
  });
@@ -34,4 +34,4 @@ export declare const cynefinToolbarConfig: {
34
34
  }];
35
35
  readonly when: (ctx: ToolbarContext) => boolean;
36
36
  };
37
- export declare const cynefinToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
37
+ export declare const cynefinToolbarExtension: import("@formicoidea/labre-core/_pkgs/store").ExtensionType;
@@ -1,13 +1,14 @@
1
1
  import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
2
2
  /**
3
3
  * Visual constants for the Estuarine framework map, reproduced from the official
4
- * SVG (viewBox 0 0 690 801). All geometry is authored in that fixed reference
5
- * space; the renderer reads every coordinate below as a RATIO of it and maps it
6
- * onto the element's real width and height independently, so a stretched map
7
- * gets a longer time axis and a taller energy axis rather than the same drawing
8
- * letterboxed (see `EstuarineFit` in `./element-renderer.ts`). The e axis is
9
- * vertical & double-headed (energy), the t axis horizontal & single-headed
10
- * (time only flows one way).
4
+ * SVG (viewBox 0 0 690 801). All geometry is authored in that fixed space; the
5
+ * renderer reads every coordinate below as a RATIO of the CROPPED window onto
6
+ * it ({@link REF_X} … {@link REF_H}) and maps it onto the element's real width
7
+ * and height independently, so a stretched map gets a longer time axis and a
8
+ * taller energy axis rather than the same drawing letterboxed (see
9
+ * `EstuarineFit` in `./element-renderer.ts`). The e axis is vertical &
10
+ * double-headed (energy), the t axis horizontal & single-headed (time only
11
+ * flows one way).
11
12
  *
12
13
  * The three curve legends below (`LABELS`) carry a `wording`, resolved by
13
14
  * `element-renderer.ts` through the `CanvasRenderer` it is handed at paint
@@ -15,8 +16,40 @@ import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
15
16
  * italic `e` / `t` axis LETTERS (`AXIS_LABELS`) are notation, not words, and
16
17
  * are deliberately NOT keyed (PO decision).
17
18
  */
18
- export declare const REF_W = 690;
19
- export declare const REF_H = 801;
19
+ /**
20
+ * The CROPPED window onto the authored 690 × 801 SVG space — the rectangle the
21
+ * element's bounds are mapped onto.
22
+ *
23
+ * The SVG's own viewBox is loose around the drawing: the t axis stops at
24
+ * `x = 616 / 690` and the e axis at `y = 763 / 801`, so a board born on the raw
25
+ * viewBox carried dead space on every side (measured on the painted pixels at
26
+ * birth size: 16.6 left, 12.6 top, 35.5 right, 20.4 bottom, in model units).
27
+ * The board's own border is therefore nowhere near its drawing, which is both
28
+ * ugly and awkward — a background is grabbed BY that border
29
+ * (`framework-background/hit-test.ts`).
30
+ *
31
+ * So the reference box is the ink's bounding box grown by {@link REF_MARGIN},
32
+ * not the viewBox. `REF_X` / `REF_Y` is where that window starts in authored
33
+ * coordinates; every authored number in this file keeps its ORIGINAL value and
34
+ * the renderer subtracts the origin (`ax` / `ay`), so nothing about the drawing
35
+ * moves relative to anything else — only the frame around it tightens.
36
+ *
37
+ * `MAP_SCALE` (see `../presets.ts`) multiplies these, so a new board is born
38
+ * smaller while its drawing keeps exactly the size it had.
39
+ */
40
+ export declare const REF_X = 8;
41
+ export declare const REF_Y = 4.5;
42
+ export declare const REF_W = 659;
43
+ export declare const REF_H = 786.5;
44
+ /**
45
+ * Space left between the ink and the edge of the board, in reference units.
46
+ *
47
+ * Small but not zero: a stroke is centred on its path, a glyph is measured
48
+ * generously, and a board whose ink touched its own border would look clipped.
49
+ * ~6 units is a little under one axis width (8) — enough to read as a margin,
50
+ * little enough that the border is visibly AROUND the drawing.
51
+ */
52
+ export declare const REF_MARGIN = 6;
20
53
  export declare const COLORS: {
21
54
  readonly axis: "#941253";
22
55
  /** Italic e / t axis letters. */
@@ -42,12 +75,38 @@ export declare const T_AXIS: {
42
75
  readonly x2: 616;
43
76
  };
44
77
  export declare const AXIS_WIDTH = 8;
45
- /** Filled arrowhead triangles: [[tipX,tipY],[baseAX,baseAY],[baseBX,baseBY]]. */
46
- export declare const ARROWHEADS: ReadonlyArray<readonly [
47
- readonly [number, number],
48
- readonly [number, number],
49
- readonly [number, number]
50
- ]>;
78
+ /**
79
+ * A filled arrowhead, welded to the END OF ITS AXIS.
80
+ *
81
+ * Declared the way the shared primitive declares one (`drawAxis`,
82
+ * `blocks/surface/src/framework-background/renderer.ts`): the head has ONE
83
+ * proportional anchor — {@link at}, the point where its axis stops, which
84
+ * travels with the stretch — and every other number below is an offset from
85
+ * that anchor in FIXED units, painted at the isotropic `strokeScale`.
86
+ *
87
+ * That split is the whole point. The vertices used to be three absolute
88
+ * authored points, so a stretch moved the tip by `sx` while the renderer
89
+ * rebuilt the base from the tip at the isotropic factor: as soon as
90
+ * `sx ≠ strokeScale` the triangle tore away from the line it belongs to
91
+ * (a 29-unit gap at 1600 × 400, the head drowning in the stroke at 400 × 1600).
92
+ * Anchoring the head to the axis end instead keeps it soldered at every ratio,
93
+ * and reproduces the authored SVG exactly when `sx === sy` — which is what the
94
+ * numbers below were read off.
95
+ */
96
+ export interface EstuarineArrowhead {
97
+ /** Authored end of the axis this head is welded to — the ratio anchor. */
98
+ at: readonly [number, number];
99
+ /** Unit vector pointing OUT of the axis, towards the tip. */
100
+ dir: readonly [number, number];
101
+ /** How far past the axis end the tip sits, in fixed units. */
102
+ tip: number;
103
+ /** How far SHORT of the axis end the base sits: the weld's overlap. */
104
+ overlap: number;
105
+ /** Half the base's width, in fixed units. */
106
+ halfWidth: number;
107
+ }
108
+ /** The three heads: both ends of the e axis, the right end of the t axis. */
109
+ export declare const ARROWHEADS: readonly EstuarineArrowhead[];
51
110
  /** Liminal: green boundary rising gently then dipping at the right end. */
52
111
  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";
53
112
  export declare const LIMINAL_WIDTH = 4.5;
@@ -99,17 +158,33 @@ export declare const LABELS: {
99
158
  readonly color: "#e63322";
100
159
  };
101
160
  };
102
- /** Italic Georgia axis letters (left-anchored, alphabetic baseline). */
161
+ /**
162
+ * Italic Georgia axis letters (left-anchored, alphabetic baseline).
163
+ *
164
+ * Each letter NAMES an axis, so it is declared against that axis the way a
165
+ * declared background declares a word (`backgroundPoint`,
166
+ * `blocks/surface/src/framework-background/def.ts`): a proportional anchor
167
+ * (`at`, the end of the axis it names) plus a FIXED `dx`/`dy` gap, painted at
168
+ * the isotropic `strokeScale` like the glyph itself.
169
+ *
170
+ * They used to be absolute authored positions projected by `sx`/`sy` while the
171
+ * glyph was typed isotropically, so the gap between letter and axis grew or
172
+ * collapsed with the ratio — the `t` climbing onto its own axis at 1600 × 400,
173
+ * falling far below it at 400 × 1600. `at + d·strokeScale` is exactly the
174
+ * authored position when `sx === sy`, and a constant gap everywhere else.
175
+ */
103
176
  export declare const AXIS_LABELS: {
104
177
  readonly e: {
105
178
  readonly text: "e";
106
- readonly x: 14;
107
- readonly y: 138;
179
+ readonly at: readonly [43.5, 97];
180
+ readonly dx: -29.5;
181
+ readonly dy: 41;
108
182
  };
109
183
  readonly t: {
110
184
  readonly text: "t";
111
- readonly x: 580;
112
- readonly y: 685;
185
+ readonly at: readonly [616, 649];
186
+ readonly dx: -36;
187
+ readonly dy: 36;
113
188
  };
114
189
  readonly size: 34;
115
190
  };
@@ -1,12 +1,13 @@
1
1
  /**
2
2
  * Visual constants for the Estuarine framework map, reproduced from the official
3
- * SVG (viewBox 0 0 690 801). All geometry is authored in that fixed reference
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
9
- * (time only flows one way).
3
+ * SVG (viewBox 0 0 690 801). All geometry is authored in that fixed space; the
4
+ * renderer reads every coordinate below as a RATIO of the CROPPED window onto
5
+ * it ({@link REF_X} … {@link REF_H}) and maps it onto the element's real width
6
+ * and height independently, so a stretched map gets a longer time axis and a
7
+ * taller energy axis rather than the same drawing letterboxed (see
8
+ * `EstuarineFit` in `./element-renderer.ts`). The e axis is vertical &
9
+ * double-headed (energy), the t axis horizontal & single-headed (time only
10
+ * flows one way).
10
11
  *
11
12
  * The three curve legends below (`LABELS`) carry a `wording`, resolved by
12
13
  * `element-renderer.ts` through the `CanvasRenderer` it is handed at paint
@@ -14,8 +15,40 @@
14
15
  * italic `e` / `t` axis LETTERS (`AXIS_LABELS`) are notation, not words, and
15
16
  * are deliberately NOT keyed (PO decision).
16
17
  */
17
- export const REF_W = 690;
18
- export const REF_H = 801;
18
+ /**
19
+ * The CROPPED window onto the authored 690 × 801 SVG space — the rectangle the
20
+ * element's bounds are mapped onto.
21
+ *
22
+ * The SVG's own viewBox is loose around the drawing: the t axis stops at
23
+ * `x = 616 / 690` and the e axis at `y = 763 / 801`, so a board born on the raw
24
+ * viewBox carried dead space on every side (measured on the painted pixels at
25
+ * birth size: 16.6 left, 12.6 top, 35.5 right, 20.4 bottom, in model units).
26
+ * The board's own border is therefore nowhere near its drawing, which is both
27
+ * ugly and awkward — a background is grabbed BY that border
28
+ * (`framework-background/hit-test.ts`).
29
+ *
30
+ * So the reference box is the ink's bounding box grown by {@link REF_MARGIN},
31
+ * not the viewBox. `REF_X` / `REF_Y` is where that window starts in authored
32
+ * coordinates; every authored number in this file keeps its ORIGINAL value and
33
+ * the renderer subtracts the origin (`ax` / `ay`), so nothing about the drawing
34
+ * moves relative to anything else — only the frame around it tightens.
35
+ *
36
+ * `MAP_SCALE` (see `../presets.ts`) multiplies these, so a new board is born
37
+ * smaller while its drawing keeps exactly the size it had.
38
+ */
39
+ export const REF_X = 8;
40
+ export const REF_Y = 4.5;
41
+ export const REF_W = 659;
42
+ export const REF_H = 786.5;
43
+ /**
44
+ * Space left between the ink and the edge of the board, in reference units.
45
+ *
46
+ * Small but not zero: a stroke is centred on its path, a glyph is measured
47
+ * generously, and a board whose ink touched its own border would look clipped.
48
+ * ~6 units is a little under one axis width (8) — enough to read as a margin,
49
+ * little enough that the border is visibly AROUND the drawing.
50
+ */
51
+ export const REF_MARGIN = 6;
19
52
  export const COLORS = {
20
53
  axis: '#941253',
21
54
  /** Italic e / t axis letters. */
@@ -33,23 +66,32 @@ export const E_AXIS = { x: 43.5, y1: 97, y2: 763 };
33
66
  /** t axis (horizontal, single-headed → right): y, left x, right x. */
34
67
  export const T_AXIS = { y: 649, x1: 28, x2: 616 };
35
68
  export const AXIS_WIDTH = 8;
36
- /** Filled arrowhead triangles: [[tipX,tipY],[baseAX,baseAY],[baseBX,baseBY]]. */
69
+ /** The three heads: both ends of the e axis, the right end of the t axis. */
37
70
  export const ARROWHEADS = [
38
- [
39
- [43.5, 72],
40
- [30, 100],
41
- [57, 100],
42
- ], // e — top
43
- [
44
- [43.5, 785],
45
- [30, 758],
46
- [57, 758],
47
- ], // e — bottom
48
- [
49
- [643, 649],
50
- [613, 636],
51
- [613, 662],
52
- ], // t — right
71
+ // e — top
72
+ {
73
+ at: [E_AXIS.x, E_AXIS.y1],
74
+ dir: [0, -1],
75
+ tip: 25,
76
+ overlap: 3,
77
+ halfWidth: 13.5,
78
+ },
79
+ // e — bottom
80
+ {
81
+ at: [E_AXIS.x, E_AXIS.y2],
82
+ dir: [0, 1],
83
+ tip: 22,
84
+ overlap: 5,
85
+ halfWidth: 13.5,
86
+ },
87
+ // t — right
88
+ {
89
+ at: [T_AXIS.x2, T_AXIS.y],
90
+ dir: [1, 0],
91
+ tip: 27,
92
+ overlap: 3,
93
+ halfWidth: 13,
94
+ },
53
95
  ];
54
96
  /** Liminal: green boundary rising gently then dipping at the right end. */
55
97
  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';
@@ -118,10 +160,24 @@ export const LABELS = {
118
160
  color: COLORS.volatile,
119
161
  },
120
162
  };
121
- /** Italic Georgia axis letters (left-anchored, alphabetic baseline). */
163
+ /**
164
+ * Italic Georgia axis letters (left-anchored, alphabetic baseline).
165
+ *
166
+ * Each letter NAMES an axis, so it is declared against that axis the way a
167
+ * declared background declares a word (`backgroundPoint`,
168
+ * `blocks/surface/src/framework-background/def.ts`): a proportional anchor
169
+ * (`at`, the end of the axis it names) plus a FIXED `dx`/`dy` gap, painted at
170
+ * the isotropic `strokeScale` like the glyph itself.
171
+ *
172
+ * They used to be absolute authored positions projected by `sx`/`sy` while the
173
+ * glyph was typed isotropically, so the gap between letter and axis grew or
174
+ * collapsed with the ratio — the `t` climbing onto its own axis at 1600 × 400,
175
+ * falling far below it at 400 × 1600. `at + d·strokeScale` is exactly the
176
+ * authored position when `sx === sy`, and a constant gap everywhere else.
177
+ */
122
178
  export const AXIS_LABELS = {
123
- e: { text: 'e', x: 14, y: 138 },
124
- t: { text: 't', x: 580, y: 685 },
179
+ e: { text: 'e', at: [E_AXIS.x, E_AXIS.y1], dx: -29.5, dy: 41 },
180
+ t: { text: 't', at: [T_AXIS.x2, T_AXIS.y], dx: -36, dy: 36 },
125
181
  size: 34,
126
182
  };
127
183
  export const LABEL_LETTER_SPACING = 4;
@@ -109,11 +109,18 @@ export declare function applyEstuarineTransform(ctx: CanvasRenderingContext2D, w
109
109
  * arrowheads and every word. Their POSITION follows the stretch — an axis
110
110
  * ends where the map now ends — while their SHAPE does not, because a
111
111
  * stretched arrowhead or a squashed letter is a defect, never a feature.
112
+ *
113
+ * One rule keeps that split honest, and it is the shared primitive's own
114
+ * (`drawAxis` / `backgroundPoint`): **an anchor is a RATIO of the frame, and
115
+ * every offset from an anchor is in FIXED units.** A shape must never take
116
+ * its anchor from one and its offset from the other — that mix is exactly
117
+ * what tore the arrowheads off their axes and slid the `e` / `t` letters
118
+ * around on a stretched map.
112
119
  * - **Stretched reference space**, entered by {@link applyEstuarineTransform}:
113
120
  * the three curves, which are boundaries across the plane and must cover
114
121
  * whatever plane the user has made.
115
122
  */
116
123
  export declare const estuarine: ElementRenderer<EstuarineElementModel>;
117
- export declare const EstuarineRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
124
+ export declare const EstuarineRendererExtension: import("@formicoidea/labre-core/_pkgs/store").ExtensionType & {
118
125
  identifier: import("@formicoidea/labre-core/_pkgs/global/di").ServiceIdentifier<ElementRenderer<EstuarineElementModel>>;
119
126
  };
@@ -1,7 +1,7 @@
1
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
5
  import { estuarineLegendText } from './labels.js';
6
6
  /**
7
7
  * The three reference curves are drawn as a permanent GHOST (PO arbitration,
@@ -82,6 +82,10 @@ export function estuarineFit(w, h) {
82
82
  export function applyEstuarineTransform(ctx, w, h) {
83
83
  const fit = estuarineFit(w, h);
84
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);
85
89
  return fit;
86
90
  }
87
91
  /**
@@ -97,6 +101,13 @@ export function applyEstuarineTransform(ctx, w, h) {
97
101
  * arrowheads and every word. Their POSITION follows the stretch — an axis
98
102
  * ends where the map now ends — while their SHAPE does not, because a
99
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.
100
111
  * - **Stretched reference space**, entered by {@link applyEstuarineTransform}:
101
112
  * the three curves, which are boundaries across the plane and must cover
102
113
  * whatever plane the user has made.
@@ -111,10 +122,10 @@ export const estuarine = (model, ctx, matrix, renderer) => {
111
122
  .rotateSelf(model.rotate)
112
123
  .translateSelf(-cx, -cy));
113
124
  const fit = estuarineFit(w, h);
114
- /** Authored x → element x. Proportional: `43.5 / 690` of the real width. */
115
- 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;
116
127
  /** Authored y → element y. */
117
- const ay = (y) => y * fit.sy;
128
+ const ay = (y) => (y - REF_Y) * fit.sy;
118
129
  ctx.lineCap = 'round';
119
130
  ctx.lineJoin = 'round';
120
131
  // ── Axes ────────────────────────────────────────────────────────────
@@ -130,17 +141,29 @@ export const estuarine = (model, ctx, matrix, renderer) => {
130
141
  ctx.moveTo(ax(T_AXIS.x1), ay(T_AXIS.y));
131
142
  ctx.lineTo(ax(T_AXIS.x2), ay(T_AXIS.y));
132
143
  ctx.stroke();
133
- // Each head is pinned by its TIP — which travels to the real end of its axis
134
- // — and then built from the authored offsets at the isotropic scale, so the
135
- // triangle keeps its shape at any aspect ratio.
136
- for (const [[tx, ty], [px, py], [qx, qy]] of ARROWHEADS) {
137
- const tipX = ax(tx);
138
- const tipY = ay(ty);
139
- const k = fit.strokeScale;
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;
140
163
  ctx.beginPath();
141
- ctx.moveTo(tipX, tipY);
142
- ctx.lineTo(tipX + (px - tx) * k, tipY + (py - ty) * k);
143
- ctx.lineTo(tipX + (qx - tx) * k, tipY + (qy - ty) * k);
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);
144
167
  ctx.closePath();
145
168
  ctx.fill();
146
169
  }
@@ -180,13 +203,18 @@ export const estuarine = (model, ctx, matrix, renderer) => {
180
203
  legend(curve.key);
181
204
  }
182
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.
183
210
  if (model.showAxisLabels) {
184
211
  ctx.fillStyle = COLORS.axisLabel;
185
- ctx.font = `italic 700 ${AXIS_LABELS.size * fit.strokeScale}px Georgia, serif`;
212
+ ctx.font = `italic 700 ${AXIS_LABELS.size * k}px Georgia, serif`;
186
213
  ctx.textAlign = 'left';
187
214
  ctx.textBaseline = 'alphabetic';
188
- ctx.fillText(AXIS_LABELS.e.text, ax(AXIS_LABELS.e.x), ay(AXIS_LABELS.e.y));
189
- ctx.fillText(AXIS_LABELS.t.text, ax(AXIS_LABELS.t.x), ay(AXIS_LABELS.t.y));
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
+ }
190
218
  }
191
219
  };
192
220
  export const EstuarineRendererExtension = ElementRendererExtension('estuarine', estuarine);
@@ -19,4 +19,4 @@ export declare class EstuarineView extends FrameworkBackgroundView<EstuarineElem
19
19
  * Resize gating: the resize handles are hidden unless `model.resizeEnabled` is
20
20
  * true (toggled from the toolbar). Moving / selecting stays available.
21
21
  */
22
- export declare const EstuarineInteraction: import("@formicoidea/labre-core/store").ExtensionType;
22
+ export declare const EstuarineInteraction: import("@formicoidea/labre-core/_pkgs/store").ExtensionType;
@@ -33,7 +33,8 @@ export declare function estuarineLegendText(model: EstuarineElementModel, key: E
33
33
  * Clickable boxes of the visible legends, in ELEMENT-LOCAL coordinates.
34
34
  *
35
35
  * Anchored through the same fit the renderer anchors them with: proportionally
36
- * in both directions, typed isotropically. A legend whose curve is switched off
37
- * is not painted and is therefore not aimable either.
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.
38
39
  */
39
40
  export declare function estuarineLabelHits(model: EstuarineElementModel, fit: EstuarineLegendFit, translate: EstuarineWordingResolver): BackgroundLabelHit[];
@@ -1,5 +1,5 @@
1
1
  import { backgroundTextHitBox } from '@formicoidea/labre-core/blocks/surface';
2
- import { LABEL_LETTER_SPACING, LABELS } from './consts.js';
2
+ import { LABEL_LETTER_SPACING, LABELS, REF_X, REF_Y } from './consts.js';
3
3
  /**
4
4
  * What a legend SAYS: the user's own word, else the vocabulary.
5
5
  *
@@ -20,8 +20,9 @@ export function estuarineLegendText(model, key, translate) {
20
20
  * Clickable boxes of the visible legends, in ELEMENT-LOCAL coordinates.
21
21
  *
22
22
  * Anchored through the same fit the renderer anchors them with: proportionally
23
- * in both directions, typed isotropically. A legend whose curve is switched off
24
- * is not painted and is therefore not aimable either.
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.
25
26
  */
26
27
  export function estuarineLabelHits(model, fit, translate) {
27
28
  const props = model;
@@ -31,7 +32,7 @@ export function estuarineLabelHits(model, fit, translate) {
31
32
  if (!props[label.visibleProp])
32
33
  continue;
33
34
  const text = estuarineLegendText(model, key, translate);
34
- const box = backgroundTextHitBox(text, label.size * fit.strokeScale, label.x * fit.sx, label.y * fit.sy, 'center');
35
+ const box = backgroundTextHitBox(text, label.size * fit.strokeScale, (label.x - REF_X) * fit.sx, (label.y - REF_Y) * fit.sy, 'center');
35
36
  // A legend is letter-spaced, so it is wider than the shared estimate: half
36
37
  // of the extra width on each side, since the word is centred on its anchor.
37
38
  const spread = (Math.max(text.length - 1, 0) * LABEL_LETTER_SPACING * fit.strokeScale) /
@@ -41,4 +41,4 @@ export declare const estuarineToolbarConfig: {
41
41
  }];
42
42
  readonly when: (ctx: ToolbarContext) => boolean;
43
43
  };
44
- export declare const estuarineToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
44
+ export declare const estuarineToolbarExtension: import("@formicoidea/labre-core/_pkgs/store").ExtensionType;
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
- /** Estuarine map default scale (REF aspect, scaled up so it reads on canvas). */
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
- /** The box an estuarine map is born in — the size the toolbox draws. */
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
- /** Estuarine map default scale (REF aspect, scaled up so it reads on canvas). */
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
- /** The box an estuarine map is born in — the size the toolbox draws. */
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. */
@@ -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 (690×801) where the button
16
- * draws it at 1.2 (828×961). Derived, neither can happen again — and
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 reference
51
- * space (690×801) — the frame the drawing was laid out in — carried onto the
52
- * box the map is actually born at.
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 * MAP_SCALE, cy * MAP_SCALE),
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 * MAP_SCALE - CAPTION_W / 2;
73
- const y = cy * MAP_SCALE + HEX_SIZE / 2 + CAPTION_GAP;
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),
@@ -1,2 +1,2 @@
1
1
  /** A single senior tool hosting both the Cynefin and Estuarine frameworks. */
2
- export declare const cynefinEstuarineSeniorTool: import("@formicoidea/labre-core/store").ExtensionType;
2
+ export declare const cynefinEstuarineSeniorTool: import("@formicoidea/labre-core/_pkgs/store").ExtensionType;
package/dist/utils.d.ts CHANGED
@@ -1,11 +1,9 @@
1
1
  /**
2
- * Uniform fit of a fixed reference design (`refW × refH`) into an element of
3
- * size `w × h`: the scale factor plus the centering offsets (letterboxed).
4
- * Keeps the artwork undistorted at any element size.
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 declare function refScale(w: number, h: number, refW: number, refH: number): {
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 (`refW × refH`) into an element of
3
- * size `w × h`: the scale factor plus the centering offsets (letterboxed).
4
- * Keeps the artwork undistorted at any element size.
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 function refScale(w, h, refW, refH) {
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';
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.42.0",
4
+ "version": "0.45.0",
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.42.0",
34
+ "@formicoidea/labre-core": "0.45.0",
35
35
  "@preact/signals-core": "^1.8.0",
36
36
  "lit": "^3.2.0"
37
37
  }