@formicoidea/labre-framework-edgy 0.37.0 → 0.38.2
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.d.ts +1 -1
- package/dist/actions.js +17 -58
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/presets.d.ts +69 -0
- package/dist/presets.js +86 -0
- package/dist/templates/dynamic.d.ts +26 -0
- package/dist/templates/dynamic.js +106 -0
- package/dist/templates/index.d.ts +6 -19
- package/dist/templates/index.js +78 -122
- package/package.json +3 -3
package/dist/actions.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import type { BlockStdScope } from '@formicoidea/labre-core/std';
|
|
2
2
|
import { type GfxController } from '@formicoidea/labre-core/std/gfx';
|
|
3
|
-
|
|
3
|
+
import { type EdgyBoxKind } from './presets.js';
|
|
4
4
|
/**
|
|
5
5
|
* Create the Enterprise Design Facets diagram centred on the viewport —
|
|
6
6
|
* cropped to the circles + facet labels (no dead margins around the Venn).
|
package/dist/actions.js
CHANGED
|
@@ -2,23 +2,26 @@ import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
|
|
|
2
2
|
import { ConnectorTool } from '@formicoidea/labre-core/gfx/connector';
|
|
3
3
|
import { createGroupCommand } from '@formicoidea/labre-core/gfx/group';
|
|
4
4
|
import { createTemplateJob } from '@formicoidea/labre-core/gfx/template';
|
|
5
|
-
import { ConnectorMode,
|
|
5
|
+
import { ConnectorMode, PointStyle, StrokeStyle } from '@formicoidea/labre-core/model';
|
|
6
6
|
import { translateKey } from '@formicoidea/labre-core/shared/services';
|
|
7
7
|
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
8
8
|
import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
|
|
9
9
|
import { CROP_LABELED } from './consts.js';
|
|
10
|
-
import {
|
|
10
|
+
import { LABEL_GAP, NODE_LABEL, nodeLabelKey, NODE_SIZE, NODE_STROKE, } from './node/consts.js';
|
|
11
|
+
import { EDGY_LABEL_W, edgyLabelProps, edgyNodeProps, edgyNodeTextProps, FACETS_SCALE, } from './presets.js';
|
|
11
12
|
import { EDGY_ROLE } from './roles.js';
|
|
12
|
-
import { edgyDynamicTemplate } from './templates/
|
|
13
|
+
import { edgyDynamicTemplate } from './templates/dynamic.js';
|
|
13
14
|
/** Stroke width the 24 template relations are drawn with. */
|
|
14
15
|
const RELATION_STROKE_WIDTH = 2;
|
|
15
|
-
/**
|
|
16
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Standalone creation actions for the EDGY toolbox — the behaviour layer,
|
|
18
|
+
* lifted out of `toolbar/edgy-menu.ts` by PF3 so the menu can become a pure
|
|
19
|
+
* renderer over the command registry. They emit no telemetry: the single
|
|
20
|
+
* emission point is `runCommand` (`docs/adr/0008`).
|
|
21
|
+
*/
|
|
17
22
|
/** Default blank-board size. */
|
|
18
23
|
const BOARD_W = 1600;
|
|
19
24
|
const BOARD_H = 1000;
|
|
20
|
-
/** Height of the native People free-text label. */
|
|
21
|
-
const LABEL_H = LABEL_FONT_SIZE + 8;
|
|
22
25
|
const gfxOf = (std) => std.get(GfxControllerIdentifier);
|
|
23
26
|
function finish(gfx, id) {
|
|
24
27
|
gfx.doc.captureSync();
|
|
@@ -26,38 +29,6 @@ function finish(gfx, id) {
|
|
|
26
29
|
gfx.selection.set({ elements: [id], editing: false });
|
|
27
30
|
// Keep the palette open (native sub-menu behaviour).
|
|
28
31
|
}
|
|
29
|
-
/**
|
|
30
|
-
* Shared props for an EDGY node shape.
|
|
31
|
-
*
|
|
32
|
-
* The `role` is the PERSISTED KIND and nothing more: somebody picking "Object"
|
|
33
|
-
* in the palette has said "this is an object", not "this is a Channel". The
|
|
34
|
-
* twelve official elements are named by the metamodel template, which knows
|
|
35
|
-
* which of them it is drawing; a base element created here specialises
|
|
36
|
-
* `edgy:element` through its kind and is judged by every rule written on the
|
|
37
|
-
* root — the overlap rule — and by none written on a leaf.
|
|
38
|
-
*/
|
|
39
|
-
const baseShapeProps = (kind) => ({
|
|
40
|
-
type: 'edgyNode',
|
|
41
|
-
kind,
|
|
42
|
-
role: EDGY_ROLE[kind],
|
|
43
|
-
filled: true,
|
|
44
|
-
fillColor: NODE_FILL,
|
|
45
|
-
strokeColor: NODE_STROKE,
|
|
46
|
-
shapeStyle: ShapeStyle.General,
|
|
47
|
-
roughness: 0,
|
|
48
|
-
});
|
|
49
|
-
/** Add a native free-text label (Inter), used for the People node. */
|
|
50
|
-
function addLabel(surface, text, x, y) {
|
|
51
|
-
return surface.addElement({
|
|
52
|
-
type: 'text',
|
|
53
|
-
text,
|
|
54
|
-
fontFamily: FontFamily.Inter,
|
|
55
|
-
fontSize: LABEL_FONT_SIZE,
|
|
56
|
-
color: NODE_STROKE,
|
|
57
|
-
textAlign: 'center',
|
|
58
|
-
xywh: new Bound(x, y, 120, LABEL_H).serialize(),
|
|
59
|
-
});
|
|
60
|
-
}
|
|
61
32
|
/**
|
|
62
33
|
* Create the Enterprise Design Facets diagram centred on the viewport —
|
|
63
34
|
* cropped to the circles + facet labels (no dead margins around the Venn).
|
|
@@ -119,21 +90,13 @@ export function createEdgyBox(std, kind) {
|
|
|
119
90
|
return;
|
|
120
91
|
const { w, h } = NODE_SIZE[kind];
|
|
121
92
|
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
122
|
-
const shapeType = kind === 'activity' ? 'polygon' : 'rect';
|
|
123
93
|
const id = surface.addElement({
|
|
124
|
-
...
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
radius: kind === 'outcome' ? OUTCOME_RADIUS : 0,
|
|
128
|
-
vertices: kind === 'activity' ? ACTIVITY_VERTICES : null,
|
|
94
|
+
...edgyNodeProps(kind, {
|
|
95
|
+
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
96
|
+
}),
|
|
129
97
|
// Translated HERE and once: what a gesture writes onto the canvas is
|
|
130
98
|
// document content the moment it lands (`nodeLabelKey`).
|
|
131
|
-
|
|
132
|
-
color: NODE_STROKE,
|
|
133
|
-
fontFamily: FontFamily.Inter,
|
|
134
|
-
fontSize: INNER_FONT_SIZE,
|
|
135
|
-
textAlign: 'center',
|
|
136
|
-
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
99
|
+
...edgyNodeTextProps(translateKey(std, nodeLabelKey(kind), NODE_LABEL[kind])),
|
|
137
100
|
});
|
|
138
101
|
finish(gfx, id);
|
|
139
102
|
}
|
|
@@ -148,14 +111,10 @@ export function createEdgyPeople(std) {
|
|
|
148
111
|
return;
|
|
149
112
|
const { w, h } = NODE_SIZE.people;
|
|
150
113
|
const { centerX: cx, centerY: cy } = gfx.viewport;
|
|
151
|
-
const nodeId = surface.addElement({
|
|
152
|
-
...baseShapeProps('people'),
|
|
153
|
-
shapeType: 'ellipse',
|
|
154
|
-
// No visible outline — People is just the glyph; the ellipse is the bound.
|
|
155
|
-
strokeWidth: 0,
|
|
114
|
+
const nodeId = surface.addElement(edgyNodeProps('people', {
|
|
156
115
|
xywh: new Bound(cx - w / 2, cy - h / 2, w, h).serialize(),
|
|
157
|
-
});
|
|
158
|
-
const labelId =
|
|
116
|
+
}));
|
|
117
|
+
const labelId = surface.addElement(edgyLabelProps(translateKey(std, nodeLabelKey('people'), NODE_LABEL.people), cx - EDGY_LABEL_W / 2, cy + h / 2 + LABEL_GAP));
|
|
159
118
|
const [, result] = std.command.exec(createGroupCommand, {
|
|
160
119
|
elements: [nodeId, labelId],
|
|
161
120
|
});
|
package/dist/index.d.ts
CHANGED
|
@@ -2,3 +2,4 @@ export { edgyCommandIcons, edgyCommands } from './commands.js';
|
|
|
2
2
|
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
|
+
export { EDGY_RULES } from './rules.js';
|
package/dist/index.js
CHANGED
|
@@ -4,3 +4,4 @@ export { edgyTranslationEntries } from './translations.js';
|
|
|
4
4
|
// library's own cross-framework guard test — can read without an editor.
|
|
5
5
|
export { EDGY_ROLE, EDGY_ROLES } from './roles.js';
|
|
6
6
|
export { EDGY_READING } from './reading.js';
|
|
7
|
+
export { EDGY_RULES } from './rules.js';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { type EdgyNodeKind, FontFamily, TextAlign } from '@formicoidea/labre-core/model';
|
|
2
|
+
/**
|
|
3
|
+
* What an EDGY artefact IS, as props — the ONE description the creation sites
|
|
4
|
+
* and the templates both read.
|
|
5
|
+
*
|
|
6
|
+
* Split out of `actions.ts` for the reason Wardley's and BPMN's `presets.ts`
|
|
7
|
+
* give, and which the 2026-09-09 template audit measured on this pack: the
|
|
8
|
+
* palette restated by hand what a node is, and the copy drifted. The four
|
|
9
|
+
* single-element templates had lost the `role` the toolbox has written since
|
|
10
|
+
* the EDGY roles landed (so `edgy.overlapping-artefacts` saw nothing, the auto
|
|
11
|
+
* legend legended nothing and the info panel would not open), People arrived
|
|
12
|
+
* ungrouped from its name, and the label box was 16/24/+70 against the
|
|
13
|
+
* toolbox's 18/26/+72. Those templates are DERIVED now
|
|
14
|
+
* (`templateFromCommand`), and the compositions that cannot derive are built
|
|
15
|
+
* on the helpers below — so there is nothing left to restate.
|
|
16
|
+
*/
|
|
17
|
+
/** The three kinds the palette drops as a labelled box (People is not one). */
|
|
18
|
+
export type EdgyBoxKind = 'outcome' | 'object' | 'activity';
|
|
19
|
+
/**
|
|
20
|
+
* Default facets-diagram size (REF aspect, scaled up so it reads on canvas).
|
|
21
|
+
*
|
|
22
|
+
* Here rather than at the creation site because the "Facets diagram" template
|
|
23
|
+
* used to spell the same `1.5` out a second time.
|
|
24
|
+
*/
|
|
25
|
+
export declare const FACETS_SCALE = 1.5;
|
|
26
|
+
/**
|
|
27
|
+
* An EDGY node, as props.
|
|
28
|
+
*
|
|
29
|
+
* The `role` is the PERSISTED KIND and nothing more: somebody picking "Object"
|
|
30
|
+
* in the palette has said "this is an object", not "this is a Channel". The
|
|
31
|
+
* twelve official elements are named by the metamodel template, which knows
|
|
32
|
+
* which of them it is drawing; a base element created here specialises
|
|
33
|
+
* `edgy:element` through its kind and is judged by every rule written on the
|
|
34
|
+
* root — the overlap rule — and by none written on a leaf.
|
|
35
|
+
*
|
|
36
|
+
* No `text`: the inner words are content, added by the caller through
|
|
37
|
+
* {@link edgyNodeTextProps} (at placement, translated once) or overridden by an
|
|
38
|
+
* illustrative template.
|
|
39
|
+
*/
|
|
40
|
+
export declare function edgyNodeProps(kind: EdgyNodeKind, box: {
|
|
41
|
+
xywh: string;
|
|
42
|
+
}): Record<string, unknown> & {
|
|
43
|
+
type: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* The words INSIDE a box, as props.
|
|
47
|
+
*
|
|
48
|
+
* `text` is a plain STRING, as `Surface.addElement` takes it; a snapshot writes
|
|
49
|
+
* the same field as a serialized `Y.Text`, which is the one key a hand-written
|
|
50
|
+
* template overrides on the way in.
|
|
51
|
+
*/
|
|
52
|
+
export declare function edgyNodeTextProps(text: string, fontSize?: number): {
|
|
53
|
+
text: string;
|
|
54
|
+
color: string;
|
|
55
|
+
fontFamily: FontFamily;
|
|
56
|
+
fontSize: number;
|
|
57
|
+
textAlign: TextAlign;
|
|
58
|
+
};
|
|
59
|
+
/** Width of the free-text label a People node travels with. */
|
|
60
|
+
export declare const EDGY_LABEL_W = 120;
|
|
61
|
+
/** Height of that label (Inter, size 18). */
|
|
62
|
+
export declare const EDGY_LABEL_H: number;
|
|
63
|
+
/**
|
|
64
|
+
* The NAME under a People node, as a native free-text element — the one EDGY
|
|
65
|
+
* artefact whose name is not stored on the shape itself.
|
|
66
|
+
*/
|
|
67
|
+
export declare function edgyLabelProps(text: string, x: number, y: number): Record<string, unknown> & {
|
|
68
|
+
type: string;
|
|
69
|
+
};
|
package/dist/presets.js
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { FontFamily, ShapeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
2
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
3
|
+
import { ACTIVITY_VERTICES, INNER_FONT_SIZE, LABEL_FONT_SIZE, NODE_FILL, NODE_STROKE, NODE_STROKE_WIDTH, OUTCOME_RADIUS, } from './node/consts.js';
|
|
4
|
+
import { EDGY_ROLE } from './roles.js';
|
|
5
|
+
/**
|
|
6
|
+
* Default facets-diagram size (REF aspect, scaled up so it reads on canvas).
|
|
7
|
+
*
|
|
8
|
+
* Here rather than at the creation site because the "Facets diagram" template
|
|
9
|
+
* used to spell the same `1.5` out a second time.
|
|
10
|
+
*/
|
|
11
|
+
export const FACETS_SCALE = 1.5;
|
|
12
|
+
/**
|
|
13
|
+
* An EDGY node, as props.
|
|
14
|
+
*
|
|
15
|
+
* The `role` is the PERSISTED KIND and nothing more: somebody picking "Object"
|
|
16
|
+
* in the palette has said "this is an object", not "this is a Channel". The
|
|
17
|
+
* twelve official elements are named by the metamodel template, which knows
|
|
18
|
+
* which of them it is drawing; a base element created here specialises
|
|
19
|
+
* `edgy:element` through its kind and is judged by every rule written on the
|
|
20
|
+
* root — the overlap rule — and by none written on a leaf.
|
|
21
|
+
*
|
|
22
|
+
* No `text`: the inner words are content, added by the caller through
|
|
23
|
+
* {@link edgyNodeTextProps} (at placement, translated once) or overridden by an
|
|
24
|
+
* illustrative template.
|
|
25
|
+
*/
|
|
26
|
+
export function edgyNodeProps(kind, box) {
|
|
27
|
+
const people = kind === 'people';
|
|
28
|
+
return {
|
|
29
|
+
type: 'edgyNode',
|
|
30
|
+
kind,
|
|
31
|
+
role: EDGY_ROLE[kind],
|
|
32
|
+
filled: true,
|
|
33
|
+
fillColor: NODE_FILL,
|
|
34
|
+
strokeColor: NODE_STROKE,
|
|
35
|
+
shapeStyle: ShapeStyle.General,
|
|
36
|
+
roughness: 0,
|
|
37
|
+
shapeType: people ? 'ellipse' : kind === 'activity' ? 'polygon' : 'rect',
|
|
38
|
+
// No visible outline on People — it IS the glyph the renderer draws, and
|
|
39
|
+
// the ellipse is only its bound.
|
|
40
|
+
strokeWidth: people ? 0 : NODE_STROKE_WIDTH,
|
|
41
|
+
// The two keys only a BOX has: People carries neither, and a key some
|
|
42
|
+
// kinds write and others do not is the truth about the shapes.
|
|
43
|
+
...(people
|
|
44
|
+
? {}
|
|
45
|
+
: {
|
|
46
|
+
radius: kind === 'outcome' ? OUTCOME_RADIUS : 0,
|
|
47
|
+
vertices: kind === 'activity' ? ACTIVITY_VERTICES : null,
|
|
48
|
+
}),
|
|
49
|
+
xywh: box.xywh,
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* The words INSIDE a box, as props.
|
|
54
|
+
*
|
|
55
|
+
* `text` is a plain STRING, as `Surface.addElement` takes it; a snapshot writes
|
|
56
|
+
* the same field as a serialized `Y.Text`, which is the one key a hand-written
|
|
57
|
+
* template overrides on the way in.
|
|
58
|
+
*/
|
|
59
|
+
export function edgyNodeTextProps(text, fontSize = INNER_FONT_SIZE) {
|
|
60
|
+
return {
|
|
61
|
+
text,
|
|
62
|
+
color: NODE_STROKE,
|
|
63
|
+
fontFamily: FontFamily.Inter,
|
|
64
|
+
fontSize,
|
|
65
|
+
textAlign: TextAlign.Center,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
/** Width of the free-text label a People node travels with. */
|
|
69
|
+
export const EDGY_LABEL_W = 120;
|
|
70
|
+
/** Height of that label (Inter, size 18). */
|
|
71
|
+
export const EDGY_LABEL_H = LABEL_FONT_SIZE + 8;
|
|
72
|
+
/**
|
|
73
|
+
* The NAME under a People node, as a native free-text element — the one EDGY
|
|
74
|
+
* artefact whose name is not stored on the shape itself.
|
|
75
|
+
*/
|
|
76
|
+
export function edgyLabelProps(text, x, y) {
|
|
77
|
+
return {
|
|
78
|
+
type: 'text',
|
|
79
|
+
text,
|
|
80
|
+
fontFamily: FontFamily.Inter,
|
|
81
|
+
fontSize: LABEL_FONT_SIZE,
|
|
82
|
+
color: NODE_STROKE,
|
|
83
|
+
textAlign: TextAlign.Center,
|
|
84
|
+
xywh: new Bound(x, y, EDGY_LABEL_W, EDGY_LABEL_H).serialize(),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type Template } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
/**
|
|
3
|
+
* The "EDGY dynamic" template — the relational metamodel drawn on the facets
|
|
4
|
+
* background, and the ONE template of this pack that cannot be derived from
|
|
5
|
+
* its command: `edgy.insertDynamic` INSERTS this very object
|
|
6
|
+
* (`createEdgyDynamic`), so deriving it from itself would be circular.
|
|
7
|
+
*
|
|
8
|
+
* Its own module for exactly that reason: `actions.ts` needs the template,
|
|
9
|
+
* `templates/index.ts` needs the command registry to derive the rest of the
|
|
10
|
+
* palette, and a single templates module would close the loop
|
|
11
|
+
* templates → commands → actions → templates.
|
|
12
|
+
*/
|
|
13
|
+
/**
|
|
14
|
+
* Background scale (from REF coords to model coords): large enough that the
|
|
15
|
+
* default-size nodes breathe inside each zone. The background is cropped to
|
|
16
|
+
* the circles (`cropToCircles`), so its xywh covers CROP × DYN_SCALE.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DYN_SCALE = 4.8;
|
|
19
|
+
/**
|
|
20
|
+
* Reference coords → template model coords: the background element sits at
|
|
21
|
+
* (0,0) and renders the cropped circles box, so a reference point maps to
|
|
22
|
+
* `(p - CROP.origin) × DYN_SCALE`.
|
|
23
|
+
*/
|
|
24
|
+
export declare function dynToModel(refX: number, refY: number): [number, number];
|
|
25
|
+
/** Exported for direct insertion from the EDGY senior toolbar menu. */
|
|
26
|
+
export declare const edgyDynamicTemplate: Template;
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
import { ConnectorMode, PointStyle, StrokeStyle } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { CROP } from '../consts.js';
|
|
4
|
+
import { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_RELATIONS, EDGY_ZONE_FILL, edgyElementLabel, } from '../metamodel.js';
|
|
5
|
+
import { NODE_SIZE, NODE_STROKE } from '../node/consts.js';
|
|
6
|
+
import { edgyNodeProps, edgyNodeTextProps } from '../presets.js';
|
|
7
|
+
import { EDGY_RELATION_LABEL_DISTANCE, edgyVerbLabelXYWH } from '../relation.js';
|
|
8
|
+
import { EDGY_ROLE, EDGY_VERB_ROLE } from '../roles.js';
|
|
9
|
+
/**
|
|
10
|
+
* The "EDGY dynamic" template — the relational metamodel drawn on the facets
|
|
11
|
+
* background, and the ONE template of this pack that cannot be derived from
|
|
12
|
+
* its command: `edgy.insertDynamic` INSERTS this very object
|
|
13
|
+
* (`createEdgyDynamic`), so deriving it from itself would be circular.
|
|
14
|
+
*
|
|
15
|
+
* Its own module for exactly that reason: `actions.ts` needs the template,
|
|
16
|
+
* `templates/index.ts` needs the command registry to derive the rest of the
|
|
17
|
+
* palette, and a single templates module would close the loop
|
|
18
|
+
* templates → commands → actions → templates.
|
|
19
|
+
*/
|
|
20
|
+
/**
|
|
21
|
+
* Background scale (from REF coords to model coords): large enough that the
|
|
22
|
+
* default-size nodes breathe inside each zone. The background is cropped to
|
|
23
|
+
* the circles (`cropToCircles`), so its xywh covers CROP × DYN_SCALE.
|
|
24
|
+
*/
|
|
25
|
+
export const DYN_SCALE = 4.8;
|
|
26
|
+
/**
|
|
27
|
+
* Reference coords → template model coords: the background element sits at
|
|
28
|
+
* (0,0) and renders the cropped circles box, so a reference point maps to
|
|
29
|
+
* `(p - CROP.origin) × DYN_SCALE`.
|
|
30
|
+
*/
|
|
31
|
+
export function dynToModel(refX, refY) {
|
|
32
|
+
return [(refX - CROP.x) * DYN_SCALE, (refY - CROP.y) * DYN_SCALE];
|
|
33
|
+
}
|
|
34
|
+
function dynamic() {
|
|
35
|
+
const out = {
|
|
36
|
+
bg: {
|
|
37
|
+
type: 'edgy',
|
|
38
|
+
showLabels: false,
|
|
39
|
+
showPictos: false,
|
|
40
|
+
cropToCircles: true,
|
|
41
|
+
// The frame a finding is attributed to. Stamped here and nowhere else in
|
|
42
|
+
// this template: the background is what makes the board an EDGY board.
|
|
43
|
+
role: EDGY_ROLE.facets,
|
|
44
|
+
xywh: `[0,0,${CROP.w * DYN_SCALE},${CROP.h * DYN_SCALE}]`,
|
|
45
|
+
},
|
|
46
|
+
};
|
|
47
|
+
for (const [key, { kind, cx, cy, w, zone }] of Object.entries(EDGY_DYNAMIC_NODES)) {
|
|
48
|
+
const nw = w ?? NODE_SIZE[kind].w;
|
|
49
|
+
const nh = NODE_SIZE[kind].h;
|
|
50
|
+
const [mx, my] = dynToModel(cx, cy);
|
|
51
|
+
const name = edgyElementLabel(key);
|
|
52
|
+
out[key] = {
|
|
53
|
+
// What an EDGY node IS — the same description the toolbox draws from.
|
|
54
|
+
...edgyNodeProps(kind, {
|
|
55
|
+
xywh: `[${mx - nw / 2},${my - nh / 2},${nw},${nh}]`,
|
|
56
|
+
}),
|
|
57
|
+
...edgyNodeTextProps(name),
|
|
58
|
+
// A snapshot stores the words as a serialized `Y.Text`, where
|
|
59
|
+
// `addElement` takes a plain string.
|
|
60
|
+
text: surfaceText(name),
|
|
61
|
+
fillColor: EDGY_ZONE_FILL[zone],
|
|
62
|
+
// The OFFICIAL element, not just its kind: this template IS the
|
|
63
|
+
// metamodel, so its Purpose is a Purpose and not merely an outcome.
|
|
64
|
+
role: EDGY_ROLE[key],
|
|
65
|
+
};
|
|
66
|
+
}
|
|
67
|
+
EDGY_DYNAMIC_RELATIONS.forEach(([src, dst, verb, t], i) => {
|
|
68
|
+
out[`rel${i}`] = {
|
|
69
|
+
type: 'connector',
|
|
70
|
+
mode: ConnectorMode.Straight,
|
|
71
|
+
stroke: NODE_STROKE,
|
|
72
|
+
strokeWidth: 2,
|
|
73
|
+
strokeStyle: StrokeStyle.Solid,
|
|
74
|
+
frontEndpointStyle: PointStyle.None,
|
|
75
|
+
rearEndpointStyle: PointStyle.None,
|
|
76
|
+
source: { id: src },
|
|
77
|
+
target: { id: dst },
|
|
78
|
+
// The verb, as a ROLE — one per canonical verb, derived from this very
|
|
79
|
+
// table (`../roles.ts`). Source is the subject and target the object
|
|
80
|
+
// (`docs/adr/0010` tier 1), which is exactly how the row above reads, so
|
|
81
|
+
// `edgy.non-canonical-link` finds all 24 of these sentences legal.
|
|
82
|
+
role: EDGY_VERB_ROLE[verb],
|
|
83
|
+
// Native connector label: the verb travels with the link. The x/y are
|
|
84
|
+
// re-centred on the path at the first layout, but the w/h ARE the label
|
|
85
|
+
// box — size it to the verb so the text lays out on one line. The
|
|
86
|
+
// distance slides the verb along the link like the reference diagram.
|
|
87
|
+
text: surfaceText(verb),
|
|
88
|
+
labelXYWH: edgyVerbLabelXYWH(verb),
|
|
89
|
+
labelOffset: { distance: t ?? EDGY_RELATION_LABEL_DISTANCE },
|
|
90
|
+
};
|
|
91
|
+
});
|
|
92
|
+
return out;
|
|
93
|
+
}
|
|
94
|
+
const NAME = 'EDGY dynamic';
|
|
95
|
+
/** Exported for direct insertion from the EDGY senior toolbar menu. */
|
|
96
|
+
export const edgyDynamicTemplate = {
|
|
97
|
+
name: NAME,
|
|
98
|
+
type: 'template',
|
|
99
|
+
preview: `<svg width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"><circle cx="55" cy="34" r="18" fill="#00ea4e" opacity="0.9"/><circle cx="80" cy="34" r="18" fill="#034cee" opacity="0.9"/><circle cx="67" cy="54" r="18" fill="#ff0056" opacity="0.9"/><path d="M55 30 H80 M56 31 L66 52 M79 31 L69 52" stroke="#fff" stroke-width="2"/><rect x="51" y="26" width="8" height="8" fill="#fff"/><circle cx="80" cy="30" r="4" fill="#fff"/><path d="M62 49 h6 l3 3 -3 3 h-6 z" fill="#fff"/></svg>`,
|
|
100
|
+
// Stamped by hand, and it is the one `commandId` in this pack that is a
|
|
101
|
+
// literal rather than a derivation: importing the registry here would close
|
|
102
|
+
// the very loop this module exists to avoid. The coverage test pairs the two
|
|
103
|
+
// and fails the day the command is renamed.
|
|
104
|
+
commandId: 'edgy.insertDynamic',
|
|
105
|
+
content: makeTemplateSnapshot(dynamic(), NAME),
|
|
106
|
+
};
|
|
@@ -1,23 +1,10 @@
|
|
|
1
|
-
import { type
|
|
1
|
+
import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* the
|
|
6
|
-
|
|
7
|
-
export declare const DYN_SCALE = 4.8;
|
|
8
|
-
/**
|
|
9
|
-
* The metamodel itself — the 12 elements and the 24 relations — now lives in
|
|
10
|
-
* `../metamodel.ts`, so the role vocabulary can DERIVE from it without this
|
|
11
|
-
* module and that one importing each other. Re-exported here under the names
|
|
12
|
-
* they have always had: nothing that reads them had to change.
|
|
3
|
+
* The metamodel itself — the 12 elements and the 24 relations — lives in
|
|
4
|
+
* `../metamodel.ts`, and the template that draws it in `./dynamic.ts`.
|
|
5
|
+
* Re-exported here under the names they have always had: nothing that reads
|
|
6
|
+
* them had to change.
|
|
13
7
|
*/
|
|
14
8
|
export { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_RELATIONS, type EdgyElementName, } from '../metamodel.js';
|
|
15
|
-
|
|
16
|
-
* Reference coords → template model coords: the background element sits at
|
|
17
|
-
* (0,0) and renders the cropped circles box, so a reference point maps to
|
|
18
|
-
* `(p - CROP.origin) × DYN_SCALE`.
|
|
19
|
-
*/
|
|
20
|
-
export declare function dynToModel(refX: number, refY: number): [number, number];
|
|
21
|
-
/** Exported for direct insertion from the EDGY senior toolbar menu. */
|
|
22
|
-
export declare const edgyDynamicTemplate: Template;
|
|
9
|
+
export { DYN_SCALE, dynToModel, edgyDynamicTemplate } from './dynamic.js';
|
|
23
10
|
export declare const edgyTemplateCategory: TemplateCategory;
|
package/dist/templates/index.js
CHANGED
|
@@ -1,11 +1,41 @@
|
|
|
1
|
-
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
1
|
+
import { makeTemplateSnapshot, surfaceText, surfaceYMap, templateFromCommand, } from '@formicoidea/labre-core/gfx/template';
|
|
2
2
|
import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
3
|
+
import { edgyCommands } from '../commands.js';
|
|
4
|
+
import { NODE_STROKE, NODE_STROKE_WIDTH } from '../node/consts.js';
|
|
5
|
+
import { edgyNodeProps, edgyNodeTextProps } from '../presets.js';
|
|
6
|
+
import { edgyDynamicTemplate } from './dynamic.js';
|
|
7
|
+
/**
|
|
8
|
+
* The EDGY palette — DERIVED from the toolbox, one template per command.
|
|
9
|
+
*
|
|
10
|
+
* Every single-artefact entry below is what its command actually draws, run
|
|
11
|
+
* once against a recording surface. They used to be hand-written restatements
|
|
12
|
+
* and had drifted exactly as far as a copy drifts (audit of 2026-09-09): the
|
|
13
|
+
* four base elements carried no `role` at all, so `edgy.overlapping-artefacts`
|
|
14
|
+
* never saw them, the auto legend never legended them and the info panel would
|
|
15
|
+
* not open on them; "People" arrived as two loose elements where the button
|
|
16
|
+
* groups the glyph with its name; the texts were English literals outside the
|
|
17
|
+
* translation seam; the label box was 16/24/+70 against the toolbox's
|
|
18
|
+
* 18/26/+72; and `1.5` was spelled out a second time instead of
|
|
19
|
+
* `FACETS_SCALE`. Derived, none of that can happen again — and
|
|
20
|
+
* `templates-parity.unit.spec.ts` re-runs each command and compares.
|
|
21
|
+
*
|
|
22
|
+
* The five COMPOSITIONS stay hand-written: a customer journey or an org chart
|
|
23
|
+
* is an arrangement of a dozen artefacts, which no single command draws. They
|
|
24
|
+
* are built on the same presets, and the same test checks their composition.
|
|
25
|
+
*/
|
|
26
|
+
/** The command a derived template is the picture of. Throws rather than skips. */
|
|
27
|
+
function byId(id) {
|
|
28
|
+
const command = edgyCommands.find(entry => entry.id === id);
|
|
29
|
+
if (!command)
|
|
30
|
+
throw new Error(`[edgy] templates: no command "${id}"`);
|
|
31
|
+
return command;
|
|
32
|
+
}
|
|
8
33
|
// EDGY facet palette (header / pale sub-card).
|
|
34
|
+
//
|
|
35
|
+
// NOT `EDGY_ZONE_FILL`: the metamodel's palette is ONE pastel per zone — the
|
|
36
|
+
// colour an official element is drawn with — where the overview needs a
|
|
37
|
+
// saturated header AND the pale card that sits on it. Two colours per facet is
|
|
38
|
+
// a different table, not a drifted copy of that one.
|
|
9
39
|
const C = {
|
|
10
40
|
identity: ['#1ec873', '#9fe6c2'],
|
|
11
41
|
organisation: ['#4fd0ea', '#c2eef8'],
|
|
@@ -39,39 +69,28 @@ function rect(x, y, w, h, opts = {}) {
|
|
|
39
69
|
return el;
|
|
40
70
|
}
|
|
41
71
|
/**
|
|
42
|
-
* An EDGY node
|
|
43
|
-
*
|
|
72
|
+
* An EDGY node of a COMPOSITION — {@link edgyNodeProps} says what that is, and
|
|
73
|
+
* this adds the two things an illustration decides for itself.
|
|
44
74
|
*
|
|
45
|
-
* `role`
|
|
46
|
-
*
|
|
47
|
-
* at,
|
|
48
|
-
* — the one that IS the metamodel — stamps its elements
|
|
75
|
+
* `role: undefined` writes nothing, and it is the point: the journey, the
|
|
76
|
+
* blueprint, the org chart and the facets overview are DRAWINGS the engine
|
|
77
|
+
* never looks at, a decision `edgy-dynamic.unit.spec.ts` pins. Only the EDGY
|
|
78
|
+
* dynamic template — the one that IS the metamodel — stamps its elements, and
|
|
79
|
+
* it does so through the same preset.
|
|
49
80
|
*/
|
|
50
81
|
function enode(kind, x, y, w, h, opts = {}) {
|
|
51
82
|
const el = {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
fillColor: opts.fill ?? NODE_FILL,
|
|
56
|
-
strokeColor: NODE_STROKE,
|
|
57
|
-
strokeWidth: kind === 'people' ? 0 : NODE_STROKE_WIDTH,
|
|
58
|
-
shapeStyle: ShapeStyle.General,
|
|
59
|
-
roughness: 0,
|
|
60
|
-
shapeType: kind === 'people' ? 'ellipse' : kind === 'activity' ? 'polygon' : 'rect',
|
|
61
|
-
radius: kind === 'outcome' ? OUTCOME_RADIUS : 0,
|
|
62
|
-
xywh: `[${x},${y},${w},${h}]`,
|
|
83
|
+
...edgyNodeProps(kind, { xywh: `[${x},${y},${w},${h}]` }),
|
|
84
|
+
role: undefined,
|
|
85
|
+
...(opts.fill === undefined ? {} : { fillColor: opts.fill }),
|
|
63
86
|
};
|
|
64
|
-
if (kind === 'activity')
|
|
65
|
-
el.vertices = ACTIVITY_VERTICES;
|
|
66
|
-
// `undefined` writes nothing: a neutral node keeps no `role` key.
|
|
67
|
-
if (opts.role !== undefined)
|
|
68
|
-
el.role = opts.role;
|
|
69
87
|
if (opts.text != null) {
|
|
88
|
+
Object.assign(el, edgyNodeTextProps(opts.text, opts.fontSize));
|
|
89
|
+
// A snapshot stores the words as a serialized `Y.Text`, where
|
|
90
|
+
// `addElement` takes a plain string.
|
|
70
91
|
el.text = surfaceText(opts.text);
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
el.fontSize = opts.fontSize ?? INNER_FONT_SIZE;
|
|
74
|
-
el.textAlign = TextAlign.Center;
|
|
92
|
+
if (opts.textColor !== undefined)
|
|
93
|
+
el.color = opts.textColor;
|
|
75
94
|
}
|
|
76
95
|
return el;
|
|
77
96
|
}
|
|
@@ -86,6 +105,15 @@ function label(x, y, w, h, str, opts = {}) {
|
|
|
86
105
|
xywh: `[${x},${y},${w},${h}]`,
|
|
87
106
|
};
|
|
88
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* The group a node and its name travel as — what `createEdgyPeople` writes and
|
|
110
|
+
* what these compositions had never carried, so dragging the person out of a
|
|
111
|
+
* journey left the word "Customer" behind.
|
|
112
|
+
*/
|
|
113
|
+
const pair = (node, name) => ({
|
|
114
|
+
type: 'group',
|
|
115
|
+
children: surfaceYMap({ [node]: true, [name]: true }),
|
|
116
|
+
});
|
|
89
117
|
function line(x1, y1, x2, y2, opts = {}) {
|
|
90
118
|
return {
|
|
91
119
|
type: 'connector',
|
|
@@ -204,6 +232,10 @@ function journey() {
|
|
|
204
232
|
return {
|
|
205
233
|
cust: enode('people', 40, 120, 64, 64, { fill: '#ffffff' }),
|
|
206
234
|
custL: label(20, 192, 104, 24, 'Customer', { fontSize: 14 }),
|
|
235
|
+
// The person and its name travel together, as `createEdgyPeople` writes
|
|
236
|
+
// them. A group is placed AFTER its members: the id middleware remaps
|
|
237
|
+
// `children` against elements it has already seen.
|
|
238
|
+
custG: pair('cust', 'custL'),
|
|
207
239
|
band: {
|
|
208
240
|
type: 'shape',
|
|
209
241
|
shapeType: 'polygon',
|
|
@@ -346,85 +378,16 @@ function orgChart() {
|
|
|
346
378
|
e6: attach('c', 'c1', { mode: ConnectorMode.Orthogonal }),
|
|
347
379
|
};
|
|
348
380
|
}
|
|
349
|
-
// ── EDGY dynamic (the relational metamodel, on the facets background) ──
|
|
350
381
|
/**
|
|
351
|
-
*
|
|
352
|
-
*
|
|
353
|
-
* the
|
|
354
|
-
|
|
355
|
-
export const DYN_SCALE = 4.8;
|
|
356
|
-
/**
|
|
357
|
-
* The metamodel itself — the 12 elements and the 24 relations — now lives in
|
|
358
|
-
* `../metamodel.ts`, so the role vocabulary can DERIVE from it without this
|
|
359
|
-
* module and that one importing each other. Re-exported here under the names
|
|
360
|
-
* they have always had: nothing that reads them had to change.
|
|
382
|
+
* The metamodel itself — the 12 elements and the 24 relations — lives in
|
|
383
|
+
* `../metamodel.ts`, and the template that draws it in `./dynamic.ts`.
|
|
384
|
+
* Re-exported here under the names they have always had: nothing that reads
|
|
385
|
+
* them had to change.
|
|
361
386
|
*/
|
|
362
387
|
export { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_RELATIONS, } from '../metamodel.js';
|
|
363
|
-
|
|
364
|
-
* Reference coords → template model coords: the background element sits at
|
|
365
|
-
* (0,0) and renders the cropped circles box, so a reference point maps to
|
|
366
|
-
* `(p - CROP.origin) × DYN_SCALE`.
|
|
367
|
-
*/
|
|
368
|
-
export function dynToModel(refX, refY) {
|
|
369
|
-
return [(refX - CROP.x) * DYN_SCALE, (refY - CROP.y) * DYN_SCALE];
|
|
370
|
-
}
|
|
371
|
-
function dynamic() {
|
|
372
|
-
const out = {
|
|
373
|
-
bg: {
|
|
374
|
-
type: 'edgy',
|
|
375
|
-
showLabels: false,
|
|
376
|
-
showPictos: false,
|
|
377
|
-
cropToCircles: true,
|
|
378
|
-
// The frame a finding is attributed to. Stamped here and nowhere else in
|
|
379
|
-
// this template: the background is what makes the board an EDGY board.
|
|
380
|
-
role: EDGY_ROLE.facets,
|
|
381
|
-
xywh: `[0,0,${CROP.w * DYN_SCALE},${CROP.h * DYN_SCALE}]`,
|
|
382
|
-
},
|
|
383
|
-
};
|
|
384
|
-
for (const [key, { kind, cx, cy, w, zone }] of Object.entries(EDGY_DYNAMIC_NODES)) {
|
|
385
|
-
const nw = w ?? NODE_SIZE[kind].w;
|
|
386
|
-
const nh = NODE_SIZE[kind].h;
|
|
387
|
-
const [mx, my] = dynToModel(cx, cy);
|
|
388
|
-
const name = edgyElementLabel(key);
|
|
389
|
-
out[key] = enode(kind, mx - nw / 2, my - nh / 2, nw, nh, {
|
|
390
|
-
text: name,
|
|
391
|
-
fill: EDGY_ZONE_FILL[zone],
|
|
392
|
-
// The OFFICIAL element, not just its kind: this template IS the
|
|
393
|
-
// metamodel, so its Purpose is a Purpose and not merely an outcome.
|
|
394
|
-
role: EDGY_ROLE[key],
|
|
395
|
-
});
|
|
396
|
-
}
|
|
397
|
-
EDGY_DYNAMIC_RELATIONS.forEach(([src, dst, verb, t], i) => {
|
|
398
|
-
out[`rel${i}`] = {
|
|
399
|
-
type: 'connector',
|
|
400
|
-
mode: ConnectorMode.Straight,
|
|
401
|
-
stroke: NODE_STROKE,
|
|
402
|
-
strokeWidth: 2,
|
|
403
|
-
strokeStyle: StrokeStyle.Solid,
|
|
404
|
-
frontEndpointStyle: PointStyle.None,
|
|
405
|
-
rearEndpointStyle: PointStyle.None,
|
|
406
|
-
source: { id: src },
|
|
407
|
-
target: { id: dst },
|
|
408
|
-
// The verb, as a ROLE — one per canonical verb, derived from this very
|
|
409
|
-
// table (`../roles.ts`). Source is the subject and target the object
|
|
410
|
-
// (`docs/adr/0010` tier 1), which is exactly how the row above reads, so
|
|
411
|
-
// `edgy.non-canonical-link` finds all 24 of these sentences legal.
|
|
412
|
-
role: EDGY_VERB_ROLE[verb],
|
|
413
|
-
// Native connector label: the verb travels with the link. The x/y are
|
|
414
|
-
// re-centred on the path at the first layout, but the w/h ARE the label
|
|
415
|
-
// box — size it to the verb so the text lays out on one line. The
|
|
416
|
-
// distance slides the verb along the link like the reference diagram.
|
|
417
|
-
text: surfaceText(verb),
|
|
418
|
-
labelXYWH: edgyVerbLabelXYWH(verb),
|
|
419
|
-
labelOffset: { distance: t ?? EDGY_RELATION_LABEL_DISTANCE },
|
|
420
|
-
};
|
|
421
|
-
});
|
|
422
|
-
return out;
|
|
423
|
-
}
|
|
424
|
-
const single = (el) => ({
|
|
425
|
-
a: el,
|
|
426
|
-
});
|
|
388
|
+
export { DYN_SCALE, dynToModel, edgyDynamicTemplate } from './dynamic.js';
|
|
427
389
|
const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
390
|
+
/** A hand-composed template — what is left once the artefacts are derived. */
|
|
428
391
|
function tpl(name, preview, elements) {
|
|
429
392
|
return {
|
|
430
393
|
name,
|
|
@@ -433,8 +396,6 @@ function tpl(name, preview, elements) {
|
|
|
433
396
|
content: makeTemplateSnapshot(elements, name),
|
|
434
397
|
};
|
|
435
398
|
}
|
|
436
|
-
/** Exported for direct insertion from the EDGY senior toolbar menu. */
|
|
437
|
-
export const edgyDynamicTemplate = tpl('EDGY dynamic', `<svg ${ATTRS}><circle cx="55" cy="34" r="18" fill="#00ea4e" opacity="0.9"/><circle cx="80" cy="34" r="18" fill="#034cee" opacity="0.9"/><circle cx="67" cy="54" r="18" fill="#ff0056" opacity="0.9"/><path d="M55 30 H80 M56 31 L66 52 M79 31 L69 52" stroke="#fff" stroke-width="2"/><rect x="51" y="26" width="8" height="8" fill="#fff"/><circle cx="80" cy="30" r="4" fill="#fff"/><path d="M62 49 h6 l3 3 -3 3 h-6 z" fill="#fff"/></svg>`, dynamic());
|
|
438
399
|
export const edgyTemplateCategory = {
|
|
439
400
|
name: 'EDGY',
|
|
440
401
|
templates: [
|
|
@@ -442,19 +403,14 @@ export const edgyTemplateCategory = {
|
|
|
442
403
|
tpl('Customer journey', `<svg ${ATTRS} fill="none"><path d="M20 24 H110 L122 40 L110 56 H20 Z" fill="#f3a3c0"/><rect x="26" y="30" width="22" height="20" fill="none" stroke="#fff"/><rect x="54" y="30" width="22" height="20" fill="none" stroke="#fff"/><rect x="82" y="30" width="22" height="20" fill="none" stroke="#fff"/></svg>`, journey()),
|
|
443
404
|
tpl('Service blueprint', `<svg ${ATTRS} fill="none"><rect x="8" y="14" width="119" height="16" fill="#fbd5e0"/><rect x="8" y="32" width="119" height="34" fill="#d4e9f8"/><rect x="20" y="18" width="22" height="9" fill="#f5246e" opacity="0.5"/><rect x="20" y="40" width="22" height="9" fill="#2f6ff0" opacity="0.4"/><rect x="60" y="40" width="22" height="9" fill="#2f6ff0" opacity="0.4"/></svg>`, blueprint()),
|
|
444
405
|
tpl('Organisation chart', `<svg ${ATTRS} fill="none"><rect x="52" y="12" width="32" height="14" rx="2" fill="#4fd0ea"/><rect x="14" y="38" width="32" height="14" rx="2" fill="#4fd0ea"/><rect x="52" y="38" width="32" height="14" rx="2" fill="#4fd0ea"/><rect x="90" y="38" width="32" height="14" rx="2" fill="#4fd0ea"/><path d="M68 26 V32 M30 32 H106 M30 32 V38 M68 32 V38 M106 32 V38" stroke="#262626"/></svg>`, orgChart()),
|
|
445
|
-
|
|
446
|
-
type: 'edgy',
|
|
447
|
-
cropToCircles: true,
|
|
448
|
-
role: EDGY_ROLE.facets,
|
|
449
|
-
xywh: `[0,0,${CROP_LABELED.w * 1.5},${CROP_LABELED.h * 1.5}]`,
|
|
450
|
-
})),
|
|
406
|
+
templateFromCommand(byId('edgy.addFacets'), `<svg ${ATTRS}><circle cx="55" cy="34" r="18" fill="#00ea4e" opacity="0.9"/><circle cx="80" cy="34" r="18" fill="#034cee" opacity="0.9"/><circle cx="67" cy="54" r="18" fill="#ff0056" opacity="0.9"/></svg>`, 'Facets diagram'),
|
|
451
407
|
edgyDynamicTemplate,
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
}),
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
408
|
+
// The blank board had no template at all until the palette was derived —
|
|
409
|
+
// the coverage test is what said so.
|
|
410
|
+
templateFromCommand(byId('edgy.addBoard'), `<svg ${ATTRS} fill="none"><rect x="8" y="9" width="119" height="62" rx="8" fill="#ffffff" stroke="#e0e0e0" stroke-width="2"/><path d="M52 30 H84 M53 31 L66 58 M83 31 L70 58" stroke="#262626" stroke-width="1.4"/><rect x="48" y="26" width="9" height="9" fill="#00ea4e"/><circle cx="84" cy="30" r="4.5" fill="#034cee"/><path d="M61 54 h8 l4 4 -4 4 h-8 z" fill="#ff0056"/></svg>`, 'EDGY board'),
|
|
411
|
+
templateFromCommand(byId('edgy.addPeople'), `<svg ${ATTRS} fill="#262626"><circle cx="67" cy="32" r="9" fill="none" stroke="#262626" stroke-width="2.4"/><path d="M50 60 a17 17 0 0 1 34 0" fill="none" stroke="#262626" stroke-width="2.4"/></svg>`),
|
|
412
|
+
templateFromCommand(byId('edgy.addOutcome'), `<svg ${ATTRS} fill="none"><rect x="20" y="24" width="95" height="34" rx="6" stroke="#262626" stroke-width="2"/></svg>`),
|
|
413
|
+
templateFromCommand(byId('edgy.addObject'), `<svg ${ATTRS} fill="none"><rect x="20" y="24" width="95" height="34" stroke="#262626" stroke-width="2"/></svg>`),
|
|
414
|
+
templateFromCommand(byId('edgy.addActivity'), `<svg ${ATTRS} fill="none"><path d="M20 24 H98 L116 41 H116 L98 58 H20 Z" stroke="#262626" stroke-width="2" stroke-linejoin="round"/></svg>`),
|
|
459
415
|
],
|
|
460
416
|
};
|
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.
|
|
4
|
+
"version": "0.38.2",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"author": "lajola",
|
|
@@ -31,8 +31,8 @@
|
|
|
31
31
|
"dist"
|
|
32
32
|
],
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@formicoidea/labre-core": "0.
|
|
35
|
-
"@formicoidea/labre-ddd-shared": "0.
|
|
34
|
+
"@formicoidea/labre-core": "0.38.2",
|
|
35
|
+
"@formicoidea/labre-ddd-shared": "0.38.2",
|
|
36
36
|
"@preact/signals-core": "^1.8.0",
|
|
37
37
|
"lit": "^3.2.0",
|
|
38
38
|
"yjs": "^13.6.21"
|