@formicoidea/labre-framework-edgy 0.23.0 → 0.23.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/dist/consts.d.ts +47 -0
  2. package/{src/consts.ts → dist/consts.js} +46 -50
  3. package/dist/descriptor.d.ts +7 -0
  4. package/{src/descriptor.ts → dist/descriptor.js} +1 -1
  5. package/dist/effects.d.ts +10 -0
  6. package/dist/effects.js +7 -0
  7. package/dist/element-renderer.d.ts +17 -0
  8. package/dist/element-renderer.js +177 -0
  9. package/dist/element-view.d.ts +19 -0
  10. package/dist/element-view.js +123 -0
  11. package/dist/index.d.ts +2 -0
  12. package/dist/index.js +2 -0
  13. package/dist/label-layout.d.ts +46 -0
  14. package/dist/label-layout.js +69 -0
  15. package/dist/node/consts.d.ts +34 -0
  16. package/{src/node/consts.ts → dist/node/consts.js} +19 -26
  17. package/dist/node/node-renderer.d.ts +14 -0
  18. package/dist/node/node-renderer.js +40 -0
  19. package/dist/node/node-view.d.ts +16 -0
  20. package/{src/node/node-view.ts → dist/node/node-view.js} +15 -21
  21. package/dist/templates/index.d.ts +3 -0
  22. package/dist/templates/index.js +210 -0
  23. package/dist/toolbar/config.d.ts +20 -0
  24. package/dist/toolbar/config.js +71 -0
  25. package/dist/toolbar/edgy-menu.d.ts +31 -0
  26. package/dist/toolbar/edgy-menu.js +195 -0
  27. package/dist/toolbar/edgy-senior-button.d.ts +16 -0
  28. package/{src/toolbar/edgy-senior-button.ts → dist/toolbar/edgy-senior-button.js} +33 -38
  29. package/dist/toolbar/icons.d.ts +13 -0
  30. package/{src/toolbar/icons.ts → dist/toolbar/icons.js} +20 -25
  31. package/dist/toolbar/node-config.d.ts +2 -0
  32. package/dist/toolbar/node-config.js +162 -0
  33. package/dist/toolbar/senior-tool.d.ts +2 -0
  34. package/{src/toolbar/senior-tool.ts → dist/toolbar/senior-tool.js} +5 -5
  35. package/dist/view.d.ts +7 -0
  36. package/dist/view.js +36 -0
  37. package/package.json +15 -6
  38. package/src/effects.ts +0 -14
  39. package/src/element-renderer.ts +0 -208
  40. package/src/element-view.ts +0 -145
  41. package/src/index.ts +0 -1
  42. package/src/label-layout.ts +0 -105
  43. package/src/node/node-renderer.ts +0 -64
  44. package/src/templates/index.ts +0 -254
  45. package/src/toolbar/config.ts +0 -96
  46. package/src/toolbar/edgy-menu.ts +0 -242
  47. package/src/toolbar/node-config.ts +0 -202
  48. package/src/view.ts +0 -39
@@ -0,0 +1,34 @@
1
+ import type { EdgyNodeKind } from '@formicoidea/labre-core/model';
2
+ /**
3
+ * Visual constants for the four EDGY base-element nodes. Each node is a NATIVE
4
+ * shape (ShapeElementModel-derived) so its stroke / fill / inner text are
5
+ * editable via the shape toolbar; these are just the pre-formatted defaults at
6
+ * creation.
7
+ */
8
+ export declare const NODE_FILL = "#ffffff";
9
+ export declare const NODE_STROKE = "#262626";
10
+ export declare const NODE_STROKE_WIDTH = 2;
11
+ /** Outcome corner radius (absolute px — a lightly rounded rectangle). */
12
+ export declare const OUTCOME_RADIUS = 6;
13
+ /** Right-pointing chevron, as normalized polygon vertices (Activity). */
14
+ export declare const ACTIVITY_VERTICES: number[][];
15
+ /** Default node sizes (model units) per kind. */
16
+ export declare const NODE_SIZE: Record<EdgyNodeKind, {
17
+ w: number;
18
+ h: number;
19
+ }>;
20
+ /** Inner-text + label font (matches the diagram labels). */
21
+ export declare const INNER_FONT_SIZE = 20;
22
+ export declare const LABEL_FONT_SIZE = 18;
23
+ export declare const LABEL_GAP = 8;
24
+ /** Default inner text / label per kind. */
25
+ export declare const NODE_LABEL: Record<EdgyNodeKind, string>;
26
+ /**
27
+ * Person glyph for `kind: 'people'`, taken from the official `Icon-People.svg`
28
+ * (viewBox 0 0 32 32). Two filled outline rings (head + shoulders) — filling
29
+ * them yields the line-art person. Drawn in the node stroke color, scaled to
30
+ * the node and centred.
31
+ */
32
+ export declare const PERSON_GLYPH_VIEWBOX = 32;
33
+ export declare const PERSON_GLYPH_PATHS: string[];
34
+ //# sourceMappingURL=consts.d.ts.map
@@ -1,5 +1,3 @@
1
- import type { EdgyNodeKind } from '@formicoidea/labre-core/model';
2
-
3
1
  /**
4
2
  * Visual constants for the four EDGY base-element nodes. Each node is a NATIVE
5
3
  * shape (ShapeElementModel-derived) so its stroke / fill / inner text are
@@ -9,40 +7,34 @@ import type { EdgyNodeKind } from '@formicoidea/labre-core/model';
9
7
  export const NODE_FILL = '#ffffff';
10
8
  export const NODE_STROKE = '#262626';
11
9
  export const NODE_STROKE_WIDTH = 2;
12
-
13
10
  /** Outcome corner radius (absolute px — a lightly rounded rectangle). */
14
11
  export const OUTCOME_RADIUS = 6;
15
-
16
12
  /** Right-pointing chevron, as normalized polygon vertices (Activity). */
17
- export const ACTIVITY_VERTICES: number[][] = [
18
- [0, 0],
19
- [0.8, 0],
20
- [1, 0.5],
21
- [0.8, 1],
22
- [0, 1],
13
+ export const ACTIVITY_VERTICES = [
14
+ [0, 0],
15
+ [0.8, 0],
16
+ [1, 0.5],
17
+ [0.8, 1],
18
+ [0, 1],
23
19
  ];
24
-
25
20
  /** Default node sizes (model units) per kind. */
26
- export const NODE_SIZE: Record<EdgyNodeKind, { w: number; h: number }> = {
27
- people: { w: 64, h: 64 },
28
- outcome: { w: 130, h: 80 },
29
- object: { w: 130, h: 80 },
30
- activity: { w: 140, h: 80 },
21
+ export const NODE_SIZE = {
22
+ people: { w: 64, h: 64 },
23
+ outcome: { w: 130, h: 80 },
24
+ object: { w: 130, h: 80 },
25
+ activity: { w: 140, h: 80 },
31
26
  };
32
-
33
27
  /** Inner-text + label font (matches the diagram labels). */
34
28
  export const INNER_FONT_SIZE = 20;
35
29
  export const LABEL_FONT_SIZE = 18;
36
30
  export const LABEL_GAP = 8;
37
-
38
31
  /** Default inner text / label per kind. */
39
- export const NODE_LABEL: Record<EdgyNodeKind, string> = {
40
- people: 'People',
41
- outcome: 'Outcome',
42
- object: 'Object',
43
- activity: 'Activity',
32
+ export const NODE_LABEL = {
33
+ people: 'People',
34
+ outcome: 'Outcome',
35
+ object: 'Object',
36
+ activity: 'Activity',
44
37
  };
45
-
46
38
  /**
47
39
  * Person glyph for `kind: 'people'`, taken from the official `Icon-People.svg`
48
40
  * (viewBox 0 0 32 32). Two filled outline rings (head + shoulders) — filling
@@ -51,6 +43,7 @@ export const NODE_LABEL: Record<EdgyNodeKind, string> = {
51
43
  */
52
44
  export const PERSON_GLYPH_VIEWBOX = 32;
53
45
  export const PERSON_GLYPH_PATHS = [
54
- 'm16,19c-3.308,0-6-2.692-6-6v-4c0-3.308,2.692-6,6-6s6,2.692,6,6v4c0,3.308-2.692,6-6,6Zm0-14c-2.206,0-4,1.794-4,4v4c0,2.206,1.794,4,4,4s4-1.794,4-4v-4c0-2.206-1.794-4-4-4Z',
55
- 'm29,30H3v-3.5c0-3.308,2.692-6,6-6h14c3.308,0,6,2.692,6,6v3.5Zm-24-2h22v-1.5c0-2.206-1.794-4-4-4h-14c-2.206,0-4,1.794-4,4v1.5Z',
46
+ 'm16,19c-3.308,0-6-2.692-6-6v-4c0-3.308,2.692-6,6-6s6,2.692,6,6v4c0,3.308-2.692,6-6,6Zm0-14c-2.206,0-4,1.794-4,4v4c0,2.206,1.794,4,4,4s4-1.794,4-4v-4c0-2.206-1.794-4-4-4Z',
47
+ 'm29,30H3v-3.5c0-3.308,2.692-6,6-6h14c3.308,0,6,2.692,6,6v3.5Zm-24-2h22v-1.5c0-2.206-1.794-4-4-4h-14c-2.206,0-4,1.794-4,4v1.5Z',
56
48
  ];
49
+ //# sourceMappingURL=consts.js.map
@@ -0,0 +1,14 @@
1
+ import { type ElementRenderer } from '@formicoidea/labre-core/blocks/surface';
2
+ import { type EdgyNodeElementModel } from '@formicoidea/labre-core/model';
3
+ /**
4
+ * Renderer for an EDGY base-element node. The shape body (rounded rect / rect /
5
+ * chevron polygon / ellipse) is drawn by REUSING the native shape renderer — so
6
+ * stroke width, colors, inner text and theme behave exactly like a native shape.
7
+ * Only `people` is decorated: an inscribed person glyph (the official
8
+ * `Icon-People` paths) drawn on top in the node's (editable) stroke color.
9
+ */
10
+ export declare const edgyNode: ElementRenderer<EdgyNodeElementModel>;
11
+ export declare const EdgyNodeRendererExtension: import("@formicoidea/labre-core/store").ExtensionType & {
12
+ identifier: import("@formicoidea/labre-core/global/di").ServiceIdentifier<ElementRenderer<EdgyNodeElementModel>>;
13
+ };
14
+ //# sourceMappingURL=node-renderer.d.ts.map
@@ -0,0 +1,40 @@
1
+ import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
2
+ import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
3
+ import { DefaultTheme } from '@formicoidea/labre-core/model';
4
+ import { PERSON_GLYPH_PATHS, PERSON_GLYPH_VIEWBOX } from './consts';
5
+ /**
6
+ * Renderer for an EDGY base-element node. The shape body (rounded rect / rect /
7
+ * chevron polygon / ellipse) is drawn by REUSING the native shape renderer — so
8
+ * stroke width, colors, inner text and theme behave exactly like a native shape.
9
+ * Only `people` is decorated: an inscribed person glyph (the official
10
+ * `Icon-People` paths) drawn on top in the node's (editable) stroke color.
11
+ */
12
+ export const edgyNode = (model, ctx, matrix, renderer, rc, bound) => {
13
+ const [, , w, h] = model.deserializedXYWH;
14
+ const cx = w / 2;
15
+ const cy = h / 2;
16
+ // Capture the element-local transform BEFORE the shape renderer mutates the
17
+ // matrix, so the glyph can be drawn in the same space afterwards.
18
+ const glyphMatrix = DOMMatrix.fromMatrix(matrix)
19
+ .translateSelf(cx, cy)
20
+ .rotateSelf(model.rotate)
21
+ .translateSelf(-cx, -cy);
22
+ // Native shape (fill / stroke / inner text / theme handled natively).
23
+ shapeRenderer(model, ctx, matrix, renderer, rc, bound);
24
+ if (model.kind !== 'people')
25
+ return;
26
+ const color = renderer.getColorValue(model.strokeColor, DefaultTheme.shapeStrokeColor, true);
27
+ // ── Person glyph (Icon-People), scaled to the node and centred ──────
28
+ const target = Math.min(w, h) * 0.8;
29
+ const s = target / PERSON_GLYPH_VIEWBOX;
30
+ ctx.setTransform(glyphMatrix);
31
+ ctx.translate(cx, cy);
32
+ ctx.scale(s, s);
33
+ ctx.translate(-PERSON_GLYPH_VIEWBOX / 2, -PERSON_GLYPH_VIEWBOX / 2);
34
+ ctx.fillStyle = color;
35
+ for (const d of PERSON_GLYPH_PATHS) {
36
+ ctx.fill(new Path2D(d));
37
+ }
38
+ };
39
+ export const EdgyNodeRendererExtension = ElementRendererExtension('edgyNode', edgyNode);
40
+ //# sourceMappingURL=node-renderer.js.map
@@ -0,0 +1,16 @@
1
+ import { type EdgyNodeElementModel } from '@formicoidea/labre-core/model';
2
+ import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
3
+ /**
4
+ * View for an EDGY base-element node. Registering it ensures `gfx.view.get(model)`
5
+ * returns a view (required so move / select / connector interactions work).
6
+ *
7
+ * EDGY nodes are native shapes, so we reuse the shape inner-text editor: a
8
+ * double-click mounts the editable text overlay (`mountShapeTextEditor`), exactly
9
+ * like a native shape. We deliberately do NOT inherit the polygon vertex-editing
10
+ * overlay (the Activity chevron should keep its shape).
11
+ */
12
+ export declare class EdgyNodeView extends GfxElementModelView<EdgyNodeElementModel> {
13
+ static type: string;
14
+ onCreated(): void;
15
+ }
16
+ //# sourceMappingURL=node-view.d.ts.map
@@ -1,10 +1,6 @@
1
1
  import { mountShapeTextEditor } from '@formicoidea/labre-core/gfx/shape';
2
- import {
3
- type EdgyNodeElementModel,
4
- ShapeElementModel,
5
- } from '@formicoidea/labre-core/model';
2
+ import { ShapeElementModel, } from '@formicoidea/labre-core/model';
6
3
  import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
7
-
8
4
  /**
9
5
  * View for an EDGY base-element node. Registering it ensures `gfx.view.get(model)`
10
6
  * returns a view (required so move / select / connector interactions work).
@@ -14,20 +10,18 @@ import { GfxElementModelView } from '@formicoidea/labre-core/std/gfx';
14
10
  * like a native shape. We deliberately do NOT inherit the polygon vertex-editing
15
11
  * overlay (the Activity chevron should keep its shape).
16
12
  */
17
- export class EdgyNodeView extends GfxElementModelView<EdgyNodeElementModel> {
18
- static override type: string = 'edgyNode';
19
-
20
- override onCreated(): void {
21
- super.onCreated();
22
- this.on('dblclick', () => {
23
- const edgeless = this.std.view.getBlock(this.std.store.root!.id);
24
- if (
25
- edgeless &&
26
- !this.model.isLocked() &&
27
- this.model instanceof ShapeElementModel
28
- ) {
29
- mountShapeTextEditor(this.model, edgeless);
30
- }
31
- });
32
- }
13
+ export class EdgyNodeView extends GfxElementModelView {
14
+ static { this.type = 'edgyNode'; }
15
+ onCreated() {
16
+ super.onCreated();
17
+ this.on('dblclick', () => {
18
+ const edgeless = this.std.view.getBlock(this.std.store.root.id);
19
+ if (edgeless &&
20
+ !this.model.isLocked() &&
21
+ this.model instanceof ShapeElementModel) {
22
+ mountShapeTextEditor(this.model, edgeless);
23
+ }
24
+ });
25
+ }
33
26
  }
27
+ //# sourceMappingURL=node-view.js.map
@@ -0,0 +1,3 @@
1
+ import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
2
+ export declare const edgyTemplateCategory: TemplateCategory;
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,210 @@
1
+ import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
2
+ import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
3
+ import { REF_H, REF_W } from '../consts';
4
+ import { ACTIVITY_VERTICES, INNER_FONT_SIZE, NODE_FILL, NODE_STROKE, NODE_STROKE_WIDTH, OUTCOME_RADIUS, } from '../node/consts';
5
+ // EDGY facet palette (header / pale sub-card).
6
+ const C = {
7
+ identity: ['#1ec873', '#9fe6c2'],
8
+ organisation: ['#4fd0ea', '#c2eef8'],
9
+ architecture: ['#2f6ff0', '#b3c8f7'],
10
+ product: ['#cf8cff', '#e7ccff'],
11
+ experience: ['#f5246e', '#ffc0d4'],
12
+ brand: ['#eeba51', '#f7e1ad'],
13
+ };
14
+ const JOURNEY_PINK = '#f3a3c0';
15
+ // ── element helpers ───────────────────────────────────────────────────
16
+ function rect(x, y, w, h, opts = {}) {
17
+ const el = {
18
+ type: 'shape',
19
+ shapeType: 'rect',
20
+ filled: true,
21
+ fillColor: opts.fill ?? '#ffffff',
22
+ strokeColor: opts.stroke ?? NODE_STROKE,
23
+ strokeWidth: opts.sw ?? NODE_STROKE_WIDTH,
24
+ shapeStyle: ShapeStyle.General,
25
+ roughness: 0,
26
+ radius: opts.radius ?? 0,
27
+ xywh: `[${x},${y},${w},${h}]`,
28
+ };
29
+ if (opts.text != null) {
30
+ el.text = surfaceText(opts.text);
31
+ el.color = opts.textColor ?? NODE_STROKE;
32
+ el.fontFamily = FontFamily.Inter;
33
+ el.fontSize = opts.fontSize ?? 16;
34
+ el.textAlign = TextAlign.Center;
35
+ }
36
+ return el;
37
+ }
38
+ /** An EDGY node (kind drives the native shape): outcome/object box, people, activity chevron. */
39
+ function enode(kind, x, y, w, h, opts = {}) {
40
+ const el = {
41
+ type: 'edgyNode',
42
+ kind,
43
+ filled: true,
44
+ fillColor: opts.fill ?? NODE_FILL,
45
+ strokeColor: NODE_STROKE,
46
+ strokeWidth: kind === 'people' ? 0 : NODE_STROKE_WIDTH,
47
+ shapeStyle: ShapeStyle.General,
48
+ roughness: 0,
49
+ shapeType: kind === 'people' ? 'ellipse' : kind === 'activity' ? 'polygon' : 'rect',
50
+ radius: kind === 'outcome' ? OUTCOME_RADIUS : 0,
51
+ xywh: `[${x},${y},${w},${h}]`,
52
+ };
53
+ if (kind === 'activity')
54
+ el.vertices = ACTIVITY_VERTICES;
55
+ if (opts.text != null) {
56
+ el.text = surfaceText(opts.text);
57
+ el.color = opts.textColor ?? NODE_STROKE;
58
+ el.fontFamily = FontFamily.Inter;
59
+ el.fontSize = opts.fontSize ?? INNER_FONT_SIZE;
60
+ el.textAlign = TextAlign.Center;
61
+ }
62
+ return el;
63
+ }
64
+ function label(x, y, w, h, str, opts = {}) {
65
+ return {
66
+ type: 'text',
67
+ text: surfaceText(str),
68
+ color: opts.color ?? NODE_STROKE,
69
+ fontFamily: FontFamily.Inter,
70
+ fontSize: opts.fontSize ?? 16,
71
+ textAlign: opts.align ?? TextAlign.Center,
72
+ xywh: `[${x},${y},${w},${h}]`,
73
+ };
74
+ }
75
+ function line(x1, y1, x2, y2, opts = {}) {
76
+ return {
77
+ type: 'connector',
78
+ mode: ConnectorMode.Orthogonal,
79
+ stroke: NODE_STROKE,
80
+ strokeWidth: opts.sw ?? 2,
81
+ strokeStyle: opts.dash ? StrokeStyle.Dash : StrokeStyle.Solid,
82
+ frontEndpointStyle: PointStyle.None,
83
+ rearEndpointStyle: opts.arrow ? PointStyle.Triangle : PointStyle.None,
84
+ source: { position: [x1, y1] },
85
+ target: { position: [x2, y2] },
86
+ };
87
+ }
88
+ // ── Facets overview (six facets, sub-cards, facets/intersections) ─────
89
+ function facetsOverview() {
90
+ const out = {};
91
+ const tall = (key, x, name, cards) => {
92
+ const [hdr, sub] = C[key];
93
+ out[`${key}F`] = rect(x, 140, 192, 460, { fill: hdr, stroke: hdr, radius: 16 });
94
+ out[`${key}N`] = label(x, 168, 192, 28, name, { color: '#ffffff', fontSize: 18 });
95
+ out[`${key}1`] = rect(x + 16, 208, 160, 80, { fill: sub, stroke: sub, radius: 6, text: cards[0], fontSize: 16 });
96
+ out[`${key}2`] = rect(x + 16, 300, 160, 80, { fill: sub, stroke: sub, radius: 6, text: cards[1], fontSize: 16 });
97
+ out[`${key}3`] = enode('activity', x + 16, 392, 160, 84, { fill: sub, text: cards[2], fontSize: 16 });
98
+ };
99
+ const low = (key, x, name) => {
100
+ const [hdr, sub] = C[key];
101
+ out[`${key}F`] = rect(x, 300, 192, 424, { fill: hdr, stroke: hdr, radius: 16 });
102
+ out[`${key}1`] = rect(x + 16, 360, 160, 92, { fill: sub, stroke: sub, radius: 6, text: name, fontSize: 16 });
103
+ out[`${key}N`] = label(x, 686, 192, 28, name, { color: '#ffffff', fontSize: 18 });
104
+ };
105
+ // lower facets first (drawn behind the tall ones at the overlaps)
106
+ low('organisation', 240, 'Organisation');
107
+ low('product', 664, 'Product');
108
+ low('brand', 1088, 'Brand');
109
+ tall('identity', 40, 'Identity', ['Purpose', 'Content', 'Story']);
110
+ tall('architecture', 464, 'Architecture', ['Capability', 'Asset', 'Process']);
111
+ tall('experience', 888, 'Experience', ['Task', 'Channel', 'Journey']);
112
+ out.fLabel = label(464, 80, 192, 28, 'Facets', { fontSize: 18 });
113
+ out.fBar = line(136, 120, 984, 120);
114
+ out.iLabel = label(664, 740, 192, 28, 'Intersections', { fontSize: 18 });
115
+ out.iBar = line(336, 728, 1184, 728);
116
+ return out;
117
+ }
118
+ // ── Customer journey ──────────────────────────────────────────────────
119
+ function journey() {
120
+ const step = (i, x) => enode('activity', x, 132, 224, 116, { fill: JOURNEY_PINK, text: `Journey step ${i}`, textColor: '#ffffff', fontSize: 18 });
121
+ const ch = (x, n) => enode('object', x, 392, 184, 96, { fill: JOURNEY_PINK, text: `Channel ${n}`, fontSize: 16 });
122
+ const tk = (x, n) => enode('object', x, 540, 168, 92, { fill: JOURNEY_PINK, text: `Task ${n}`, fontSize: 16 });
123
+ return {
124
+ cust: enode('people', 40, 120, 64, 64, { fill: '#ffffff' }),
125
+ custL: label(20, 192, 104, 24, 'Customer', { fontSize: 14 }),
126
+ band: { type: 'shape', shapeType: 'polygon', vertices: [[0, 0], [0.86, 0], [1, 0.5], [0.86, 1], [0, 1]], filled: true, fillColor: JOURNEY_PINK, strokeColor: JOURNEY_PINK, strokeWidth: 1, shapeStyle: ShapeStyle.General, roughness: 0, xywh: '[150,108,860,164]' },
127
+ bandL: label(170, 116, 120, 24, 'Journey', { color: '#ffffff', fontSize: 16, align: TextAlign.Left }),
128
+ s1: step(1, 240),
129
+ s2: step(2, 500),
130
+ s3: step(3, 760),
131
+ rightTask: enode('object', 1060, 132, 168, 92, { text: 'Task', fontSize: 16 }),
132
+ c1: ch(258, 'X'), c2: ch(518, 'Y'), c3: ch(778, 'Z'),
133
+ t1: tk(266, 'A'), t2: tk(526, 'B'), t3: tk(786, 'C'),
134
+ trav1: label(258, 350, 184, 20, 'traverses', { fontSize: 13, color: '#5f6368' }),
135
+ trav2: label(518, 350, 184, 20, 'traverses', { fontSize: 13, color: '#5f6368' }),
136
+ trav3: label(778, 350, 184, 20, 'traverses', { fontSize: 13, color: '#5f6368' }),
137
+ use1: label(258, 504, 184, 20, 'uses', { fontSize: 13, color: '#5f6368' }),
138
+ use2: label(518, 504, 184, 20, 'uses', { fontSize: 13, color: '#5f6368' }),
139
+ use3: label(778, 504, 184, 20, 'uses', { fontSize: 13, color: '#5f6368' }),
140
+ l1: line(350, 248, 350, 392), l2: line(610, 248, 610, 392), l3: line(870, 248, 870, 392),
141
+ l4: line(350, 488, 350, 540), l5: line(610, 488, 610, 540), l6: line(870, 488, 870, 540),
142
+ };
143
+ }
144
+ // ── Service blueprint (six swimlanes) ─────────────────────────────────
145
+ function blueprint() {
146
+ const lanes = ['Physical Evidence', 'Customer Actions', 'On-stage Actions', 'Back-stage Actions', 'Support Processes', 'Support Systems'];
147
+ const laneFill = ['#fdeef2', '#fbd5e0', '#dff0fb', '#d4e9f8', '#d4e9f8', '#d4e9f8'];
148
+ const out = {};
149
+ lanes.forEach((name, i) => {
150
+ const y = 40 + i * 150;
151
+ out[`lane${i}`] = rect(36, y, 1280, 150, { fill: laneFill[i], stroke: laneFill[i], sw: 0 });
152
+ out[`laneL${i}`] = label(52, y + 12, 260, 22, name, { fontSize: 15, align: TextAlign.Left });
153
+ });
154
+ const chev = (x, y, fill, t) => enode('activity', x, y, 200, 60, { fill, text: t, fontSize: 14 });
155
+ const box = (x, y, fill, t) => enode('object', x, y, 200, 60, { fill, text: t, fontSize: 14 });
156
+ const P = C.experience[1], B = C.architecture[1];
157
+ Object.assign(out, {
158
+ af: rect(280, 64, 220, 70, { text: 'Admission Form' }),
159
+ cf: rect(1010, 64, 220, 70, { text: 'Confirmation' }),
160
+ sf: chev(280, 214, P, 'Send Form'), fs: chev(600, 214, P, 'Follow status'), rd: chev(1010, 214, P, 'Receive decision'),
161
+ fh: chev(280, 364, B, 'Form handling'), cs: chev(600, 364, B, 'Customer support'), ct: chev(1010, 364, B, 'Contact'),
162
+ csv: chev(280, 514, B, 'Customer service'), cfu: box(1010, 514, B, 'Customer follow-up'),
163
+ pf: chev(600, 664, B, 'Process Form'), dec: chev(900, 664, B, 'Decision [Accept|Reject]'),
164
+ crm: box(280, 814, B, 'CRM Application'), cms: box(600, 814, B, 'Case Management'), pay: box(900, 814, B, 'Payments System'),
165
+ a1: line(380, 134, 380, 214, { arrow: true }),
166
+ a2: line(480, 244, 600, 244, { arrow: true }),
167
+ a3: line(800, 244, 1010, 244, { arrow: true }),
168
+ a4: line(320, 274, 320, 364, { arrow: true }),
169
+ a5: line(320, 424, 320, 514, { arrow: true }),
170
+ a6: line(700, 724, 900, 694, { arrow: true }),
171
+ a7: line(380, 874, 600, 844, { arrow: true }),
172
+ a8: line(800, 844, 900, 844, { arrow: true }),
173
+ });
174
+ return out;
175
+ }
176
+ // ── Organisation chart ────────────────────────────────────────────────
177
+ function orgChart() {
178
+ const cyan = C.organisation[0];
179
+ const u = (x, y, w, t) => enode('object', x, y, w, 64, { fill: cyan, text: t, fontSize: 16 });
180
+ return {
181
+ org: u(420, 40, 180, 'Organisation'),
182
+ a: u(120, 200, 200, 'Business Unit A'), b: u(410, 200, 200, 'Business Unit B'), c: u(700, 200, 200, 'Business Unit C'),
183
+ a1: u(60, 360, 170, 'Group A-1'), a2: u(260, 360, 170, 'Group A-2'), c1: u(715, 360, 170, 'Group C-1'),
184
+ e1: line(510, 104, 510, 150), e2: line(220, 150, 800, 150),
185
+ e3: line(220, 150, 220, 200), e4: line(510, 150, 510, 200), e5: line(800, 150, 800, 200),
186
+ e6: line(220, 264, 220, 320), e7: line(145, 320, 345, 320),
187
+ e8: line(145, 320, 145, 360), e9: line(345, 320, 345, 360),
188
+ e10: line(800, 264, 800, 360),
189
+ };
190
+ }
191
+ const single = (el) => ({ a: el });
192
+ const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
193
+ function tpl(name, preview, elements) {
194
+ return { name, type: 'template', preview, content: makeTemplateSnapshot(elements, name) };
195
+ }
196
+ export const edgyTemplateCategory = {
197
+ name: 'EDGY',
198
+ templates: [
199
+ tpl('Facets overview', `<svg ${ATTRS} fill="none"><rect x="6" y="20" width="18" height="46" rx="3" fill="#1ec873"/><rect x="46" y="20" width="18" height="46" rx="3" fill="#2f6ff0"/><rect x="86" y="20" width="18" height="46" rx="3" fill="#f5246e"/><rect x="26" y="30" width="18" height="40" rx="3" fill="#4fd0ea"/><rect x="66" y="30" width="18" height="40" rx="3" fill="#cf8cff"/><rect x="106" y="30" width="18" height="40" rx="3" fill="#eeba51"/></svg>`, facetsOverview()),
200
+ 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()),
201
+ 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()),
202
+ 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()),
203
+ tpl('Facets diagram', `<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>`, single({ type: 'edgy', xywh: `[0,0,${REF_W * 1.5},${REF_H * 1.5}]` })),
204
+ tpl('People', `<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>`, { n: enode('people', 0, 0, 64, 64), l: label(-28, 70, 120, 24, 'People', { fontSize: 16 }) }),
205
+ tpl('Outcome', `<svg ${ATTRS} fill="none"><rect x="20" y="24" width="95" height="34" rx="6" stroke="#262626" stroke-width="2"/></svg>`, single(enode('outcome', 0, 0, 130, 80, { text: 'Outcome' }))),
206
+ tpl('Object', `<svg ${ATTRS} fill="none"><rect x="20" y="24" width="95" height="34" stroke="#262626" stroke-width="2"/></svg>`, single(enode('object', 0, 0, 130, 80, { text: 'Object' }))),
207
+ tpl('Activity', `<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>`, single(enode('activity', 0, 0, 140, 80, { text: 'Activity' }))),
208
+ ],
209
+ };
210
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,20 @@
1
+ import { type ToolbarContext } from '@formicoidea/labre-core/shared/services';
2
+ import { type TemplateResult } from 'lit';
3
+ export declare const edgyToolbarConfig: {
4
+ readonly actions: [{
5
+ id: string;
6
+ tooltip: string;
7
+ icon: TemplateResult;
8
+ active(ctx: ToolbarContext): boolean;
9
+ run(ctx: ToolbarContext): void;
10
+ }, {
11
+ id: string;
12
+ tooltip: string;
13
+ icon: TemplateResult;
14
+ active(ctx: ToolbarContext): boolean;
15
+ run(ctx: ToolbarContext): void;
16
+ }];
17
+ readonly when: (ctx: ToolbarContext) => boolean;
18
+ };
19
+ export declare const edgyToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
20
+ //# sourceMappingURL=config.d.ts.map
@@ -0,0 +1,71 @@
1
+ import { EdgelessCRUDIdentifier } from '@formicoidea/labre-core/blocks/surface';
2
+ import { EdgyFacetsElementModel } from '@formicoidea/labre-core/model';
3
+ import { ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
4
+ import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
5
+ import { html } from 'lit';
6
+ const ResizeIcon = html `<svg
7
+ width="24"
8
+ height="24"
9
+ viewBox="0 0 24 24"
10
+ fill="none"
11
+ stroke="currentColor"
12
+ stroke-width="1.6"
13
+ stroke-linecap="round"
14
+ stroke-linejoin="round"
15
+ >
16
+ <path d="M9 5H5v4M15 19h4v-4" />
17
+ <path d="M5 5l6 6M19 19l-6-6" />
18
+ </svg>`;
19
+ /** Tag — show / hide the facet name labels. */
20
+ const LabelsIcon = html `<svg
21
+ width="24"
22
+ height="24"
23
+ viewBox="0 0 24 24"
24
+ fill="none"
25
+ stroke="currentColor"
26
+ stroke-width="1.6"
27
+ stroke-linecap="round"
28
+ stroke-linejoin="round"
29
+ >
30
+ <path d="M3 8.5l6-4.5 12 0 0 16-12 0-6-4.5z" />
31
+ <circle cx="8" cy="12" r="1.4" />
32
+ </svg>`;
33
+ /**
34
+ * Build a toolbar toggle that flips a boolean flag on every selected facets
35
+ * diagram: `active` reflects the current state, `run` flips it (with an undo
36
+ * checkpoint).
37
+ */
38
+ function booleanToggle(id, tooltip, icon, prop) {
39
+ return {
40
+ id,
41
+ tooltip,
42
+ icon,
43
+ active(ctx) {
44
+ const models = ctx.getSurfaceModelsByType(EdgyFacetsElementModel);
45
+ return models.length > 0 && models.every(model => model[prop]);
46
+ },
47
+ run(ctx) {
48
+ const models = ctx.getSurfaceModelsByType(EdgyFacetsElementModel);
49
+ if (!models.length)
50
+ return;
51
+ const enable = !models.every(model => model[prop]);
52
+ ctx.std.store.captureSync();
53
+ const crud = ctx.std.get(EdgelessCRUDIdentifier);
54
+ for (const model of models) {
55
+ crud.updateElement(model.id, { [prop]: enable });
56
+ }
57
+ },
58
+ };
59
+ }
60
+ export const edgyToolbarConfig = {
61
+ actions: [
62
+ booleanToggle('a.toggle-resize', 'Enable / lock resizing', ResizeIcon, 'resizeEnabled'),
63
+ booleanToggle('b.toggle-labels', 'Show / hide facet labels', LabelsIcon, 'showLabels'),
64
+ ],
65
+ when: ctx => ctx.getSurfaceModelsByType(EdgyFacetsElementModel).length > 0,
66
+ };
67
+ export const edgyToolbarExtension = ToolbarModuleExtension({
68
+ id: BlockFlavourIdentifier('affine:surface:edgy'),
69
+ config: edgyToolbarConfig,
70
+ });
71
+ //# sourceMappingURL=config.js.map
@@ -0,0 +1,31 @@
1
+ import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
2
+ import { LitElement } from 'lit';
3
+ declare const EdgelessEdgyMenu_base: typeof LitElement & import("@formicoidea/labre-core/global/utils").Constructor<import("@formicoidea/labre-core/widgets/edgeless-toolbar").EdgelessToolbarToolClass>;
4
+ /**
5
+ * The popover above the toolbar for the EDGY toolbox. Items create the facets
6
+ * diagram (the on-click Venn) and the four base-element prefab shapes — all
7
+ * native shapes so they stay editable.
8
+ */
9
+ export declare class EdgelessEdgyMenu extends EdgelessEdgyMenu_base {
10
+ static styles: import("lit").CSSResult;
11
+ type: typeof EmptyTool;
12
+ /** Create the Enterprise Design Facets diagram centred on the viewport. */
13
+ private _createFacets;
14
+ /** Add a native free-text label (Inter), used for the People node. */
15
+ private _addLabel;
16
+ private _group;
17
+ /** Shared props for an EDGY node shape. */
18
+ private _baseShapeProps;
19
+ /** Create a box base element (outcome / object / activity) with inner text. */
20
+ private _createBox;
21
+ /**
22
+ * Create the People base element: an (invisible) ellipse decorated with the
23
+ * person glyph by the renderer, plus a native text label below, grouped.
24
+ */
25
+ private _createPeople;
26
+ private _finish;
27
+ private _track;
28
+ render(): import("lit-html").TemplateResult<1>;
29
+ }
30
+ export {};
31
+ //# sourceMappingURL=edgy-menu.d.ts.map