@formicoidea/labre-framework-edgy 0.59.0 → 0.61.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/actions.js CHANGED
@@ -4,25 +4,24 @@ import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
4
4
  import { createTemplateJob } from '@formicoidea/labre-core/gfx/template';
5
5
  import { ConnectorMode, PointStyle, StrokeStyle } from '@formicoidea/labre-core/model';
6
6
  import { translateKey } from '@formicoidea/labre-core/shared/services';
7
- import { Bound } from '@formicoidea/labre-core/global/gfx';
8
7
  import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
9
- import { CROP_LABELED } from './consts.js';
10
8
  import { edgyElementLabel, edgyElementLabelKey } from './metamodel.js';
11
- import { LABEL_GAP, NODE_LABEL, nodeLabelKey, NODE_SIZE, NODE_STROKE, } from './node/consts.js';
12
- import { EDGY_LABEL_W, edgyLabelProps, edgyNodeProps, edgyNodeTextProps, FACETS_SCALE, } from './presets.js';
9
+ import { NODE_LABEL, nodeLabelKey, NODE_SIZE, NODE_STROKE, } from './node/consts.js';
10
+ import { EDGY_BOARD_SIZE, RELATION_STROKE_WIDTH, } from './presets.js';
13
11
  import { EDGY_ROLE } from './roles.js';
14
12
  import { edgyDynamicTemplate } from './templates/dynamic.js';
15
- /** Stroke width the 24 template relations are drawn with. */
16
- const RELATION_STROKE_WIDTH = 2;
13
+ import { EDGY_FACETS_SIZE, EDGY_PEOPLE_SIZE, edgyBoardElement, edgyBoxElement, edgyFacetsElement, edgyPeopleElements, } from './toolbox.js';
17
14
  /**
18
15
  * Standalone creation actions for the EDGY toolbox — the behaviour layer,
19
16
  * lifted out of `toolbar/edgy-menu.ts` by PF3 so the menu can become a pure
20
17
  * renderer over the command registry. They emit no telemetry: the single
21
18
  * emission point is `runCommand` (`docs/adr/0008`).
19
+ *
20
+ * What they place comes from the ONE assembly a host with no editor also
21
+ * calls (`toolbox.ts`, `docs/adr/0045`); what stays here is what only an
22
+ * editor has: the viewport centre, the seeds resolved in the host's language
23
+ * (`docs/adr/0023`), the group command and the selection.
22
24
  */
23
- /** Default blank-board size. */
24
- const BOARD_W = 1600;
25
- const BOARD_H = 1000;
26
25
  const gfxOf = (std) => std.get(GfxControllerIdentifier);
27
26
  function finish(gfx, id) {
28
27
  gfx.doc.captureSync();
@@ -38,27 +37,22 @@ export function createEdgyFacets(std) {
38
37
  const gfx = gfxOf(std);
39
38
  if (!gfx.surface)
40
39
  return;
41
- const width = CROP_LABELED.w * FACETS_SCALE;
42
- const height = CROP_LABELED.h * FACETS_SCALE;
40
+ const { w, h } = EDGY_FACETS_SIZE;
43
41
  const { centerX, centerY } = gfx.viewport;
44
- const id = gfx.surface.addElement({
45
- type: 'edgy',
46
- cropToCircles: true,
47
- // The frame: what makes this an EDGY board rather than three circles, and
48
- // what a finding is attributed to.
49
- role: EDGY_ROLE.facets,
50
- // The three facet names, written HERE rather than left to the model's
51
- // defaults (`EdgyFacetsElementModel.identityLabel` etc — a red-zone
52
- // field: a document created before this key existed keeps its literal
53
- // 'Identity' / 'Architecture' / 'Experience' verbatim). Shares its key
54
- // with the very same words wherever the metamodel names a zone
55
- // (`edgyElementLabelKey`, `templates/dynamic.ts`, `templates/index.ts`).
56
- identityLabel: translateKey(std, edgyElementLabelKey('identity'), edgyElementLabel('identity')),
57
- architectureLabel: translateKey(std, edgyElementLabelKey('architecture'), edgyElementLabel('architecture')),
58
- experienceLabel: translateKey(std, edgyElementLabelKey('experience'), edgyElementLabel('experience')),
59
- xywh: new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize(),
42
+ const zone = (name) => translateKey(std, edgyElementLabelKey(name), edgyElementLabel(name));
43
+ const { id: _local, ...props } = edgyFacetsElement({ x: centerX - w / 2, y: centerY - h / 2 },
44
+ // The three facet names, written HERE rather than left to the model's
45
+ // defaults (`EdgyFacetsElementModel.identityLabel` etc — a red-zone
46
+ // field: a document created before this key existed keeps its literal
47
+ // 'Identity' / 'Architecture' / 'Experience' verbatim). Shares its key
48
+ // with the very same words wherever the metamodel names a zone
49
+ // (`edgyElementLabelKey`, `templates/dynamic.ts`, `templates/index.ts`).
50
+ {
51
+ identity: zone('identity'),
52
+ architecture: zone('architecture'),
53
+ experience: zone('experience'),
60
54
  });
61
- finish(gfx, id);
55
+ finish(gfx, gfx.surface.addElement(props));
62
56
  }
63
57
  /**
64
58
  * Insert the "EDGY dynamic" template (facets background + the 12 elements
@@ -90,15 +84,13 @@ export function createEdgyBoard(std) {
90
84
  const gfx = gfxOf(std);
91
85
  if (!gfx.surface)
92
86
  return;
87
+ const { w, h } = EDGY_BOARD_SIZE;
93
88
  const { centerX, centerY } = gfx.viewport;
94
- const id = gfx.surface.addElement({
95
- type: 'edgyBoard',
96
- // The blank board is a frame too: the same rules apply to what is drawn on
97
- // it, and the same map-wide arbitration has to have somewhere to live.
98
- role: EDGY_ROLE.board,
99
- xywh: new Bound(centerX - BOARD_W / 2, centerY - BOARD_H / 2, BOARD_W, BOARD_H).serialize(),
89
+ const { id: _local, ...props } = edgyBoardElement({
90
+ x: centerX - w / 2,
91
+ y: centerY - h / 2,
100
92
  });
101
- finish(gfx, id);
93
+ finish(gfx, gfx.surface.addElement(props));
102
94
  }
103
95
  /** Create a box base element (outcome / object / activity) with inner text. */
104
96
  export function createEdgyBox(std, kind) {
@@ -108,15 +100,11 @@ export function createEdgyBox(std, kind) {
108
100
  return;
109
101
  const { w, h } = NODE_SIZE[kind];
110
102
  const { centerX: cx, centerY: cy } = gfx.viewport;
111
- const id = surface.addElement({
112
- ...edgyNodeProps(kind, {
113
- xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
114
- }),
115
- // Translated HERE and once: what a gesture writes onto the canvas is
116
- // document content the moment it lands (`nodeLabelKey`).
117
- ...edgyNodeTextProps(translateKey(std, nodeLabelKey(kind), NODE_LABEL[kind])),
118
- });
119
- finish(gfx, id);
103
+ const { id: _local, ...props } = edgyBoxElement(kind, { x: cx - w / 2, y: cy - h / 2 },
104
+ // Translated HERE and once: what a gesture writes onto the canvas is
105
+ // document content the moment it lands (`nodeLabelKey`).
106
+ translateKey(std, nodeLabelKey(kind), NODE_LABEL[kind]));
107
+ finish(gfx, surface.addElement(props));
120
108
  }
121
109
  /**
122
110
  * Create the People base element: an (invisible) ellipse decorated with the
@@ -127,14 +115,27 @@ export function createEdgyPeople(std) {
127
115
  const surface = gfx.surface;
128
116
  if (!surface)
129
117
  return;
130
- const { w, h } = NODE_SIZE.people;
131
118
  const { centerX: cx, centerY: cy } = gfx.viewport;
132
- const nodeId = surface.addElement(edgyNodeProps('people', {
133
- xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
134
- }));
135
- const labelId = surface.addElement(edgyLabelProps(translateKey(std, nodeLabelKey('people'), NODE_LABEL.people), cx - EDGY_LABEL_W / 2, cy + h / 2 + LABEL_GAP));
119
+ // The GLYPH, not the whole item, sits on the viewport centre, as it always
120
+ // has: the item's box is as wide as the name and starts at the glyph's top.
121
+ const elements = edgyPeopleElements({
122
+ x: cx - EDGY_PEOPLE_SIZE.w / 2,
123
+ y: cy - NODE_SIZE.people.h / 2,
124
+ }, translateKey(std, nodeLabelKey('people'), NODE_LABEL.people));
125
+ const members = [];
126
+ for (const { id: _local, ...props } of elements) {
127
+ // The builder's group is for a host with no editor. Here the editor's own
128
+ // group command makes it, because it writes what only an editor has: the
129
+ // numbered "Group N" title from the host's catalogue and the user layer.
130
+ if (props.type === 'group')
131
+ continue;
132
+ members.push(surface.addElement(props));
133
+ }
134
+ const nodeId = members[0];
135
+ if (!nodeId)
136
+ return;
136
137
  const [, result] = std.command.exec(createGroupCommand, {
137
- elements: [nodeId, labelId],
138
+ elements: members,
138
139
  });
139
140
  finish(gfx, result.groupId || nodeId);
140
141
  }
package/dist/consts.d.ts CHANGED
@@ -6,18 +6,7 @@
6
6
  * uniformly to the element bounds — so the three circles always stay circular
7
7
  * and the pictos/labels keep their relative proportions at any size.
8
8
  */
9
- /** Reference design size (the validated mockup canvas). */
10
- export declare const REF_W = 680;
11
- export declare const REF_H = 400;
12
- /** Circle geometry in reference coords. */
13
- export declare const VENN: {
14
- readonly cx: number;
15
- readonly cy: 176;
16
- /** Circle radius. */
17
- readonly R: 95;
18
- /** Distance of each circle centre from the diagram centre. */
19
- readonly r0: 56;
20
- };
9
+ export { CROP, CROP_LABELED, cropLabeledScale, cropScale, REF_H, REF_W, VENN, } from './facets-crop.js';
21
10
  /** Saturated facet + intersection colours (faithful to the official icons). */
22
11
  export declare const COLORS: {
23
12
  readonly identity: "#00ea4e";
@@ -43,56 +32,3 @@ export declare const FONT_FAMILY = "Inter, sans-serif";
43
32
  * reference size is this framework's business.
44
33
  */
45
34
  export declare function refScale(w: number, h: number): import("@formicoidea/labre-core/_pkgs/affine-block-surface").RefFit;
46
- /**
47
- * Bounding box of the three circles in reference coords, with just enough
48
- * padding to see their outline. When a diagram hides its facet names
49
- * (`cropToCircles` without `showLabels`) the renderer fits THIS box into the
50
- * element bounds, so the background hugs the Venn.
51
- *
52
- * ## Why the padding is 2.5 and not the 8 it was
53
- *
54
- * The padding is the ONLY empty band such a board has, and it is spent at the
55
- * element's scale, not at the reference one. The EDGY dynamic template draws
56
- * its board at `DYN_SCALE = 4.8`, so eight reference units of padding were
57
- * **32.4 model units** of nothing on each of the four sides — the board a user
58
- * has to catch by its border (R37) sat a good inch away from the circles, which
59
- * is what the recette of 23/09/2026 reported. 2.5 leaves 1.25 past the circles'
60
- * own 2.5-wide outline, i.e. 6 model units at the dynamic board's scale and
61
- * under 2 at a facets board's.
62
- *
63
- * `templates/dynamic.ts` places every element of the dynamic board in
64
- * coordinates relative to this box (`dynToModel`) and sizes the board from it,
65
- * so the template follows in the same breath: both are derived here, and a
66
- * freshly inserted template is aligned by construction. A board built from the
67
- * OLD template keeps its `xywh` and sees the Venn grow 3.8 % inside it — see
68
- * the changeset.
69
- */
70
- export declare const CROP: {
71
- x: number;
72
- y: number;
73
- w: number;
74
- h: number;
75
- };
76
- /**
77
- * Crop box KEEPING room for the three facet name labels (drawn outside the
78
- * circles): the circles plus {@link LABEL_ALLOWANCE}, which is asymmetric
79
- * because the words are. Used when a cropped diagram still shows its labels.
80
- */
81
- export declare const CROP_LABELED: {
82
- x: number;
83
- y: number;
84
- w: number;
85
- h: number;
86
- };
87
- /** `refScale` variant fitting the cropped circles box (see {@link CROP}). */
88
- export declare const cropScale: (w: number, h: number) => {
89
- s: number;
90
- ox: number;
91
- oy: number;
92
- };
93
- /** `refScale` variant fitting the circles + facet labels box. */
94
- export declare const cropLabeledScale: (w: number, h: number) => {
95
- s: number;
96
- ox: number;
97
- oy: number;
98
- };
package/dist/consts.js CHANGED
@@ -7,18 +7,11 @@
7
7
  * and the pictos/labels keep their relative proportions at any size.
8
8
  */
9
9
  import { refScale as fitReference } from '@formicoidea/labre-core/blocks/surface';
10
- /** Reference design size (the validated mockup canvas). */
11
- export const REF_W = 680;
12
- export const REF_H = 400;
13
- /** Circle geometry in reference coords. */
14
- export const VENN = {
15
- cx: REF_W / 2,
16
- cy: 176,
17
- /** Circle radius. */
18
- R: 95,
19
- /** Distance of each circle centre from the diagram centre. */
20
- r0: 56,
21
- };
10
+ import { REF_H, REF_W } from './facets-crop.js';
11
+ // The reference geometry lives in an editor-free file (`facets-crop.ts`) so the
12
+ // toolbox can size a facets background without the surface index; every
13
+ // binding is re-exported here unchanged.
14
+ export { CROP, CROP_LABELED, cropLabeledScale, cropScale, REF_H, REF_W, VENN, } from './facets-crop.js';
22
15
  /** Saturated facet + intersection colours (faithful to the official icons). */
23
16
  export const COLORS = {
24
17
  identity: '#00ea4e',
@@ -46,120 +39,3 @@ export const FONT_FAMILY = 'Inter, sans-serif';
46
39
  export function refScale(w, h) {
47
40
  return fitReference(w, h, REF_W, REF_H);
48
41
  }
49
- /**
50
- * The three circles, and nothing else: their exact bounding box in reference
51
- * coords. Both crop boxes below are this box plus what each of them has to make
52
- * room for, so the two can never disagree about where the Venn is.
53
- */
54
- const CIRCLES = (() => {
55
- const ax = VENN.cx - 0.866 * VENN.r0; // Identity / Architecture centres
56
- const abY = VENN.cy - 0.5 * VENN.r0;
57
- const cY = VENN.cy + VENN.r0; // Experience centre
58
- return {
59
- minX: ax - VENN.R,
60
- maxX: VENN.cx + 0.866 * VENN.r0 + VENN.R,
61
- minY: abY - VENN.R,
62
- maxY: cY + VENN.R,
63
- };
64
- })();
65
- /**
66
- * Bounding box of the three circles in reference coords, with just enough
67
- * padding to see their outline. When a diagram hides its facet names
68
- * (`cropToCircles` without `showLabels`) the renderer fits THIS box into the
69
- * element bounds, so the background hugs the Venn.
70
- *
71
- * ## Why the padding is 2.5 and not the 8 it was
72
- *
73
- * The padding is the ONLY empty band such a board has, and it is spent at the
74
- * element's scale, not at the reference one. The EDGY dynamic template draws
75
- * its board at `DYN_SCALE = 4.8`, so eight reference units of padding were
76
- * **32.4 model units** of nothing on each of the four sides — the board a user
77
- * has to catch by its border (R37) sat a good inch away from the circles, which
78
- * is what the recette of 23/09/2026 reported. 2.5 leaves 1.25 past the circles'
79
- * own 2.5-wide outline, i.e. 6 model units at the dynamic board's scale and
80
- * under 2 at a facets board's.
81
- *
82
- * `templates/dynamic.ts` places every element of the dynamic board in
83
- * coordinates relative to this box (`dynToModel`) and sizes the board from it,
84
- * so the template follows in the same breath: both are derived here, and a
85
- * freshly inserted template is aligned by construction. A board built from the
86
- * OLD template keeps its `xywh` and sees the Venn grow 3.8 % inside it — see
87
- * the changeset.
88
- */
89
- export const CROP = (() => {
90
- const pad = 2.5;
91
- return {
92
- x: CIRCLES.minX - pad,
93
- y: CIRCLES.minY - pad,
94
- w: CIRCLES.maxX - CIRCLES.minX + 2 * pad,
95
- h: CIRCLES.maxY - CIRCLES.minY + 2 * pad,
96
- };
97
- })();
98
- /**
99
- * How far past the circles each side of the labelled box has to reach, in REF
100
- * units. MEASURED on the canvas, in the shipped wording, at
101
- * {@link LABEL_FONT_SIZE}; each number is that reach plus 4 or 5 units of
102
- * gutter.
103
- *
104
- * It used to be one symmetric number — `sideAllowance = 140` either side of the
105
- * padded box, `bottomAllowance = 35` under it — and the three words are not
106
- * symmetric at all. "Identity" is written right-aligned 10 left of the left
107
- * circle and reaches 63.6 past the circles; "Architecture" is written
108
- * left-aligned 10 right of the right circle and reaches 98.1; "Experience"
109
- * hangs 29.2 below the bottom one (a `middle` baseline plus the descender of
110
- * its "p"); and above them there is no word at all, only the 1.25 of circle
111
- * stroke. So a facets diagram was born inside 126.6 model units of nothing on
112
- * its left and 74.8 on its right — and a framework background is caught by its
113
- * border and by nothing else
114
- * (`model/src/elements/framework-background/hit-test.ts`), which put the
115
- * catchable edge a seventh of the board away from anything drawn.
116
- *
117
- * ## Why constants rather than a measurement at paint time
118
- *
119
- * Because the three names are EDITABLE props of the element (`identityLabel` &
120
- * co) and `cropLabeledScale` is not only what the renderer draws through —
121
- * `element-view.ts` maps every click back through it too. Deriving the box from
122
- * the current text would rescale and re-centre the whole Venn on each
123
- * keystroke, and move the click mapping under the user's finger while they
124
- * type; two boards of the same size would also draw their circles at two
125
- * different sizes. The allowance is furniture: it is measured once, here.
126
- *
127
- * A name rewritten to something much longer than the vocabulary therefore
128
- * reaches past the element's edge. It paints (the surface clips nothing); only
129
- * the SVG export, which crops to the `xywh`, would cut it.
130
- */
131
- const LABEL_ALLOWANCE = {
132
- /** No word up there: the circle's own stroke, and room to see it. */
133
- top: 5,
134
- /** "Identity", right-aligned, reaching left of the circles. */
135
- left: 68,
136
- /** "Architecture", left-aligned, reaching right of them. */
137
- right: 103,
138
- /** "Experience", under the bottom circle, descender included. */
139
- bottom: 32,
140
- };
141
- /**
142
- * Crop box KEEPING room for the three facet name labels (drawn outside the
143
- * circles): the circles plus {@link LABEL_ALLOWANCE}, which is asymmetric
144
- * because the words are. Used when a cropped diagram still shows its labels.
145
- */
146
- export const CROP_LABELED = {
147
- x: CIRCLES.minX - LABEL_ALLOWANCE.left,
148
- y: CIRCLES.minY - LABEL_ALLOWANCE.top,
149
- w: CIRCLES.maxX - CIRCLES.minX + LABEL_ALLOWANCE.left + LABEL_ALLOWANCE.right,
150
- h: CIRCLES.maxY - CIRCLES.minY + LABEL_ALLOWANCE.top + LABEL_ALLOWANCE.bottom,
151
- };
152
- function fitScale(box) {
153
- return (w, h) => {
154
- const s = Math.min(w / box.w, h / box.h);
155
- return {
156
- s,
157
- ox: (w - box.w * s) / 2 - box.x * s,
158
- oy: (h - box.h * s) / 2 - box.y * s,
159
- };
160
- };
161
- }
162
- /** `refScale` variant fitting the cropped circles box (see {@link CROP}). */
163
- export const cropScale = fitScale(CROP);
164
- /** `refScale` variant fitting the circles + facet labels box. */
165
- export const cropLabeledScale = fitScale(CROP_LABELED);
@@ -0,0 +1,75 @@
1
+ /**
2
+ * The facets diagram's reference geometry: the fixed design space, the Venn
3
+ * and the two crop boxes, as plain numbers.
4
+ *
5
+ * Split out of `consts.ts`, which re-exports every binding unchanged, because
6
+ * that file also reads the library's fit from the surface package index: the
7
+ * toolbox (`toolbox.ts`, `docs/adr/0045`) sizes a facets background from
8
+ * {@link CROP_LABELED} on the editor-free `./interchange` entry, which must not
9
+ * load the editor graph that index brings.
10
+ */
11
+ /** Reference design size (the validated mockup canvas). */
12
+ export declare const REF_W = 680;
13
+ export declare const REF_H = 400;
14
+ /** Circle geometry in reference coords. */
15
+ export declare const VENN: {
16
+ readonly cx: number;
17
+ readonly cy: 176;
18
+ /** Circle radius. */
19
+ readonly R: 95;
20
+ /** Distance of each circle centre from the diagram centre. */
21
+ readonly r0: 56;
22
+ };
23
+ /**
24
+ * Bounding box of the three circles in reference coords, with just enough
25
+ * padding to see their outline. When a diagram hides its facet names
26
+ * (`cropToCircles` without `showLabels`) the renderer fits THIS box into the
27
+ * element bounds, so the background hugs the Venn.
28
+ *
29
+ * ## Why the padding is 2.5 and not the 8 it was
30
+ *
31
+ * The padding is the ONLY empty band such a board has, and it is spent at the
32
+ * element's scale, not at the reference one. The EDGY dynamic template draws
33
+ * its board at `DYN_SCALE = 4.8`, so eight reference units of padding were
34
+ * **32.4 model units** of nothing on each of the four sides — the board a user
35
+ * has to catch by its border (R37) sat a good inch away from the circles, which
36
+ * is what the recette of 23/09/2026 reported. 2.5 leaves 1.25 past the circles'
37
+ * own 2.5-wide outline, i.e. 6 model units at the dynamic board's scale and
38
+ * under 2 at a facets board's.
39
+ *
40
+ * `templates/dynamic.ts` places every element of the dynamic board in
41
+ * coordinates relative to this box (`dynToModel`) and sizes the board from it,
42
+ * so the template follows in the same breath: both are derived here, and a
43
+ * freshly inserted template is aligned by construction. A board built from the
44
+ * OLD template keeps its `xywh` and sees the Venn grow 3.8 % inside it — see
45
+ * the changeset.
46
+ */
47
+ export declare const CROP: {
48
+ x: number;
49
+ y: number;
50
+ w: number;
51
+ h: number;
52
+ };
53
+ /**
54
+ * Crop box KEEPING room for the three facet name labels (drawn outside the
55
+ * circles): the circles plus {@link LABEL_ALLOWANCE}, which is asymmetric
56
+ * because the words are. Used when a cropped diagram still shows its labels.
57
+ */
58
+ export declare const CROP_LABELED: {
59
+ x: number;
60
+ y: number;
61
+ w: number;
62
+ h: number;
63
+ };
64
+ /** `refScale` variant fitting the cropped circles box (see {@link CROP}). */
65
+ export declare const cropScale: (w: number, h: number) => {
66
+ s: number;
67
+ ox: number;
68
+ oy: number;
69
+ };
70
+ /** `refScale` variant fitting the circles + facet labels box. */
71
+ export declare const cropLabeledScale: (w: number, h: number) => {
72
+ s: number;
73
+ ox: number;
74
+ oy: number;
75
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * The facets diagram's reference geometry: the fixed design space, the Venn
3
+ * and the two crop boxes, as plain numbers.
4
+ *
5
+ * Split out of `consts.ts`, which re-exports every binding unchanged, because
6
+ * that file also reads the library's fit from the surface package index: the
7
+ * toolbox (`toolbox.ts`, `docs/adr/0045`) sizes a facets background from
8
+ * {@link CROP_LABELED} on the editor-free `./interchange` entry, which must not
9
+ * load the editor graph that index brings.
10
+ */
11
+ /** Reference design size (the validated mockup canvas). */
12
+ export const REF_W = 680;
13
+ export const REF_H = 400;
14
+ /** Circle geometry in reference coords. */
15
+ export const VENN = {
16
+ cx: REF_W / 2,
17
+ cy: 176,
18
+ /** Circle radius. */
19
+ R: 95,
20
+ /** Distance of each circle centre from the diagram centre. */
21
+ r0: 56,
22
+ };
23
+ /**
24
+ * The three circles, and nothing else: their exact bounding box in reference
25
+ * coords. Both crop boxes below are this box plus what each of them has to make
26
+ * room for, so the two can never disagree about where the Venn is.
27
+ */
28
+ const CIRCLES = (() => {
29
+ const ax = VENN.cx - 0.866 * VENN.r0; // Identity / Architecture centres
30
+ const abY = VENN.cy - 0.5 * VENN.r0;
31
+ const cY = VENN.cy + VENN.r0; // Experience centre
32
+ return {
33
+ minX: ax - VENN.R,
34
+ maxX: VENN.cx + 0.866 * VENN.r0 + VENN.R,
35
+ minY: abY - VENN.R,
36
+ maxY: cY + VENN.R,
37
+ };
38
+ })();
39
+ /**
40
+ * Bounding box of the three circles in reference coords, with just enough
41
+ * padding to see their outline. When a diagram hides its facet names
42
+ * (`cropToCircles` without `showLabels`) the renderer fits THIS box into the
43
+ * element bounds, so the background hugs the Venn.
44
+ *
45
+ * ## Why the padding is 2.5 and not the 8 it was
46
+ *
47
+ * The padding is the ONLY empty band such a board has, and it is spent at the
48
+ * element's scale, not at the reference one. The EDGY dynamic template draws
49
+ * its board at `DYN_SCALE = 4.8`, so eight reference units of padding were
50
+ * **32.4 model units** of nothing on each of the four sides — the board a user
51
+ * has to catch by its border (R37) sat a good inch away from the circles, which
52
+ * is what the recette of 23/09/2026 reported. 2.5 leaves 1.25 past the circles'
53
+ * own 2.5-wide outline, i.e. 6 model units at the dynamic board's scale and
54
+ * under 2 at a facets board's.
55
+ *
56
+ * `templates/dynamic.ts` places every element of the dynamic board in
57
+ * coordinates relative to this box (`dynToModel`) and sizes the board from it,
58
+ * so the template follows in the same breath: both are derived here, and a
59
+ * freshly inserted template is aligned by construction. A board built from the
60
+ * OLD template keeps its `xywh` and sees the Venn grow 3.8 % inside it — see
61
+ * the changeset.
62
+ */
63
+ export const CROP = (() => {
64
+ const pad = 2.5;
65
+ return {
66
+ x: CIRCLES.minX - pad,
67
+ y: CIRCLES.minY - pad,
68
+ w: CIRCLES.maxX - CIRCLES.minX + 2 * pad,
69
+ h: CIRCLES.maxY - CIRCLES.minY + 2 * pad,
70
+ };
71
+ })();
72
+ /**
73
+ * How far past the circles each side of the labelled box has to reach, in REF
74
+ * units. MEASURED on the canvas, in the shipped wording, at
75
+ * `LABEL_FONT_SIZE` (`consts.ts`); each number is that reach plus 4 or 5 units of
76
+ * gutter.
77
+ *
78
+ * It used to be one symmetric number — `sideAllowance = 140` either side of the
79
+ * padded box, `bottomAllowance = 35` under it — and the three words are not
80
+ * symmetric at all. "Identity" is written right-aligned 10 left of the left
81
+ * circle and reaches 63.6 past the circles; "Architecture" is written
82
+ * left-aligned 10 right of the right circle and reaches 98.1; "Experience"
83
+ * hangs 29.2 below the bottom one (a `middle` baseline plus the descender of
84
+ * its "p"); and above them there is no word at all, only the 1.25 of circle
85
+ * stroke. So a facets diagram was born inside 126.6 model units of nothing on
86
+ * its left and 74.8 on its right — and a framework background is caught by its
87
+ * border and by nothing else
88
+ * (`model/src/elements/framework-background/hit-test.ts`), which put the
89
+ * catchable edge a seventh of the board away from anything drawn.
90
+ *
91
+ * ## Why constants rather than a measurement at paint time
92
+ *
93
+ * Because the three names are EDITABLE props of the element (`identityLabel` &
94
+ * co) and `cropLabeledScale` is not only what the renderer draws through —
95
+ * `element-view.ts` maps every click back through it too. Deriving the box from
96
+ * the current text would rescale and re-centre the whole Venn on each
97
+ * keystroke, and move the click mapping under the user's finger while they
98
+ * type; two boards of the same size would also draw their circles at two
99
+ * different sizes. The allowance is furniture: it is measured once, here.
100
+ *
101
+ * A name rewritten to something much longer than the vocabulary therefore
102
+ * reaches past the element's edge. It paints (the surface clips nothing); only
103
+ * the SVG export, which crops to the `xywh`, would cut it.
104
+ */
105
+ const LABEL_ALLOWANCE = {
106
+ /** No word up there: the circle's own stroke, and room to see it. */
107
+ top: 5,
108
+ /** "Identity", right-aligned, reaching left of the circles. */
109
+ left: 68,
110
+ /** "Architecture", left-aligned, reaching right of them. */
111
+ right: 103,
112
+ /** "Experience", under the bottom circle, descender included. */
113
+ bottom: 32,
114
+ };
115
+ /**
116
+ * Crop box KEEPING room for the three facet name labels (drawn outside the
117
+ * circles): the circles plus {@link LABEL_ALLOWANCE}, which is asymmetric
118
+ * because the words are. Used when a cropped diagram still shows its labels.
119
+ */
120
+ export const CROP_LABELED = {
121
+ x: CIRCLES.minX - LABEL_ALLOWANCE.left,
122
+ y: CIRCLES.minY - LABEL_ALLOWANCE.top,
123
+ w: CIRCLES.maxX - CIRCLES.minX + LABEL_ALLOWANCE.left + LABEL_ALLOWANCE.right,
124
+ h: CIRCLES.maxY - CIRCLES.minY + LABEL_ALLOWANCE.top + LABEL_ALLOWANCE.bottom,
125
+ };
126
+ function fitScale(box) {
127
+ return (w, h) => {
128
+ const s = Math.min(w / box.w, h / box.h);
129
+ return {
130
+ s,
131
+ ox: (w - box.w * s) / 2 - box.x * s,
132
+ oy: (h - box.h * s) / 2 - box.y * s,
133
+ };
134
+ };
135
+ }
136
+ /** `refScale` variant fitting the cropped circles box (see {@link CROP}). */
137
+ export const cropScale = fitScale(CROP);
138
+ /** `refScale` variant fitting the circles + facet labels box. */
139
+ export const cropLabeledScale = fitScale(CROP_LABELED);
package/dist/index.d.ts CHANGED
@@ -3,3 +3,4 @@ export { edgyTranslationEntries } from './translations.js';
3
3
  export { EDGY_ROLE, EDGY_ROLES, type EdgyRoleId } from './roles.js';
4
4
  export { EDGY_READING } from './reading.js';
5
5
  export { EDGY_RULES } from './rules.js';
6
+ export { EDGY_FACETS_SIZE, EDGY_PEOPLE_SIZE, EDGY_TOOLBOX, edgyBoardElement, edgyBoxElement, edgyFacetsElement, type EdgyFacetsTexts, edgyPeopleElements, edgyRelationElement, } from './toolbox.js';
package/dist/index.js CHANGED
@@ -5,3 +5,5 @@ export { edgyTranslationEntries } from './translations.js';
5
5
  export { EDGY_ROLE, EDGY_ROLES } from './roles.js';
6
6
  export { EDGY_READING } from './reading.js';
7
7
  export { EDGY_RULES } from './rules.js';
8
+ // The toolbar as pure builders (`docs/adr/0045`), also on `./interchange`.
9
+ export { EDGY_FACETS_SIZE, EDGY_PEOPLE_SIZE, EDGY_TOOLBOX, edgyBoardElement, edgyBoxElement, edgyFacetsElement, edgyPeopleElements, edgyRelationElement, } from './toolbox.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@labre/affine-gfx-edgy/interchange` — EDGY with no editor behind it.
3
+ *
4
+ * EDGY declares no file format. What a host without an editor uses is its
5
+ * toolbar as pure builders (`docs/adr/0045`): the elements a user places from
6
+ * the toolbar, for a server or an MCP tool to place the same ones.
7
+ *
8
+ * Held by `interchange-headless.unit.spec.ts` in `packages/affine/all`.
9
+ */
10
+ export { EDGY_FACETS_SIZE, EDGY_PEOPLE_SIZE, EDGY_TOOLBOX, edgyBoardElement, edgyBoxElement, edgyFacetsElement, type EdgyFacetsTexts, edgyPeopleElements, edgyRelationElement, } from './toolbox.js';
@@ -0,0 +1,10 @@
1
+ /**
2
+ * `@labre/affine-gfx-edgy/interchange` — EDGY with no editor behind it.
3
+ *
4
+ * EDGY declares no file format. What a host without an editor uses is its
5
+ * toolbar as pure builders (`docs/adr/0045`): the elements a user places from
6
+ * the toolbar, for a server or an MCP tool to place the same ones.
7
+ *
8
+ * Held by `interchange-headless.unit.spec.ts` in `packages/affine/all`.
9
+ */
10
+ export { EDGY_FACETS_SIZE, EDGY_PEOPLE_SIZE, EDGY_TOOLBOX, edgyBoardElement, edgyBoxElement, edgyFacetsElement, edgyPeopleElements, edgyRelationElement, } from './toolbox.js';
@@ -1,4 +1,4 @@
1
- import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
1
+ import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/headless';
2
2
  /**
3
3
  * Visual constants for the four EDGY base-element nodes. Each node is a NATIVE
4
4
  * shape (ShapeElementModel-derived) so its stroke / fill / inner text are
package/dist/presets.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type EdgyNodeKind, FontFamily, TextAlign } from '@formicoidea/labre-core/model';
1
+ import type { EdgyNodeKind } from '@formicoidea/labre-core/model';
2
+ import { FontFamily, TextAlign } from '@formicoidea/labre-core/model/headless';
2
3
  /**
3
4
  * What an EDGY artefact IS, as props — the ONE description the creation sites
4
5
  * and the templates both read.
@@ -23,6 +24,13 @@ export type EdgyBoxKind = 'outcome' | 'object' | 'activity';
23
24
  * used to spell the same `1.5` out a second time.
24
25
  */
25
26
  export declare const FACETS_SCALE = 1.5;
27
+ /** Default blank-board size. */
28
+ export declare const EDGY_BOARD_SIZE: {
29
+ readonly w: 1600;
30
+ readonly h: 1000;
31
+ };
32
+ /** Stroke width the 24 template relations are drawn with. */
33
+ export declare const RELATION_STROKE_WIDTH = 2;
26
34
  /**
27
35
  * An EDGY node, as props.
28
36
  *
package/dist/presets.js CHANGED
@@ -1,4 +1,7 @@
1
- import { FontFamily, ShapeStyle, TextAlign, } from '@formicoidea/labre-core/model';
1
+ // Values from the headless entry, never the model index: the toolbox
2
+ // (`toolbox.ts`, `docs/adr/0045`) builds on these presets from the
3
+ // editor-free `./interchange` entry.
4
+ import { FontFamily, ShapeStyle, TextAlign, } from '@formicoidea/labre-core/model/headless';
2
5
  import { Bound } from '@formicoidea/labre-core/global/gfx';
3
6
  import { ACTIVITY_VERTICES, INNER_FONT_SIZE, LABEL_FONT_SIZE, NODE_FILL, NODE_STROKE, NODE_STROKE_WIDTH, OUTCOME_RADIUS, } from './node/consts.js';
4
7
  import { EDGY_ROLE } from './roles.js';
@@ -9,6 +12,10 @@ import { EDGY_ROLE } from './roles.js';
9
12
  * used to spell the same `1.5` out a second time.
10
13
  */
11
14
  export const FACETS_SCALE = 1.5;
15
+ /** Default blank-board size. */
16
+ export const EDGY_BOARD_SIZE = { w: 1600, h: 1000 };
17
+ /** Stroke width the 24 template relations are drawn with. */
18
+ export const RELATION_STROKE_WIDTH = 2;
12
19
  /**
13
20
  * An EDGY node, as props.
14
21
  *
@@ -0,0 +1,67 @@
1
+ import type { FrameworkToolbox, SerializedElementProps, ToolboxPoint } from '@formicoidea/labre-core/interchange';
2
+ import { type EdgyBoxKind } from './presets.js';
3
+ /**
4
+ * EDGY's toolbar as pure builders (`docs/adr/0045`): what `createEdgyFacets`,
5
+ * `createEdgyBoard`, `createEdgyPeople`, `createEdgyBox` and the relation tool
6
+ * place, with no editor behind them. The actions call these, so an element
7
+ * built here and one placed from the toolbar are the same element.
8
+ *
9
+ * "EDGY dynamic" is NOT an item: it inserts a template snapshot through the
10
+ * template job (`createEdgyDynamic`), not elements a builder can return.
11
+ */
12
+ /** The facets background is drawn cropped to its circles and their names. */
13
+ export declare const EDGY_FACETS_SIZE: {
14
+ readonly w: number;
15
+ readonly h: number;
16
+ };
17
+ /** People's box: the glyph on top, its name centred under it. */
18
+ export declare const EDGY_PEOPLE_SIZE: {
19
+ readonly w: number;
20
+ readonly h: number;
21
+ };
22
+ /** The three facet names a facets background writes. */
23
+ export interface EdgyFacetsTexts {
24
+ identity?: string;
25
+ architecture?: string;
26
+ experience?: string;
27
+ }
28
+ /**
29
+ * The Enterprise Design facets background at `at`, cropped to the circles and
30
+ * the facet names. The three names are WRITTEN on the element rather than left
31
+ * to the model's defaults, as `createEdgyFacets` requires: the action passes
32
+ * them resolved in the host's language, a host passes its own or gets the
33
+ * English seed.
34
+ */
35
+ export declare function edgyFacetsElement(at: ToolboxPoint, texts?: EdgyFacetsTexts, local?: string): SerializedElementProps;
36
+ /** A blank EDGY board (the hover spotlight host) at `at`. */
37
+ export declare function edgyBoardElement(at: ToolboxPoint, local?: string): SerializedElementProps;
38
+ /**
39
+ * An Outcome, Object or Activity at `at`: one node carrying its name as the
40
+ * shape's own inner text.
41
+ */
42
+ export declare function edgyBoxElement(kind: EdgyBoxKind, at: ToolboxPoint, name?: string, local?: string): SerializedElementProps;
43
+ /**
44
+ * People at `at`, the top-left of {@link EDGY_PEOPLE_SIZE}: the glyph's
45
+ * ellipse centred on top, its name as a free text under it, and the group
46
+ * that makes the two one thing — last, holding both.
47
+ *
48
+ * The group carries no title: the numbered "Group N" the toolbar's group
49
+ * command writes counts the document's groups and asks the host's catalogue,
50
+ * which only an editor has (`createEdgyPeople`).
51
+ */
52
+ export declare function edgyPeopleElements(at: ToolboxPoint, name?: string, local?: string): SerializedElementProps[];
53
+ /**
54
+ * The connector the relation tool is armed with, between two ends: straight,
55
+ * the stencil ink, two units wide, no arrowhead, and the GENERIC role.
56
+ *
57
+ * The verb is not the builder's: in the editor, `EdgyRelationResolver` names a
58
+ * generic relation from the roles of the two elements it joins, as a verb role
59
+ * and a label. A `label` given here is written as the link's text and leaves
60
+ * the role generic.
61
+ */
62
+ export declare function edgyRelationElement(from: string, to: string, label?: string): SerializedElementProps;
63
+ /**
64
+ * Every item of the EDGY toolbar that places elements, in the toolbar's order,
65
+ * "EDGY dynamic" excepted (see the note at the top of this file).
66
+ */
67
+ export declare const EDGY_TOOLBOX: FrameworkToolbox;
@@ -0,0 +1,190 @@
1
+ import { ConnectorMode, PointStyle, StrokeStyle, } from '@formicoidea/labre-core/model/headless';
2
+ import { Bound } from '@formicoidea/labre-core/global/gfx';
3
+ import { edgyCommandsManifest } from './commands-manifest.js';
4
+ import { CROP_LABELED } from './facets-crop.js';
5
+ import { edgyElementLabel } from './metamodel.js';
6
+ import { LABEL_GAP, NODE_LABEL, NODE_SIZE, NODE_STROKE } from './node/consts.js';
7
+ import { EDGY_BOARD_SIZE, EDGY_LABEL_H, EDGY_LABEL_W, edgyLabelProps, edgyNodeProps, edgyNodeTextProps, FACETS_SCALE, RELATION_STROKE_WIDTH, } from './presets.js';
8
+ import { EDGY_ROLE } from './roles.js';
9
+ /**
10
+ * EDGY's toolbar as pure builders (`docs/adr/0045`): what `createEdgyFacets`,
11
+ * `createEdgyBoard`, `createEdgyPeople`, `createEdgyBox` and the relation tool
12
+ * place, with no editor behind them. The actions call these, so an element
13
+ * built here and one placed from the toolbar are the same element.
14
+ *
15
+ * "EDGY dynamic" is NOT an item: it inserts a template snapshot through the
16
+ * template job (`createEdgyDynamic`), not elements a builder can return.
17
+ */
18
+ /** The facets background is drawn cropped to its circles and their names. */
19
+ export const EDGY_FACETS_SIZE = {
20
+ w: CROP_LABELED.w * FACETS_SCALE,
21
+ h: CROP_LABELED.h * FACETS_SCALE,
22
+ };
23
+ /** People's box: the glyph on top, its name centred under it. */
24
+ export const EDGY_PEOPLE_SIZE = {
25
+ w: Math.max(NODE_SIZE.people.w, EDGY_LABEL_W),
26
+ h: NODE_SIZE.people.h + LABEL_GAP + EDGY_LABEL_H,
27
+ };
28
+ /**
29
+ * The Enterprise Design facets background at `at`, cropped to the circles and
30
+ * the facet names. The three names are WRITTEN on the element rather than left
31
+ * to the model's defaults, as `createEdgyFacets` requires: the action passes
32
+ * them resolved in the host's language, a host passes its own or gets the
33
+ * English seed.
34
+ */
35
+ export function edgyFacetsElement(at, texts = {}, local = 'facets') {
36
+ const name = (zone) => texts[zone]?.trim() || edgyElementLabel(zone);
37
+ return {
38
+ type: 'edgy',
39
+ id: local,
40
+ cropToCircles: true,
41
+ // The frame: what makes this an EDGY board rather than three circles, and
42
+ // what a finding is attributed to.
43
+ role: EDGY_ROLE.facets,
44
+ identityLabel: name('identity'),
45
+ architectureLabel: name('architecture'),
46
+ experienceLabel: name('experience'),
47
+ xywh: new Bound(at.x, at.y, EDGY_FACETS_SIZE.w, EDGY_FACETS_SIZE.h).serialize(),
48
+ };
49
+ }
50
+ /** A blank EDGY board (the hover spotlight host) at `at`. */
51
+ export function edgyBoardElement(at, local = 'board') {
52
+ return {
53
+ type: 'edgyBoard',
54
+ id: local,
55
+ // The blank board is a frame too: the same rules apply to what is drawn on
56
+ // it, and the same map-wide arbitration has to have somewhere to live.
57
+ role: EDGY_ROLE.board,
58
+ xywh: new Bound(at.x, at.y, EDGY_BOARD_SIZE.w, EDGY_BOARD_SIZE.h).serialize(),
59
+ };
60
+ }
61
+ /**
62
+ * An Outcome, Object or Activity at `at`: one node carrying its name as the
63
+ * shape's own inner text.
64
+ */
65
+ export function edgyBoxElement(kind, at, name, local = kind) {
66
+ const { w, h } = NODE_SIZE[kind];
67
+ return {
68
+ ...edgyNodeProps(kind, {
69
+ xywh: new Bound(at.x, at.y, w, h).serialize(),
70
+ }),
71
+ ...edgyNodeTextProps(name?.trim() || NODE_LABEL[kind]),
72
+ id: local,
73
+ };
74
+ }
75
+ /**
76
+ * People at `at`, the top-left of {@link EDGY_PEOPLE_SIZE}: the glyph's
77
+ * ellipse centred on top, its name as a free text under it, and the group
78
+ * that makes the two one thing — last, holding both.
79
+ *
80
+ * The group carries no title: the numbered "Group N" the toolbar's group
81
+ * command writes counts the document's groups and asks the host's catalogue,
82
+ * which only an editor has (`createEdgyPeople`).
83
+ */
84
+ export function edgyPeopleElements(at, name, local = 'people') {
85
+ const { w, h } = NODE_SIZE.people;
86
+ const id = (part) => `${local}:${part}`;
87
+ const members = [
88
+ {
89
+ ...edgyNodeProps('people', {
90
+ xywh: new Bound(at.x + (EDGY_PEOPLE_SIZE.w - w) / 2, at.y, w, h).serialize(),
91
+ }),
92
+ id: id('node'),
93
+ },
94
+ {
95
+ ...edgyLabelProps(name?.trim() || NODE_LABEL.people, at.x + (EDGY_PEOPLE_SIZE.w - EDGY_LABEL_W) / 2, at.y + h + LABEL_GAP),
96
+ id: id('label'),
97
+ },
98
+ ];
99
+ return [
100
+ ...members,
101
+ {
102
+ type: 'group',
103
+ id: id('group'),
104
+ children: Object.fromEntries(members.map(m => [m.id, true])),
105
+ },
106
+ ];
107
+ }
108
+ /**
109
+ * The connector the relation tool is armed with, between two ends: straight,
110
+ * the stencil ink, two units wide, no arrowhead, and the GENERIC role.
111
+ *
112
+ * The verb is not the builder's: in the editor, `EdgyRelationResolver` names a
113
+ * generic relation from the roles of the two elements it joins, as a verb role
114
+ * and a label. A `label` given here is written as the link's text and leaves
115
+ * the role generic.
116
+ */
117
+ export function edgyRelationElement(from, to, label) {
118
+ return {
119
+ type: 'connector',
120
+ mode: ConnectorMode.Straight,
121
+ role: EDGY_ROLE.relation,
122
+ stroke: NODE_STROKE,
123
+ strokeStyle: StrokeStyle.Solid,
124
+ strokeWidth: RELATION_STROKE_WIDTH,
125
+ frontEndpointStyle: PointStyle.None,
126
+ rearEndpointStyle: PointStyle.None,
127
+ source: { id: from },
128
+ target: { id: to },
129
+ ...(label?.trim() ? { text: label.trim() } : {}),
130
+ };
131
+ }
132
+ /** The toolbar command's label, so a host names an item as the toolbar does. */
133
+ function label(commandId) {
134
+ const row = edgyCommandsManifest.find(r => r.id === commandId);
135
+ return {
136
+ labelKey: row?.labelKey ?? commandId,
137
+ labelFallback: row?.labelFallback ?? commandId,
138
+ };
139
+ }
140
+ const BOX_COMMANDS = {
141
+ outcome: 'edgy.addOutcome',
142
+ object: 'edgy.addObject',
143
+ activity: 'edgy.addActivity',
144
+ };
145
+ const boxItem = (kind) => ({
146
+ id: kind,
147
+ ...label(BOX_COMMANDS[kind]),
148
+ texts: ['name'],
149
+ size: NODE_SIZE[kind],
150
+ build: (at, texts, local) => [edgyBoxElement(kind, at, texts?.name, local)],
151
+ });
152
+ const relation = {
153
+ id: 'relation',
154
+ ...label('edgy.addRelation'),
155
+ build: edgyRelationElement,
156
+ };
157
+ /**
158
+ * Every item of the EDGY toolbar that places elements, in the toolbar's order,
159
+ * "EDGY dynamic" excepted (see the note at the top of this file).
160
+ */
161
+ export const EDGY_TOOLBOX = {
162
+ framework: 'edgy',
163
+ items: [
164
+ {
165
+ id: 'facets',
166
+ ...label('edgy.addFacets'),
167
+ texts: ['identity', 'architecture', 'experience'],
168
+ size: EDGY_FACETS_SIZE,
169
+ build: (at, texts, local) => [edgyFacetsElement(at, texts, local)],
170
+ },
171
+ {
172
+ id: 'board',
173
+ ...label('edgy.addBoard'),
174
+ texts: [],
175
+ size: EDGY_BOARD_SIZE,
176
+ build: (at, _texts, local) => [edgyBoardElement(at, local)],
177
+ },
178
+ {
179
+ id: 'people',
180
+ ...label('edgy.addPeople'),
181
+ texts: ['name'],
182
+ size: EDGY_PEOPLE_SIZE,
183
+ build: (at, texts, local) => edgyPeopleElements(at, texts?.name, local),
184
+ },
185
+ boxItem('outcome'),
186
+ boxItem('object'),
187
+ boxItem('activity'),
188
+ ],
189
+ links: [relation],
190
+ };
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-edgy",
3
3
  "description": "Labre edgy framework for @formicoidea/labre-core.",
4
- "version": "0.59.0",
4
+ "version": "0.61.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -25,13 +25,17 @@
25
25
  "./commands-manifest": {
26
26
  "types": "./dist/commands-manifest.d.ts",
27
27
  "import": "./dist/commands-manifest.js"
28
+ },
29
+ "./interchange": {
30
+ "types": "./dist/interchange-headless.d.ts",
31
+ "import": "./dist/interchange-headless.js"
28
32
  }
29
33
  },
30
34
  "files": [
31
35
  "dist"
32
36
  ],
33
37
  "dependencies": {
34
- "@formicoidea/labre-core": "0.59.0",
38
+ "@formicoidea/labre-core": "0.61.0",
35
39
  "@preact/signals-core": "^1.8.0",
36
40
  "lit": "^3.2.0",
37
41
  "yjs": "^13.6.21"