@formicoidea/labre-framework-edgy 0.39.3 → 0.41.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
@@ -7,6 +7,7 @@ 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 { edgyElementLabel, edgyElementLabelKey } from './metamodel.js';
10
11
  import { LABEL_GAP, NODE_LABEL, nodeLabelKey, NODE_SIZE, NODE_STROKE, } from './node/consts.js';
11
12
  import { EDGY_LABEL_W, edgyLabelProps, edgyNodeProps, edgyNodeTextProps, FACETS_SCALE, } from './presets.js';
12
13
  import { EDGY_ROLE } from './roles.js';
@@ -46,6 +47,15 @@ export function createEdgyFacets(std) {
46
47
  // The frame: what makes this an EDGY board rather than three circles, and
47
48
  // what a finding is attributed to.
48
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')),
49
59
  xywh: new Bound(centerX - width / 2, centerY - height / 2, width, height).serialize(),
50
60
  });
51
61
  finish(gfx, id);
@@ -59,7 +69,15 @@ export async function createEdgyDynamic(std) {
59
69
  if (!gfx.surface)
60
70
  return;
61
71
  const job = createTemplateJob(std, 'template');
62
- const bound = await job.insertTemplate(structuredClone(edgyDynamicTemplate.content));
72
+ // Resolved through `localize` rather than the raw `content`: this action IS
73
+ // the command the Templates panel derives "EDGY dynamic" from
74
+ // (`templates/dynamic.ts`), and inserting the English snapshot here would
75
+ // make the two paths disagree — a senior-menu insertion staying English
76
+ // while the very same card from the panel comes out translated.
77
+ const snapshot = edgyDynamicTemplate.localize
78
+ ? edgyDynamicTemplate.localize(std)
79
+ : edgyDynamicTemplate.content;
80
+ const bound = await job.insertTemplate(structuredClone(snapshot));
63
81
  if (bound) {
64
82
  const padding = 20 / gfx.viewport.zoom;
65
83
  gfx.viewport.setViewportByBound(bound, [padding, padding, padding, padding], true);
@@ -1,6 +1,8 @@
1
1
  import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
2
- const BOARD_FILL = '#ffffff';
3
- const BOARD_BORDER = '#e0e0e0';
2
+ import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
3
+ /** The shared notation scale's card: painted at render time, so every board. */
4
+ const BOARD_FILL = NOTATION_NEUTRALS.cardFill;
5
+ const BOARD_BORDER = NOTATION_NEUTRALS.cardBorder;
4
6
  const BOARD_RADIUS = 16;
5
7
  const BOARD_BORDER_WIDTH = 2;
6
8
  /**
package/dist/legend.d.ts CHANGED
@@ -1,2 +1,11 @@
1
1
  import { type AutoLegendSpec } from '@formicoidea/labre-ddd-shared';
2
+ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The two section titles above that are this framework's OWN word rather than
5
+ * a role's — `titleKey` reuses a role's key where the section says a role's
6
+ * name (the facets, the intersections), but "Base elements" and "Relations"
7
+ * name a GROUPING of roles, so each mints a key of its own. For
8
+ * `translations.ts`'s manifest.
9
+ */
10
+ export declare const EDGY_LEGEND_CHROME_WORDINGS: readonly ChromeWording[];
2
11
  export declare const EDGY_AUTO_LEGEND: AutoLegendSpec;
package/dist/legend.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import { roleLabel, } from '@formicoidea/labre-ddd-shared';
2
- import { EDGY_DYNAMIC_NODES, EDGY_ZONE_FILL, EDGY_ZONES, edgyElementLabel, } from './metamodel.js';
2
+ import { BOARD_LEGEND_TITLE, } from '@formicoidea/labre-core/shared/services';
3
+ import { EDGY_DYNAMIC_NODES, EDGY_ZONE_FILL, EDGY_ZONES, edgyElementLabel, edgyElementLabelKey, } from './metamodel.js';
3
4
  import { NODE_FILL, NODE_STROKE } from './node/consts.js';
4
5
  import { EDGY_ROLE, EDGY_ROLES } from './roles.js';
5
6
  /**
@@ -38,8 +39,14 @@ function zoneEntries(zone) {
38
39
  },
39
40
  }));
40
41
  }
42
+ /**
43
+ * A facet's section title says the facet's own name, so it reuses the key the
44
+ * metamodel's seeds already carry (`edgyElementLabelKey`): the legend and the
45
+ * facets diagram say "Identity" with one key, not two.
46
+ */
41
47
  const FACET_SECTIONS = EDGY_ZONES.filter(zone => zone.group === 'facet').map(zone => ({
42
48
  title: edgyElementLabel(zone.id),
49
+ titleKey: edgyElementLabelKey(zone.id),
43
50
  entries: zoneEntries(zone.id),
44
51
  }));
45
52
  /**
@@ -49,6 +56,7 @@ const FACET_SECTIONS = EDGY_ZONES.filter(zone => zone.group === 'facet').map(zon
49
56
  */
50
57
  const INTERSECTIONS_SECTION = {
51
58
  title: 'Intersections',
59
+ titleKey: 'com.labre.edgy.seed.intersections-title',
52
60
  entries: EDGY_ZONES.filter(zone => zone.group === 'intersection').flatMap(zone => zoneEntries(zone.id)),
53
61
  };
54
62
  /**
@@ -66,6 +74,7 @@ const INTERSECTIONS_SECTION = {
66
74
  */
67
75
  const BASE_SECTION = {
68
76
  title: 'Base elements',
77
+ titleKey: 'com.labre.edgy.legend.section.base-elements',
69
78
  entries: ['people', 'outcome', 'object', 'activity'].map(kind => ({
70
79
  role: EDGY_ROLE[kind],
71
80
  exact: true,
@@ -85,6 +94,7 @@ const BASE_SECTION = {
85
94
  */
86
95
  const RELATIONS_SECTION = {
87
96
  title: 'Relations',
97
+ titleKey: 'com.labre.edgy.legend.section.relations',
88
98
  entries: [
89
99
  {
90
100
  role: EDGY_ROLE.relation,
@@ -97,8 +107,22 @@ const RELATIONS_SECTION = {
97
107
  },
98
108
  ],
99
109
  };
110
+ /**
111
+ * The two section titles above that are this framework's OWN word rather than
112
+ * a role's — `titleKey` reuses a role's key where the section says a role's
113
+ * name (the facets, the intersections), but "Base elements" and "Relations"
114
+ * name a GROUPING of roles, so each mints a key of its own. For
115
+ * `translations.ts`'s manifest.
116
+ */
117
+ export const EDGY_LEGEND_CHROME_WORDINGS = [
118
+ ['com.labre.edgy.legend.section.base-elements', 'Base elements'],
119
+ ['com.labre.edgy.legend.section.relations', 'Relations'],
120
+ ];
100
121
  export const EDGY_AUTO_LEGEND = {
101
- title: 'Legend',
122
+ // The shared DDD auto-legend box's own generic chrome, resolved through
123
+ // the same key every board that has one reuses — see `AutoLegendSpec.title`.
124
+ title: BOARD_LEGEND_TITLE[1],
125
+ titleKey: BOARD_LEGEND_TITLE[0],
102
126
  roles: EDGY_ROLES,
103
127
  sections: [
104
128
  ...FACET_SECTIONS,
@@ -87,6 +87,26 @@ export type EdgyElementName = 'content' | 'purpose' | 'organisation' | 'process'
87
87
  * prints as a row label or a section title. One expression, one wording.
88
88
  */
89
89
  export declare const edgyElementLabel: (name: EdgyElementName | EdgyZone) => string;
90
+ /**
91
+ * The i18n key {@link edgyElementLabel} is the English default of — one key
92
+ * per lowercase name, shared by every surface that writes the word: the "EDGY
93
+ * dynamic" template (`templates/dynamic.ts`), the Facets overview,
94
+ * Organisation chart and Customer journey scenes (`templates/index.ts`), and
95
+ * the facets background's own creation site (`actions.ts`, `EdgyFacetsProps`).
96
+ * `organisation`, `brand` and `product` are BOTH an element name and an
97
+ * intersection zone id — one word, one key, exactly the "un mot partagé = une
98
+ * clé" rule.
99
+ */
100
+ export declare const edgyElementLabelKey: (name: EdgyElementName | EdgyZone) => string;
101
+ /**
102
+ * The i18n key one of the metamodel's 24 canonical VERBS is the English
103
+ * default of — drawn raw as a connector label wherever the "EDGY dynamic"
104
+ * template or a hand-composed scene uses it (`surfaceText(verb)`), sharing one
105
+ * key per distinct verb string across every surface that writes it.
106
+ */
107
+ export declare const edgyVerbSeedKey: (verb: string) => string;
108
+ /** Every distinct verb {@link EDGY_DYNAMIC_RELATIONS} carries, in table order. */
109
+ export declare const EDGY_DYNAMIC_VERBS: readonly string[];
90
110
  export declare const EDGY_DYNAMIC_NODES: Record<EdgyElementName, {
91
111
  kind: 'outcome' | 'object' | 'activity';
92
112
  cx: number;
package/dist/metamodel.js CHANGED
@@ -100,6 +100,28 @@ export const EDGY_DYNAMIC_RELATIONS = [
100
100
  * prints as a row label or a section title. One expression, one wording.
101
101
  */
102
102
  export const edgyElementLabel = (name) => name.charAt(0).toUpperCase() + name.slice(1);
103
+ /**
104
+ * The i18n key {@link edgyElementLabel} is the English default of — one key
105
+ * per lowercase name, shared by every surface that writes the word: the "EDGY
106
+ * dynamic" template (`templates/dynamic.ts`), the Facets overview,
107
+ * Organisation chart and Customer journey scenes (`templates/index.ts`), and
108
+ * the facets background's own creation site (`actions.ts`, `EdgyFacetsProps`).
109
+ * `organisation`, `brand` and `product` are BOTH an element name and an
110
+ * intersection zone id — one word, one key, exactly the "un mot partagé = une
111
+ * clé" rule.
112
+ */
113
+ export const edgyElementLabelKey = (name) => `com.labre.edgy.seed.${name}`;
114
+ /**
115
+ * The i18n key one of the metamodel's 24 canonical VERBS is the English
116
+ * default of — drawn raw as a connector label wherever the "EDGY dynamic"
117
+ * template or a hand-composed scene uses it (`surfaceText(verb)`), sharing one
118
+ * key per distinct verb string across every surface that writes it.
119
+ */
120
+ export const edgyVerbSeedKey = (verb) => `com.labre.edgy.seed.verb-${verb.replace(/\s+/g, '-')}`;
121
+ /** Every distinct verb {@link EDGY_DYNAMIC_RELATIONS} carries, in table order. */
122
+ export const EDGY_DYNAMIC_VERBS = [
123
+ ...new Set(EDGY_DYNAMIC_RELATIONS.map(([, , verb]) => verb)),
124
+ ];
103
125
  export const EDGY_DYNAMIC_NODES = {
104
126
  content: { kind: 'object', cx: 237.5, cy: 100, zone: 'identity' },
105
127
  purpose: { kind: 'outcome', cx: 282.5, cy: 100, zone: 'identity' },
@@ -4,9 +4,19 @@ import type { EdgyNodeKind } from '@formicoidea/labre-core/model';
4
4
  * shape (ShapeElementModel-derived) so its stroke / fill / inner text are
5
5
  * editable via the shape toolbar; these are just the pre-formatted defaults at
6
6
  * creation.
7
+ *
8
+ * The fill is the shared notation scale's card. The stroke is NOT the scale's
9
+ * ink: it is the official EDGY stencil ink, sourced from the pictograms this
10
+ * package ships (`pictograms/Shape-Activity.svg`, `Shape-Object.svg`,
11
+ * `Shape-Outcome.svg`, every `Small-Shape-*.svg`, `Icon-People.svg`,
12
+ * `Tag-Black.svg` all draw `stroke:#262626` at `stroke-width:2`). A neutral a
13
+ * notation borrows from its official source is content of the notation, like
14
+ * the C4 stencil's `#444444`, and stays a literal.
7
15
  */
8
- export declare const NODE_FILL = "#ffffff";
16
+ export declare const NODE_FILL: string;
17
+ /** The EDGY stencil ink (`stroke:#262626` in the official pictograms). */
9
18
  export declare const NODE_STROKE = "#262626";
19
+ /** The EDGY stencil stroke weight (`stroke-width:2` in the pictograms). */
10
20
  export declare const NODE_STROKE_WIDTH = 2;
11
21
  /** Outcome corner radius (absolute px — a lightly rounded rectangle). */
12
22
  export declare const OUTCOME_RADIUS = 6;
@@ -1,11 +1,22 @@
1
+ import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
1
2
  /**
2
3
  * Visual constants for the four EDGY base-element nodes. Each node is a NATIVE
3
4
  * shape (ShapeElementModel-derived) so its stroke / fill / inner text are
4
5
  * editable via the shape toolbar; these are just the pre-formatted defaults at
5
6
  * creation.
7
+ *
8
+ * The fill is the shared notation scale's card. The stroke is NOT the scale's
9
+ * ink: it is the official EDGY stencil ink, sourced from the pictograms this
10
+ * package ships (`pictograms/Shape-Activity.svg`, `Shape-Object.svg`,
11
+ * `Shape-Outcome.svg`, every `Small-Shape-*.svg`, `Icon-People.svg`,
12
+ * `Tag-Black.svg` all draw `stroke:#262626` at `stroke-width:2`). A neutral a
13
+ * notation borrows from its official source is content of the notation, like
14
+ * the C4 stencil's `#444444`, and stays a literal.
6
15
  */
7
- export const NODE_FILL = '#ffffff';
16
+ export const NODE_FILL = NOTATION_NEUTRALS.cardFill;
17
+ /** The EDGY stencil ink (`stroke:#262626` in the official pictograms). */
8
18
  export const NODE_STROKE = '#262626';
19
+ /** The EDGY stencil stroke weight (`stroke-width:2` in the pictograms). */
9
20
  export const NODE_STROKE_WIDTH = 2;
10
21
  /** Outcome corner radius (absolute px — a lightly rounded rectangle). */
11
22
  export const OUTCOME_RADIUS = 6;
@@ -1,7 +1,8 @@
1
1
  import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
2
2
  import { ConnectorMode, PointStyle, StrokeStyle } from '@formicoidea/labre-core/model';
3
+ import { translateKey } from '@formicoidea/labre-core/shared/services';
3
4
  import { CROP } from '../consts.js';
4
- import { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_RELATIONS, EDGY_ZONE_FILL, edgyElementLabel, } from '../metamodel.js';
5
+ import { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_RELATIONS, EDGY_ZONE_FILL, edgyElementLabel, edgyElementLabelKey, edgyVerbSeedKey, } from '../metamodel.js';
5
6
  import { NODE_SIZE, NODE_STROKE } from '../node/consts.js';
6
7
  import { edgyNodeProps, edgyNodeTextProps } from '../presets.js';
7
8
  import { EDGY_RELATION_LABEL_DISTANCE, edgyVerbLabelXYWH } from '../relation.js';
@@ -31,7 +32,15 @@ export const DYN_SCALE = 4.8;
31
32
  export function dynToModel(refX, refY) {
32
33
  return [(refX - CROP.x) * DYN_SCALE, (refY - CROP.y) * DYN_SCALE];
33
34
  }
34
- function dynamic() {
35
+ /**
36
+ * One label's resolved text — the host's catalogue when `std` is a real
37
+ * inserting editor, the English fallback when building the module's own
38
+ * `content` (no editor exists yet at that point).
39
+ */
40
+ function seedText(std, key, fallback) {
41
+ return std ? translateKey(std, key, fallback) : fallback;
42
+ }
43
+ function dynamic(std) {
35
44
  const out = {
36
45
  bg: {
37
46
  type: 'edgy',
@@ -48,7 +57,7 @@ function dynamic() {
48
57
  const nw = w ?? NODE_SIZE[kind].w;
49
58
  const nh = NODE_SIZE[kind].h;
50
59
  const [mx, my] = dynToModel(cx, cy);
51
- const name = edgyElementLabel(key);
60
+ const name = seedText(std, edgyElementLabelKey(key), edgyElementLabel(key));
52
61
  out[key] = {
53
62
  // What an EDGY node IS — the same description the toolbox draws from.
54
63
  ...edgyNodeProps(kind, {
@@ -65,6 +74,7 @@ function dynamic() {
65
74
  };
66
75
  }
67
76
  EDGY_DYNAMIC_RELATIONS.forEach(([src, dst, verb, t], i) => {
77
+ const verbText = seedText(std, edgyVerbSeedKey(verb), verb);
68
78
  out[`rel${i}`] = {
69
79
  type: 'connector',
70
80
  mode: ConnectorMode.Straight,
@@ -84,7 +94,12 @@ function dynamic() {
84
94
  // re-centred on the path at the first layout, but the w/h ARE the label
85
95
  // box — size it to the verb so the text lays out on one line. The
86
96
  // distance slides the verb along the link like the reference diagram.
87
- text: surfaceText(verb),
97
+ //
98
+ // The BOX (`edgyVerbLabelXYWH`) is still measured off the ENGLISH verb:
99
+ // it is layout, sized once from the table this template already reads,
100
+ // and a translated word longer than its English original wraps inside
101
+ // that box like any other native text rather than resizing it.
102
+ text: surfaceText(verbText),
88
103
  labelXYWH: edgyVerbLabelXYWH(verb),
89
104
  labelOffset: { distance: t ?? EDGY_RELATION_LABEL_DISTANCE },
90
105
  };
@@ -103,4 +118,5 @@ export const edgyDynamicTemplate = {
103
118
  // and fails the day the command is renamed.
104
119
  commandId: 'edgy.insertDynamic',
105
120
  content: makeTemplateSnapshot(dynamic(), NAME),
121
+ localize: std => makeTemplateSnapshot(dynamic(std), NAME),
106
122
  };
@@ -1,4 +1,132 @@
1
1
  import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
2
+ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
3
+ /**
4
+ * The four hand-composed scenes' own tile names — chrome, not seeds: a
5
+ * `Template.nameKey`, re-resolved every time the panel opens, never written
6
+ * into a document. None of the four derives from a command.
7
+ */
8
+ export declare const EDGY_TEMPLATE_NAME_FACETS_OVERVIEW: ChromeWording;
9
+ export declare const EDGY_TEMPLATE_NAME_CUSTOMER_JOURNEY: ChromeWording;
10
+ export declare const EDGY_TEMPLATE_NAME_SERVICE_BLUEPRINT: ChromeWording;
11
+ export declare const EDGY_TEMPLATE_NAME_ORGANISATION_CHART: ChromeWording;
12
+ export declare const EDGY_TEMPLATE_SEED: {
13
+ readonly facetsTitle: {
14
+ key: string;
15
+ fallback: string;
16
+ };
17
+ readonly intersectionsTitle: {
18
+ key: string;
19
+ fallback: string;
20
+ };
21
+ readonly journeyStep: {
22
+ key: string;
23
+ fallback: string;
24
+ };
25
+ readonly channel: {
26
+ key: string;
27
+ fallback: string;
28
+ };
29
+ readonly taskN: {
30
+ key: string;
31
+ fallback: string;
32
+ };
33
+ readonly customer: {
34
+ key: string;
35
+ fallback: string;
36
+ };
37
+ readonly laneEvidence: {
38
+ key: string;
39
+ fallback: string;
40
+ };
41
+ readonly laneCustomerActions: {
42
+ key: string;
43
+ fallback: string;
44
+ };
45
+ readonly laneOnstage: {
46
+ key: string;
47
+ fallback: string;
48
+ };
49
+ readonly laneBackstage: {
50
+ key: string;
51
+ fallback: string;
52
+ };
53
+ readonly laneSupportProcesses: {
54
+ key: string;
55
+ fallback: string;
56
+ };
57
+ readonly laneSupportSystems: {
58
+ key: string;
59
+ fallback: string;
60
+ };
61
+ readonly admissionForm: {
62
+ key: string;
63
+ fallback: string;
64
+ };
65
+ readonly confirmation: {
66
+ key: string;
67
+ fallback: string;
68
+ };
69
+ readonly sendForm: {
70
+ key: string;
71
+ fallback: string;
72
+ };
73
+ readonly followStatus: {
74
+ key: string;
75
+ fallback: string;
76
+ };
77
+ readonly receiveDecision: {
78
+ key: string;
79
+ fallback: string;
80
+ };
81
+ readonly formHandling: {
82
+ key: string;
83
+ fallback: string;
84
+ };
85
+ readonly customerSupport: {
86
+ key: string;
87
+ fallback: string;
88
+ };
89
+ readonly contact: {
90
+ key: string;
91
+ fallback: string;
92
+ };
93
+ readonly customerService: {
94
+ key: string;
95
+ fallback: string;
96
+ };
97
+ readonly customerFollowUp: {
98
+ key: string;
99
+ fallback: string;
100
+ };
101
+ readonly processForm: {
102
+ key: string;
103
+ fallback: string;
104
+ };
105
+ readonly decisionAcceptReject: {
106
+ key: string;
107
+ fallback: string;
108
+ };
109
+ readonly crmApplication: {
110
+ key: string;
111
+ fallback: string;
112
+ };
113
+ readonly caseManagement: {
114
+ key: string;
115
+ fallback: string;
116
+ };
117
+ readonly paymentsSystem: {
118
+ key: string;
119
+ fallback: string;
120
+ };
121
+ readonly businessUnit: {
122
+ key: string;
123
+ fallback: string;
124
+ };
125
+ readonly group: {
126
+ key: string;
127
+ fallback: string;
128
+ };
129
+ };
2
130
  /**
3
131
  * The metamodel itself — the 12 elements and the 24 relations — lives in
4
132
  * `../metamodel.ts`, and the template that draws it in `./dynamic.ts`.
@@ -1,9 +1,102 @@
1
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 { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
4
+ import { translateKey, } from '@formicoidea/labre-core/shared/services';
3
5
  import { edgyCommands } from '../commands.js';
4
- import { NODE_STROKE, NODE_STROKE_WIDTH } from '../node/consts.js';
6
+ import { edgyElementLabel, edgyElementLabelKey, edgyVerbSeedKey, } from '../metamodel.js';
7
+ import { NODE_FILL, NODE_STROKE, NODE_STROKE_WIDTH } from '../node/consts.js';
5
8
  import { edgyNodeProps, edgyNodeTextProps } from '../presets.js';
6
9
  import { edgyDynamicTemplate } from './dynamic.js';
10
+ /**
11
+ * The seeds that belong to these four scenes only — a case name, a lane
12
+ * title, a step counter — and nowhere else in the framework. Declared once so
13
+ * the builders below and the manifest (`../translations.ts`) read the very
14
+ * same fallback. The twelve element/zone names and the two verbs these scenes
15
+ * REUSE (`Organisation`, `Task`, `traverses`…) go through
16
+ * `edgyElementLabelKey` / `edgyVerbSeedKey` instead — the same key the
17
+ * metamodel and `templates/dynamic.ts` already write, per "un mot partagé =
18
+ * une clé".
19
+ */
20
+ const S = (slug, fallback) => ({
21
+ key: `com.labre.edgy.seed.${slug}`,
22
+ fallback,
23
+ });
24
+ /**
25
+ * The four hand-composed scenes' own tile names — chrome, not seeds: a
26
+ * `Template.nameKey`, re-resolved every time the panel opens, never written
27
+ * into a document. None of the four derives from a command.
28
+ */
29
+ export const EDGY_TEMPLATE_NAME_FACETS_OVERVIEW = [
30
+ 'com.labre.edgy.template.facets-overview',
31
+ 'Facets overview',
32
+ ];
33
+ export const EDGY_TEMPLATE_NAME_CUSTOMER_JOURNEY = [
34
+ 'com.labre.edgy.template.customer-journey',
35
+ 'Customer journey',
36
+ ];
37
+ export const EDGY_TEMPLATE_NAME_SERVICE_BLUEPRINT = [
38
+ 'com.labre.edgy.template.service-blueprint',
39
+ 'Service blueprint',
40
+ ];
41
+ export const EDGY_TEMPLATE_NAME_ORGANISATION_CHART = [
42
+ 'com.labre.edgy.template.organisation-chart',
43
+ 'Organisation chart',
44
+ ];
45
+ export const EDGY_TEMPLATE_SEED = {
46
+ facetsTitle: S('facets-title', 'Facets'),
47
+ intersectionsTitle: S('intersections-title', 'Intersections'),
48
+ journeyStep: S('journey-step', 'Journey step {{n}}'),
49
+ channel: S('channel-n', 'Channel {{n}}'),
50
+ taskN: S('task-n', 'Task {{n}}'),
51
+ customer: S('customer', 'Customer'),
52
+ laneEvidence: S('lane-physical-evidence', 'Physical Evidence'),
53
+ laneCustomerActions: S('lane-customer-actions', 'Customer Actions'),
54
+ laneOnstage: S('lane-onstage-actions', 'On-stage Actions'),
55
+ laneBackstage: S('lane-backstage-actions', 'Back-stage Actions'),
56
+ laneSupportProcesses: S('lane-support-processes', 'Support Processes'),
57
+ laneSupportSystems: S('lane-support-systems', 'Support Systems'),
58
+ admissionForm: S('admission-form', 'Admission Form'),
59
+ confirmation: S('confirmation', 'Confirmation'),
60
+ sendForm: S('send-form', 'Send Form'),
61
+ followStatus: S('follow-status', 'Follow status'),
62
+ receiveDecision: S('receive-decision', 'Receive decision'),
63
+ formHandling: S('form-handling', 'Form handling'),
64
+ customerSupport: S('customer-support', 'Customer support'),
65
+ contact: S('contact', 'Contact'),
66
+ customerService: S('customer-service', 'Customer service'),
67
+ customerFollowUp: S('customer-follow-up', 'Customer follow-up'),
68
+ processForm: S('process-form', 'Process Form'),
69
+ decisionAcceptReject: S('decision-accept-reject', 'Decision [Accept|Reject]'),
70
+ crmApplication: S('crm-application', 'CRM Application'),
71
+ caseManagement: S('case-management', 'Case Management'),
72
+ paymentsSystem: S('payments-system', 'Payments System'),
73
+ businessUnit: S('business-unit', 'Business Unit {{unit}}'),
74
+ group: S('group', 'Group {{code}}'),
75
+ };
76
+ /**
77
+ * One seed's resolved text — the host's catalogue when `std` is a real
78
+ * inserting editor, the English fallback when building the module's own
79
+ * `content` (no editor exists yet at that point).
80
+ */
81
+ function seedText(std, def, params) {
82
+ return std
83
+ ? translateKey(std, def.key, def.fallback, params)
84
+ : // No interpolation library at module load: every fallback with a
85
+ // placeholder is filled by hand here, the same way `translateKey`
86
+ // fills it internally when a host has no entry for the key.
87
+ Object.entries(params ?? {}).reduce((text, [name, value]) => text.replace(`{{${name}}}`, String(value)), def.fallback);
88
+ }
89
+ /** One of the twelve element/zone names, shared with the rest of the framework. */
90
+ function elementLabel(std, name) {
91
+ return seedText(std, {
92
+ key: edgyElementLabelKey(name),
93
+ fallback: edgyElementLabel(name),
94
+ });
95
+ }
96
+ /** One of the metamodel's verbs, shared with `templates/dynamic.ts`. */
97
+ function verbLabel(std, verb) {
98
+ return seedText(std, { key: edgyVerbSeedKey(verb), fallback: verb });
99
+ }
7
100
  /**
8
101
  * The EDGY palette — DERIVED from the toolbox, one template per command.
9
102
  *
@@ -51,7 +144,7 @@ function rect(x, y, w, h, opts = {}) {
51
144
  type: 'shape',
52
145
  shapeType: 'rect',
53
146
  filled: true,
54
- fillColor: opts.fill ?? '#ffffff',
147
+ fillColor: opts.fill ?? NODE_FILL,
55
148
  strokeColor: opts.stroke ?? NODE_STROKE,
56
149
  strokeWidth: opts.sw ?? NODE_STROKE_WIDTH,
57
150
  shapeStyle: ShapeStyle.General,
@@ -146,8 +239,9 @@ function attach(src, dst, opts = {}) {
146
239
  };
147
240
  }
148
241
  // ── Facets overview (six facets, sub-cards, facets/intersections) ─────
149
- function facetsOverview() {
242
+ function facetsOverview(std) {
150
243
  const out = {};
244
+ const el = (name) => elementLabel(std, name);
151
245
  const tall = (key, x, name, cards) => {
152
246
  const [hdr, sub] = C[key];
153
247
  out[`${key}F`] = rect(x, 140, 192, 460, {
@@ -199,39 +293,55 @@ function facetsOverview() {
199
293
  });
200
294
  };
201
295
  // lower facets first (drawn behind the tall ones at the overlaps)
202
- low('organisation', 240, 'Organisation');
203
- low('product', 664, 'Product');
204
- low('brand', 1088, 'Brand');
205
- tall('identity', 40, 'Identity', ['Purpose', 'Content', 'Story']);
206
- tall('architecture', 464, 'Architecture', ['Capability', 'Asset', 'Process']);
207
- tall('experience', 888, 'Experience', ['Task', 'Channel', 'Journey']);
208
- out.fLabel = label(464, 80, 192, 28, 'Facets', { fontSize: 18 });
296
+ low('organisation', 240, el('organisation'));
297
+ low('product', 664, el('product'));
298
+ low('brand', 1088, el('brand'));
299
+ tall('identity', 40, el('identity'), [
300
+ el('purpose'),
301
+ el('content'),
302
+ el('story'),
303
+ ]);
304
+ tall('architecture', 464, el('architecture'), [
305
+ el('capability'),
306
+ el('asset'),
307
+ el('process'),
308
+ ]);
309
+ tall('experience', 888, el('experience'), [
310
+ el('task'),
311
+ el('channel'),
312
+ el('journey'),
313
+ ]);
314
+ out.fLabel = label(464, 80, 192, 28, seedText(std, EDGY_TEMPLATE_SEED.facetsTitle), { fontSize: 18 });
209
315
  out.fBar = line(136, 120, 984, 120);
210
- out.iLabel = label(664, 740, 192, 28, 'Intersections', { fontSize: 18 });
316
+ out.iLabel = label(664, 740, 192, 28, seedText(std, EDGY_TEMPLATE_SEED.intersectionsTitle), { fontSize: 18 });
211
317
  out.iBar = line(336, 728, 1184, 728);
212
318
  return out;
213
319
  }
214
320
  // ── Customer journey ──────────────────────────────────────────────────
215
- function journey() {
321
+ function journey(std) {
216
322
  const step = (i, x) => enode('activity', x, 132, 224, 116, {
217
323
  fill: JOURNEY_PINK,
218
- text: `Journey step ${i}`,
324
+ text: seedText(std, EDGY_TEMPLATE_SEED.journeyStep, { n: i }),
219
325
  textColor: '#ffffff',
220
326
  fontSize: 18,
221
327
  });
222
328
  const ch = (x, n) => enode('object', x, 392, 184, 96, {
223
329
  fill: JOURNEY_PINK,
224
- text: `Channel ${n}`,
330
+ text: seedText(std, EDGY_TEMPLATE_SEED.channel, { n }),
225
331
  fontSize: 16,
226
332
  });
227
333
  const tk = (x, n) => enode('object', x, 540, 168, 92, {
228
334
  fill: JOURNEY_PINK,
229
- text: `Task ${n}`,
335
+ text: seedText(std, EDGY_TEMPLATE_SEED.taskN, { n }),
230
336
  fontSize: 16,
231
337
  });
338
+ const traverses = verbLabel(std, 'traverses');
339
+ const uses = verbLabel(std, 'uses');
232
340
  return {
233
- cust: enode('people', 40, 120, 64, 64, { fill: '#ffffff' }),
234
- custL: label(20, 192, 104, 24, 'Customer', { fontSize: 14 }),
341
+ cust: enode('people', 40, 120, 64, 64, { fill: NODE_FILL }),
342
+ custL: label(20, 192, 104, 24, seedText(std, EDGY_TEMPLATE_SEED.customer), {
343
+ fontSize: 14,
344
+ }),
235
345
  // The person and its name travel together, as `createEdgyPeople` writes
236
346
  // them. A group is placed AFTER its members: the id middleware remaps
237
347
  // `children` against elements it has already seen.
@@ -254,7 +364,7 @@ function journey() {
254
364
  roughness: 0,
255
365
  xywh: '[150,108,860,164]',
256
366
  },
257
- bandL: label(170, 116, 120, 24, 'Journey', {
367
+ bandL: label(170, 116, 120, 24, elementLabel(std, 'journey'), {
258
368
  color: '#ffffff',
259
369
  fontSize: 16,
260
370
  align: TextAlign.Left,
@@ -263,7 +373,7 @@ function journey() {
263
373
  s2: step(2, 500),
264
374
  s3: step(3, 760),
265
375
  rightTask: enode('object', 1060, 132, 168, 92, {
266
- text: 'Task',
376
+ text: elementLabel(std, 'task'),
267
377
  fontSize: 16,
268
378
  }),
269
379
  c1: ch(258, 'X'),
@@ -272,21 +382,30 @@ function journey() {
272
382
  t1: tk(266, 'A'),
273
383
  t2: tk(526, 'B'),
274
384
  t3: tk(786, 'C'),
275
- trav1: label(258, 350, 184, 20, 'traverses', {
385
+ trav1: label(258, 350, 184, 20, traverses, {
386
+ fontSize: 13,
387
+ color: NOTATION_NEUTRALS.label,
388
+ }),
389
+ trav2: label(518, 350, 184, 20, traverses, {
390
+ fontSize: 13,
391
+ color: NOTATION_NEUTRALS.label,
392
+ }),
393
+ trav3: label(778, 350, 184, 20, traverses, {
394
+ fontSize: 13,
395
+ color: NOTATION_NEUTRALS.label,
396
+ }),
397
+ use1: label(258, 504, 184, 20, uses, {
276
398
  fontSize: 13,
277
- color: '#5f6368',
399
+ color: NOTATION_NEUTRALS.label,
278
400
  }),
279
- trav2: label(518, 350, 184, 20, 'traverses', {
401
+ use2: label(518, 504, 184, 20, uses, {
280
402
  fontSize: 13,
281
- color: '#5f6368',
403
+ color: NOTATION_NEUTRALS.label,
282
404
  }),
283
- trav3: label(778, 350, 184, 20, 'traverses', {
405
+ use3: label(778, 504, 184, 20, uses, {
284
406
  fontSize: 13,
285
- color: '#5f6368',
407
+ color: NOTATION_NEUTRALS.label,
286
408
  }),
287
- use1: label(258, 504, 184, 20, 'uses', { fontSize: 13, color: '#5f6368' }),
288
- use2: label(518, 504, 184, 20, 'uses', { fontSize: 13, color: '#5f6368' }),
289
- use3: label(778, 504, 184, 20, 'uses', { fontSize: 13, color: '#5f6368' }),
290
409
  l1: attach('s1', 'c1'),
291
410
  l2: attach('s2', 'c2'),
292
411
  l3: attach('s3', 'c3'),
@@ -296,14 +415,14 @@ function journey() {
296
415
  };
297
416
  }
298
417
  // ── Service blueprint (six swimlanes) ─────────────────────────────────
299
- function blueprint() {
418
+ function blueprint(std) {
300
419
  const lanes = [
301
- 'Physical Evidence',
302
- 'Customer Actions',
303
- 'On-stage Actions',
304
- 'Back-stage Actions',
305
- 'Support Processes',
306
- 'Support Systems',
420
+ seedText(std, EDGY_TEMPLATE_SEED.laneEvidence),
421
+ seedText(std, EDGY_TEMPLATE_SEED.laneCustomerActions),
422
+ seedText(std, EDGY_TEMPLATE_SEED.laneOnstage),
423
+ seedText(std, EDGY_TEMPLATE_SEED.laneBackstage),
424
+ seedText(std, EDGY_TEMPLATE_SEED.laneSupportProcesses),
425
+ seedText(std, EDGY_TEMPLATE_SEED.laneSupportSystems),
307
426
  ];
308
427
  const laneFill = [
309
428
  '#fdeef2',
@@ -330,21 +449,25 @@ function blueprint() {
330
449
  const box = (x, y, fill, t) => enode('object', x, y, 200, 60, { fill, text: t, fontSize: 14 });
331
450
  const P = C.experience[1], B = C.architecture[1];
332
451
  Object.assign(out, {
333
- af: rect(280, 64, 220, 70, { text: 'Admission Form' }),
334
- cf: rect(1010, 64, 220, 70, { text: 'Confirmation' }),
335
- sf: chev(280, 214, P, 'Send Form'),
336
- fs: chev(600, 214, P, 'Follow status'),
337
- rd: chev(1010, 214, P, 'Receive decision'),
338
- fh: chev(280, 364, B, 'Form handling'),
339
- cs: chev(600, 364, B, 'Customer support'),
340
- ct: chev(1010, 364, B, 'Contact'),
341
- csv: chev(280, 514, B, 'Customer service'),
342
- cfu: box(1010, 514, B, 'Customer follow-up'),
343
- pf: chev(600, 664, B, 'Process Form'),
344
- dec: chev(900, 664, B, 'Decision [Accept|Reject]'),
345
- crm: box(280, 814, B, 'CRM Application'),
346
- cms: box(600, 814, B, 'Case Management'),
347
- pay: box(900, 814, B, 'Payments System'),
452
+ af: rect(280, 64, 220, 70, {
453
+ text: seedText(std, EDGY_TEMPLATE_SEED.admissionForm),
454
+ }),
455
+ cf: rect(1010, 64, 220, 70, {
456
+ text: seedText(std, EDGY_TEMPLATE_SEED.confirmation),
457
+ }),
458
+ sf: chev(280, 214, P, seedText(std, EDGY_TEMPLATE_SEED.sendForm)),
459
+ fs: chev(600, 214, P, seedText(std, EDGY_TEMPLATE_SEED.followStatus)),
460
+ rd: chev(1010, 214, P, seedText(std, EDGY_TEMPLATE_SEED.receiveDecision)),
461
+ fh: chev(280, 364, B, seedText(std, EDGY_TEMPLATE_SEED.formHandling)),
462
+ cs: chev(600, 364, B, seedText(std, EDGY_TEMPLATE_SEED.customerSupport)),
463
+ ct: chev(1010, 364, B, seedText(std, EDGY_TEMPLATE_SEED.contact)),
464
+ csv: chev(280, 514, B, seedText(std, EDGY_TEMPLATE_SEED.customerService)),
465
+ cfu: box(1010, 514, B, seedText(std, EDGY_TEMPLATE_SEED.customerFollowUp)),
466
+ pf: chev(600, 664, B, seedText(std, EDGY_TEMPLATE_SEED.processForm)),
467
+ dec: chev(900, 664, B, seedText(std, EDGY_TEMPLATE_SEED.decisionAcceptReject)),
468
+ crm: box(280, 814, B, seedText(std, EDGY_TEMPLATE_SEED.crmApplication)),
469
+ cms: box(600, 814, B, seedText(std, EDGY_TEMPLATE_SEED.caseManagement)),
470
+ pay: box(900, 814, B, seedText(std, EDGY_TEMPLATE_SEED.paymentsSystem)),
348
471
  a1: attach('af', 'sf', { arrow: true }),
349
472
  a2: attach('sf', 'fs', { arrow: true }),
350
473
  a3: attach('fs', 'rd', { arrow: true }),
@@ -357,17 +480,19 @@ function blueprint() {
357
480
  return out;
358
481
  }
359
482
  // ── Organisation chart ────────────────────────────────────────────────
360
- function orgChart() {
483
+ function orgChart(std) {
361
484
  const cyan = C.organisation[0];
362
485
  const u = (x, y, w, t) => enode('object', x, y, w, 64, { fill: cyan, text: t, fontSize: 16 });
486
+ const businessUnit = (unit) => seedText(std, EDGY_TEMPLATE_SEED.businessUnit, { unit });
487
+ const group = (code) => seedText(std, EDGY_TEMPLATE_SEED.group, { code });
363
488
  return {
364
- org: u(420, 40, 180, 'Organisation'),
365
- a: u(120, 200, 200, 'Business Unit A'),
366
- b: u(410, 200, 200, 'Business Unit B'),
367
- c: u(700, 200, 200, 'Business Unit C'),
368
- a1: u(60, 360, 170, 'Group A-1'),
369
- a2: u(260, 360, 170, 'Group A-2'),
370
- c1: u(715, 360, 170, 'Group C-1'),
489
+ org: u(420, 40, 180, elementLabel(std, 'organisation')),
490
+ a: u(120, 200, 200, businessUnit('A')),
491
+ b: u(410, 200, 200, businessUnit('B')),
492
+ c: u(700, 200, 200, businessUnit('C')),
493
+ a1: u(60, 360, 170, group('A-1')),
494
+ a2: u(260, 360, 170, group('A-2')),
495
+ c1: u(715, 360, 170, group('C-1')),
371
496
  // Attached orthogonal connectors: the router draws the org-chart elbows
372
497
  // and the links follow when units are moved around.
373
498
  e1: attach('org', 'a', { mode: ConnectorMode.Orthogonal }),
@@ -387,30 +512,50 @@ function orgChart() {
387
512
  export { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_RELATIONS, } from '../metamodel.js';
388
513
  export { DYN_SCALE, dynToModel, edgyDynamicTemplate } from './dynamic.js';
389
514
  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. */
391
- function tpl(name, preview, elements) {
515
+ /**
516
+ * A hand-composed template — what is left once the artefacts are derived.
517
+ *
518
+ * `build` takes the OPTIONAL inserting editor: called with none, at module
519
+ * load, for the English `content`; called again with the real `std` as
520
+ * {@link Template.localize}, so the two builds read the very same layout and
521
+ * differ only in the words a seed resolves to (`docs/adr/0016`).
522
+ */
523
+ function tpl(name, preview, build, nameKey) {
392
524
  return {
393
525
  name,
394
526
  type: 'template',
395
527
  preview,
396
- content: makeTemplateSnapshot(elements, name),
528
+ nameKey,
529
+ content: makeTemplateSnapshot(build(), name),
530
+ localize: std => makeTemplateSnapshot(build(std), name),
397
531
  };
398
532
  }
399
533
  export const edgyTemplateCategory = {
400
534
  name: 'EDGY',
535
+ // Reuses the senior button's own key — see `TemplateCategory.nameKey`.
536
+ nameKey: 'com.labre.framework.edgy',
401
537
  templates: [
402
- 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()),
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()),
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()),
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()),
538
+ 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, EDGY_TEMPLATE_NAME_FACETS_OVERVIEW[0]),
539
+ 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, EDGY_TEMPLATE_NAME_CUSTOMER_JOURNEY[0]),
540
+ 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, EDGY_TEMPLATE_NAME_SERVICE_BLUEPRINT[0]),
541
+ 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="${NODE_STROKE}"/></svg>`, orgChart, EDGY_TEMPLATE_NAME_ORGANISATION_CHART[0]),
542
+ // Kept: `addFacets`'s own label is "Enterprise Design facets" (the senior
543
+ // sub-menu's wording), and this tile's tooltip has always said the
544
+ // shorter "Facets diagram". `resolveTemplateName` still resolves it
545
+ // through the command's `labelKey`, with THIS literal (not the command's)
546
+ // as the fallback, so the tile keeps its own English wording with no
547
+ // catalogue.
406
548
  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'),
407
549
  edgyDynamicTemplate,
408
550
  // The blank board had no template at all until the palette was derived —
409
551
  // 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>`),
552
+ //
553
+ // Kept, same reason as `addFacets` above: `addBoard`'s own label spells
554
+ // out "(hover spotlight)", which this tile's tooltip never has.
555
+ templateFromCommand(byId('edgy.addBoard'), `<svg ${ATTRS} fill="none"><rect x="8" y="9" width="119" height="62" rx="8" fill="${NOTATION_NEUTRALS.cardFill}" stroke="${NOTATION_NEUTRALS.cardBorder}" stroke-width="2"/><path d="M52 30 H84 M53 31 L66 58 M83 31 L70 58" stroke="${NODE_STROKE}" 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'),
556
+ templateFromCommand(byId('edgy.addPeople'), `<svg ${ATTRS} fill="${NODE_STROKE}"><circle cx="67" cy="32" r="9" fill="none" stroke="${NODE_STROKE}" stroke-width="2.4"/><path d="M50 60 a17 17 0 0 1 34 0" fill="none" stroke="${NODE_STROKE}" stroke-width="2.4"/></svg>`),
557
+ templateFromCommand(byId('edgy.addOutcome'), `<svg ${ATTRS} fill="none"><rect x="20" y="24" width="95" height="34" rx="6" stroke="${NODE_STROKE}" stroke-width="2"/></svg>`),
558
+ templateFromCommand(byId('edgy.addObject'), `<svg ${ATTRS} fill="none"><rect x="20" y="24" width="95" height="34" stroke="${NODE_STROKE}" stroke-width="2"/></svg>`),
559
+ templateFromCommand(byId('edgy.addActivity'), `<svg ${ATTRS} fill="none"><path d="M20 24 H98 L116 41 H116 L98 58 H20 Z" stroke="${NODE_STROKE}" stroke-width="2" stroke-linejoin="round"/></svg>`),
415
560
  ],
416
561
  };
@@ -1,5 +1,7 @@
1
1
  import { type ChromeWording, type ToolbarContext } from '@formicoidea/labre-core/shared/services';
2
2
  import { type TemplateResult } from 'lit';
3
+ /** Every wording above, for `translations.ts`'s manifest contribution. */
4
+ export declare const EDGY_TOOLBAR_WORDINGS: readonly ChromeWording[];
3
5
  export declare const edgyToolbarConfig: {
4
6
  readonly actions: [{
5
7
  id: string;
@@ -5,6 +5,20 @@ import { BOARD_LEGEND_NOTATION, BOARD_RESIZE_TOGGLE, TelemetryProvider, ToolbarM
5
5
  import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
6
6
  import { html } from 'lit';
7
7
  import { EDGY_AUTO_LEGEND } from '../legend.js';
8
+ /** The two toolbar tooltips this file used to hard-code as English literals. */
9
+ const FACET_LABELS_TOGGLE = [
10
+ 'com.labre.edgy.toolbar.facet-labels',
11
+ 'Show / hide facet labels',
12
+ ];
13
+ const HOVER_SPOTLIGHT_TOGGLE = [
14
+ 'com.labre.edgy.toolbar.hover-spotlight',
15
+ 'Enable / disable hover spotlight',
16
+ ];
17
+ /** Every wording above, for `translations.ts`'s manifest contribution. */
18
+ export const EDGY_TOOLBAR_WORDINGS = [
19
+ FACET_LABELS_TOGGLE,
20
+ HOVER_SPOTLIGHT_TOGGLE,
21
+ ];
8
22
  const ResizeIcon = html `<svg
9
23
  width="24"
10
24
  height="24"
@@ -118,7 +132,7 @@ function legendAction(Model, id) {
118
132
  export const edgyToolbarConfig = {
119
133
  actions: [
120
134
  booleanToggle(EdgyFacetsElementModel, 'a.toggle-resize', BOARD_RESIZE_TOGGLE, ResizeIcon, 'resizeEnabled'),
121
- booleanToggle(EdgyFacetsElementModel, 'b.toggle-labels', 'Show / hide facet labels', LabelsIcon, 'showLabels'),
135
+ booleanToggle(EdgyFacetsElementModel, 'b.toggle-labels', FACET_LABELS_TOGGLE, LabelsIcon, 'showLabels'),
122
136
  // No spotlight toggle here: the hover spotlight is BOARD logic and lives on
123
137
  // the EDGY board toolbar below. The Venn only carries APPEARANCE (#195).
124
138
  legendAction(EdgyFacetsElementModel, 'c.legend'),
@@ -132,7 +146,7 @@ export const edgyToolbarExtension = ToolbarModuleExtension({
132
146
  export const edgyBoardToolbarConfig = {
133
147
  actions: [
134
148
  booleanToggle(EdgyBoardElementModel, 'a.toggle-resize', BOARD_RESIZE_TOGGLE, ResizeIcon, 'resizeEnabled'),
135
- booleanToggle(EdgyBoardElementModel, 'b.toggle-spotlight', 'Enable / disable hover spotlight', SpotlightIcon, 'spotlightEnabled'),
149
+ booleanToggle(EdgyBoardElementModel, 'b.toggle-spotlight', HOVER_SPOTLIGHT_TOGGLE, SpotlightIcon, 'spotlightEnabled'),
136
150
  legendAction(EdgyBoardElementModel, 'c.legend'),
137
151
  ],
138
152
  when: ctx => ctx.getSurfaceModelsByType(EdgyBoardElementModel).length > 0,
@@ -1 +1,26 @@
1
+ import { type ChromeWording } from '@formicoidea/labre-core/shared/services';
2
+ /**
3
+ * The twelve EDGY swatches' own names, resolved by `resolvePaletteLabel`
4
+ * (`@labre/affine-components/color-picker`) through each swatch's own
5
+ * {@link NamedPalette.labelWording} — see Wardley's own
6
+ * `WARDLEY_PALETTE_WORDINGS` (`../../wardley/src/toolbar/node-config.ts`)
7
+ * for the same mechanism applied to a sibling framework; the two never
8
+ * import each other.
9
+ *
10
+ * Exported so `../translations.ts` can list them in the manifest under
11
+ * source `chrome`.
12
+ */
13
+ export declare const EDGY_PALETTE_WORDING_IDENTITY: ChromeWording;
14
+ export declare const EDGY_PALETTE_WORDING_ARCHITECTURE: ChromeWording;
15
+ export declare const EDGY_PALETTE_WORDING_EXPERIENCE: ChromeWording;
16
+ export declare const EDGY_PALETTE_WORDING_ORGANISATION: ChromeWording;
17
+ export declare const EDGY_PALETTE_WORDING_BRAND: ChromeWording;
18
+ export declare const EDGY_PALETTE_WORDING_PRODUCT: ChromeWording;
19
+ export declare const EDGY_PALETTE_WORDING_IDENTITY_LIGHT: ChromeWording;
20
+ export declare const EDGY_PALETTE_WORDING_ARCHITECTURE_LIGHT: ChromeWording;
21
+ export declare const EDGY_PALETTE_WORDING_EXPERIENCE_LIGHT: ChromeWording;
22
+ export declare const EDGY_PALETTE_WORDING_ORGANISATION_LIGHT: ChromeWording;
23
+ export declare const EDGY_PALETTE_WORDING_BRAND_LIGHT: ChromeWording;
24
+ export declare const EDGY_PALETTE_WORDING_PRODUCT_LIGHT: ChromeWording;
25
+ export declare const EDGY_PALETTE_WORDINGS: readonly ChromeWording[];
1
26
  export declare const edgyNodeToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
@@ -1,31 +1,151 @@
1
1
  import { neutralPalettes, paletteColorAction, shapeToolbarConfig, } from '@formicoidea/labre-core/gfx/shape';
2
2
  import { ToolbarModuleExtension, } from '@formicoidea/labre-core/shared/services';
3
3
  import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
4
+ /**
5
+ * The twelve EDGY swatches' own names, resolved by `resolvePaletteLabel`
6
+ * (`@labre/affine-components/color-picker`) through each swatch's own
7
+ * {@link NamedPalette.labelWording} — see Wardley's own
8
+ * `WARDLEY_PALETTE_WORDINGS` (`../../wardley/src/toolbar/node-config.ts`)
9
+ * for the same mechanism applied to a sibling framework; the two never
10
+ * import each other.
11
+ *
12
+ * Exported so `../translations.ts` can list them in the manifest under
13
+ * source `chrome`.
14
+ */
15
+ export const EDGY_PALETTE_WORDING_IDENTITY = [
16
+ 'com.labre.edgy.palette.identity',
17
+ 'Identity',
18
+ ];
19
+ export const EDGY_PALETTE_WORDING_ARCHITECTURE = [
20
+ 'com.labre.edgy.palette.architecture',
21
+ 'Architecture',
22
+ ];
23
+ export const EDGY_PALETTE_WORDING_EXPERIENCE = [
24
+ 'com.labre.edgy.palette.experience',
25
+ 'Experience',
26
+ ];
27
+ export const EDGY_PALETTE_WORDING_ORGANISATION = [
28
+ 'com.labre.edgy.palette.organisation',
29
+ 'Organisation',
30
+ ];
31
+ export const EDGY_PALETTE_WORDING_BRAND = [
32
+ 'com.labre.edgy.palette.brand',
33
+ 'Brand',
34
+ ];
35
+ export const EDGY_PALETTE_WORDING_PRODUCT = [
36
+ 'com.labre.edgy.palette.product',
37
+ 'Product',
38
+ ];
39
+ export const EDGY_PALETTE_WORDING_IDENTITY_LIGHT = [
40
+ 'com.labre.edgy.palette.identity-light',
41
+ 'Identity light',
42
+ ];
43
+ export const EDGY_PALETTE_WORDING_ARCHITECTURE_LIGHT = [
44
+ 'com.labre.edgy.palette.architecture-light',
45
+ 'Architecture light',
46
+ ];
47
+ export const EDGY_PALETTE_WORDING_EXPERIENCE_LIGHT = [
48
+ 'com.labre.edgy.palette.experience-light',
49
+ 'Experience light',
50
+ ];
51
+ export const EDGY_PALETTE_WORDING_ORGANISATION_LIGHT = [
52
+ 'com.labre.edgy.palette.organisation-light',
53
+ 'Organisation light',
54
+ ];
55
+ export const EDGY_PALETTE_WORDING_BRAND_LIGHT = [
56
+ 'com.labre.edgy.palette.brand-light',
57
+ 'Brand light',
58
+ ];
59
+ export const EDGY_PALETTE_WORDING_PRODUCT_LIGHT = [
60
+ 'com.labre.edgy.palette.product-light',
61
+ 'Product light',
62
+ ];
63
+ export const EDGY_PALETTE_WORDINGS = [
64
+ EDGY_PALETTE_WORDING_IDENTITY,
65
+ EDGY_PALETTE_WORDING_ARCHITECTURE,
66
+ EDGY_PALETTE_WORDING_EXPERIENCE,
67
+ EDGY_PALETTE_WORDING_ORGANISATION,
68
+ EDGY_PALETTE_WORDING_BRAND,
69
+ EDGY_PALETTE_WORDING_PRODUCT,
70
+ EDGY_PALETTE_WORDING_IDENTITY_LIGHT,
71
+ EDGY_PALETTE_WORDING_ARCHITECTURE_LIGHT,
72
+ EDGY_PALETTE_WORDING_EXPERIENCE_LIGHT,
73
+ EDGY_PALETTE_WORDING_ORGANISATION_LIGHT,
74
+ EDGY_PALETTE_WORDING_BRAND_LIGHT,
75
+ EDGY_PALETTE_WORDING_PRODUCT_LIGHT,
76
+ ];
4
77
  /**
5
78
  * The typical EDGY palette, surfaced as ready-made swatches in the EDGY node
6
79
  * color picker (facet + intersection colours, saturated then pastel), followed
7
80
  * by the default editor palette.
8
81
  */
9
82
  const EDGY_PALETTES = [
10
- { key: 'Identity', value: '#00ea4e' },
11
- { key: 'Architecture', value: '#034cee' },
12
- { key: 'Experience', value: '#ff0056' },
13
- { key: 'Organisation', value: '#00caf4' },
14
- { key: 'Brand', value: '#ffa500' },
15
- { key: 'Product', value: '#cf00ff' },
16
- { key: 'Identity light', value: '#80ffb7' },
17
- { key: 'Architecture light', value: '#a6c0ff' },
18
- { key: 'Experience light', value: '#ff99bd' },
19
- { key: 'Organisation light', value: '#80eaff' },
20
- { key: 'Brand light', value: '#ffd580' },
21
- { key: 'Product light', value: '#e599ff' },
83
+ {
84
+ key: 'Identity',
85
+ value: '#00ea4e',
86
+ labelWording: EDGY_PALETTE_WORDING_IDENTITY,
87
+ },
88
+ {
89
+ key: 'Architecture',
90
+ value: '#034cee',
91
+ labelWording: EDGY_PALETTE_WORDING_ARCHITECTURE,
92
+ },
93
+ {
94
+ key: 'Experience',
95
+ value: '#ff0056',
96
+ labelWording: EDGY_PALETTE_WORDING_EXPERIENCE,
97
+ },
98
+ {
99
+ key: 'Organisation',
100
+ value: '#00caf4',
101
+ labelWording: EDGY_PALETTE_WORDING_ORGANISATION,
102
+ },
103
+ { key: 'Brand', value: '#ffa500', labelWording: EDGY_PALETTE_WORDING_BRAND },
104
+ {
105
+ key: 'Product',
106
+ value: '#cf00ff',
107
+ labelWording: EDGY_PALETTE_WORDING_PRODUCT,
108
+ },
109
+ {
110
+ key: 'Identity light',
111
+ value: '#80ffb7',
112
+ labelWording: EDGY_PALETTE_WORDING_IDENTITY_LIGHT,
113
+ },
114
+ {
115
+ key: 'Architecture light',
116
+ value: '#a6c0ff',
117
+ labelWording: EDGY_PALETTE_WORDING_ARCHITECTURE_LIGHT,
118
+ },
119
+ {
120
+ key: 'Experience light',
121
+ value: '#ff99bd',
122
+ labelWording: EDGY_PALETTE_WORDING_EXPERIENCE_LIGHT,
123
+ },
124
+ {
125
+ key: 'Organisation light',
126
+ value: '#80eaff',
127
+ labelWording: EDGY_PALETTE_WORDING_ORGANISATION_LIGHT,
128
+ },
129
+ {
130
+ key: 'Brand light',
131
+ value: '#ffd580',
132
+ labelWording: EDGY_PALETTE_WORDING_BRAND_LIGHT,
133
+ },
134
+ {
135
+ key: 'Product light',
136
+ value: '#e599ff',
137
+ labelWording: EDGY_PALETTE_WORDING_PRODUCT_LIGHT,
138
+ },
22
139
  ];
23
140
  /**
24
141
  * From the default editor palette we keep ONLY the neutrals (greys, white,
25
142
  * black, transparent) — the historical colours are dropped in favour of the
26
143
  * EDGY swatches above.
27
144
  */
28
- const EDGY_PALETTE_LIST = [...EDGY_PALETTES, ...neutralPalettes()];
145
+ const EDGY_PALETTE_LIST = [
146
+ ...EDGY_PALETTES,
147
+ ...neutralPalettes(),
148
+ ];
29
149
  /**
30
150
  * EDGY fill / stroke colour picker — identical to the shape one but seeded with
31
151
  * the EDGY palette swatches (`.palettes`).
@@ -1,11 +1,84 @@
1
1
  import { collectTranslationKeys, commandCategoryTranslationEntries, commandTranslationEntries, mergeTranslationEntries, } from '@formicoidea/labre-core/std';
2
2
  import { edgyCommands } from './commands.js';
3
+ import { EDGY_LEGEND_CHROME_WORDINGS } from './legend.js';
4
+ import { EDGY_DYNAMIC_NODES, EDGY_DYNAMIC_VERBS, EDGY_ZONES, edgyElementLabel, edgyElementLabelKey, edgyVerbSeedKey, } from './metamodel.js';
3
5
  import { NODE_LABEL, nodeLabelKey } from './node/consts.js';
4
6
  import { EDGY_NUDGES } from './nudges.js';
5
7
  import { EDGY_PROFILES } from './profiles.js';
6
8
  import { EDGY_READING } from './reading.js';
7
9
  import { EDGY_ROLES } from './roles.js';
8
10
  import { EDGY_RULES } from './rules.js';
11
+ import { EDGY_TEMPLATE_NAME_CUSTOMER_JOURNEY, EDGY_TEMPLATE_NAME_FACETS_OVERVIEW, EDGY_TEMPLATE_NAME_ORGANISATION_CHART, EDGY_TEMPLATE_NAME_SERVICE_BLUEPRINT, EDGY_TEMPLATE_SEED, } from './templates/index.js';
12
+ import { EDGY_TOOLBAR_WORDINGS } from './toolbar/config.js';
13
+ import { EDGY_PALETTE_WORDINGS } from './toolbar/node-config.js';
14
+ /**
15
+ * The name every one of the 12 official elements and the 6 Venn zones is
16
+ * seeded with — one key per distinct lowercase word (`organisation`, `brand`
17
+ * and `product` name BOTH an element and an intersection zone, so the same
18
+ * key covers both), reused verbatim by `templates/dynamic.ts`,
19
+ * `templates/index.ts` and the facets background's own creation site
20
+ * (`actions.ts`).
21
+ */
22
+ const elementAndZoneSeedEntries = () => {
23
+ const names = new Set([
24
+ ...Object.keys(EDGY_DYNAMIC_NODES),
25
+ ...EDGY_ZONES.map(zone => zone.id),
26
+ ]);
27
+ return [...names].map(name => ({
28
+ key: edgyElementLabelKey(name),
29
+ fallback: edgyElementLabel(name),
30
+ source: 'seed',
31
+ }));
32
+ };
33
+ /** The legend's own section titles (see {@link EDGY_LEGEND_CHROME_WORDINGS}). */
34
+ const legendChromeEntries = () => EDGY_LEGEND_CHROME_WORDINGS.map(([key, fallback]) => ({
35
+ key,
36
+ fallback,
37
+ source: 'chrome',
38
+ }));
39
+ /** The toolbar tooltips this framework used to hard-code as English literals. */
40
+ const toolbarChromeEntries = () => EDGY_TOOLBAR_WORDINGS.map(([key, fallback]) => ({
41
+ key,
42
+ fallback,
43
+ source: 'chrome',
44
+ }));
45
+ /**
46
+ * The twelve node-colour-picker swatches' own names
47
+ * (`EDGY_PALETTE_WORDINGS`, `toolbar/node-config.ts`) — chrome, re-rendered
48
+ * on every locale switch, never seeded into a document.
49
+ */
50
+ const paletteWordingEntries = () => EDGY_PALETTE_WORDINGS.map(([key, fallback]) => ({
51
+ key,
52
+ fallback,
53
+ source: 'chrome',
54
+ }));
55
+ /** The metamodel's 24 canonical verbs, drawn raw as a connector label. */
56
+ const verbSeedEntries = () => EDGY_DYNAMIC_VERBS.map(verb => ({
57
+ key: edgyVerbSeedKey(verb),
58
+ fallback: verb,
59
+ source: 'seed',
60
+ }));
61
+ /**
62
+ * The seeds that belong to the four hand-composed scenes only (a lane title,
63
+ * a case name, a step counter…) — derived from `EDGY_TEMPLATE_SEED`, the very
64
+ * table the scenes read at placement.
65
+ */
66
+ const sceneSeedEntries = () => Object.values(EDGY_TEMPLATE_SEED).map(({ key, fallback }) => ({
67
+ key,
68
+ fallback,
69
+ source: 'seed',
70
+ }));
71
+ /**
72
+ * The four hand-composed scenes' own tile names — chrome, not seeds: a
73
+ * `Template.nameKey`, re-resolved every time the panel opens, never written
74
+ * into a document. None of the four derives from a command.
75
+ */
76
+ const templateNameEntries = () => [
77
+ EDGY_TEMPLATE_NAME_FACETS_OVERVIEW,
78
+ EDGY_TEMPLATE_NAME_CUSTOMER_JOURNEY,
79
+ EDGY_TEMPLATE_NAME_SERVICE_BLUEPRINT,
80
+ EDGY_TEMPLATE_NAME_ORGANISATION_CHART,
81
+ ].map(([key, fallback]) => ({ key, fallback, source: 'chrome' }));
9
82
  /**
10
83
  * THIS framework's contribution to the translation-key manifest — every
11
84
  * `com.labre.*` key EDGY can hand to `TranslationProvider.t`, derived from the
@@ -42,7 +115,7 @@ Object.entries(NODE_LABEL).map(([kind, label]) => ({
42
115
  key: nodeLabelKey(kind),
43
116
  fallback: label,
44
117
  source: 'seed',
45
- })),
118
+ })), elementAndZoneSeedEntries(), verbSeedEntries(), sceneSeedEntries(), templateNameEntries(), legendChromeEntries(), toolbarChromeEntries(), paletteWordingEntries(),
46
119
  // LAST, and the order is load-bearing: a reading profile carries the
47
120
  // framework's own `roles`, so walking it reaches every role key the line
48
121
  // above already named. `mergeTranslationEntries` keeps the FIRST
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.39.3",
4
+ "version": "0.41.0",
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.39.3",
35
- "@formicoidea/labre-ddd-shared": "0.39.3",
34
+ "@formicoidea/labre-core": "0.41.0",
35
+ "@formicoidea/labre-ddd-shared": "0.41.0",
36
36
  "@preact/signals-core": "^1.8.0",
37
37
  "lit": "^3.2.0",
38
38
  "yjs": "^13.6.21"