@formicoidea/labre-framework-wardley 0.32.0 → 0.33.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/index.d.ts CHANGED
@@ -7,3 +7,21 @@ export { WARDLEY_PROFILES } from './profiles.js';
7
7
  export { WARDLEY_NUDGES } from './nudges.js';
8
8
  export { WARDLEY_NAMING_CONVENTIONS, WARDLEY_READING } from './reading.js';
9
9
  export { WARDLEY_RULES } from './rules.js';
10
+ /**
11
+ * The map's own frame, declared. Public because a host that lays elements out
12
+ * against the plot — labre-mcp does, and so does anything that generates a map
13
+ * without an editor — needs the margins the renderer actually uses, and a
14
+ * second copy of four numbers is a copy that drifts (`templates/maps.ts` says
15
+ * how that went the first time). P3's packaging obligation: a value both
16
+ * consumers read is reachable from the index, never by deep import.
17
+ */
18
+ export { WARDLEY_BACKGROUND } from './background.js';
19
+ /**
20
+ * The OWM DSL, both directions — pure functions of text and models, exported
21
+ * for the reason ADR 0012 § P3 gives: the editor command and labre-mcp call the
22
+ * SAME function, and no serialization logic for a Labre framework lives outside
23
+ * this repo. `exportWardleyOwm` is what replaces labre-mcp's own serializer.
24
+ */
25
+ export { exportWardleyOwm, exportWardleyOwmWithWarnings, OWM_SCOPE, type OwmPlot, owmCoordsOf, owmPlotOf, owmPointOf, WARDLEY_OWM_FORMAT_ID, type WardleyExportBoard, wardleyBoardFrom, wardleySafeFilename, } from './export.js';
26
+ export { importWardleyOwm } from './import.js';
27
+ export { WARDLEY_INTERCHANGE, WARDLEY_OWM_EXPORT, WARDLEY_OWM_EXTENSION, WARDLEY_OWM_FORMAT, WARDLEY_OWM_IMPORT, WARDLEY_OWM_MIME, WARDLEY_SVG_FORMAT, WARDLEY_SVG_IMPORT, } from './interchange.js';
package/dist/index.js CHANGED
@@ -7,3 +7,25 @@ export { WARDLEY_PROFILES } from './profiles.js';
7
7
  export { WARDLEY_NUDGES } from './nudges.js';
8
8
  export { WARDLEY_NAMING_CONVENTIONS, WARDLEY_READING } from './reading.js';
9
9
  export { WARDLEY_RULES } from './rules.js';
10
+ /**
11
+ * The map's own frame, declared. Public because a host that lays elements out
12
+ * against the plot — labre-mcp does, and so does anything that generates a map
13
+ * without an editor — needs the margins the renderer actually uses, and a
14
+ * second copy of four numbers is a copy that drifts (`templates/maps.ts` says
15
+ * how that went the first time). P3's packaging obligation: a value both
16
+ * consumers read is reachable from the index, never by deep import.
17
+ */
18
+ export { WARDLEY_BACKGROUND } from './background.js';
19
+ /**
20
+ * The OWM DSL, both directions — pure functions of text and models, exported
21
+ * for the reason ADR 0012 § P3 gives: the editor command and labre-mcp call the
22
+ * SAME function, and no serialization logic for a Labre framework lives outside
23
+ * this repo. `exportWardleyOwm` is what replaces labre-mcp's own serializer.
24
+ */
25
+ export { exportWardleyOwm, exportWardleyOwmWithWarnings, OWM_SCOPE, owmCoordsOf, owmPlotOf, owmPointOf, WARDLEY_OWM_FORMAT_ID, wardleyBoardFrom, wardleySafeFilename, } from './export.js';
26
+ export { importWardleyOwm } from './import.js';
27
+ // Wardley's entries in the interchange registry (`docs/adr/0012`) — the OWM
28
+ // DSL both ways, and the visual-tier SVG fallback. Exported whole so a host can
29
+ // ask what Wardley can read and write without mounting an editor, and call it
30
+ // without one either (P3).
31
+ export { WARDLEY_INTERCHANGE, WARDLEY_OWM_EXPORT, WARDLEY_OWM_EXTENSION, WARDLEY_OWM_FORMAT, WARDLEY_OWM_IMPORT, WARDLEY_OWM_MIME, WARDLEY_SVG_FORMAT, WARDLEY_SVG_IMPORT, } from './interchange.js';
@@ -0,0 +1,80 @@
1
+ import type { InterchangeCapability, InterchangeExportCapability, InterchangeFormat, InterchangeImportCapability } from '@formicoidea/labre-core/blocks/surface';
2
+ /**
3
+ * Wardley's entries in the interchange registry (`docs/adr/0012`, P1).
4
+ *
5
+ * Two formats and three rows. Both directions of the OWM DSL — the export is
6
+ * the row the ADR records as **owed** (a Wardley serializer exists today in
7
+ * labre-mcp, outside this repo, and is the one violation of P3 the ADR names;
8
+ * it exists here now, so that repo becomes a caller and its copy is deleted),
9
+ * and the import is the row the ADR calls **the reference Wardley import**,
10
+ * because the OWM DSL is the settled Wardley vocabulary while mermaid's Wardley
11
+ * diagram type is still experimental upstream. Then SVG IN, the visual-tier
12
+ * FALLBACK, which promises recognition and nothing else.
13
+ *
14
+ * The file is laid out as one section per FORMAT, each holding its format
15
+ * object then its capabilities, with {@link WARDLEY_INTERCHANGE} at the bottom
16
+ * collecting them. A format is added by adding a section, not by editing one.
17
+ *
18
+ * Everything here is pure. No half has ever had a `std` in sight, and this file
19
+ * adds no editor to any of them: it picks the artefacts the writer speaks about
20
+ * out of a surface's elements, and hands a reader's output straight back.
21
+ */
22
+ /**
23
+ * The OnlineWardleyMaps DSL. **Semantic** — the file carries a model, not a
24
+ * picture: a `[visibility, evolution]` pair IS a position on the value chain
25
+ * and on the evolution axis, so the whole preservation contract applies and the
26
+ * import needs no invented axis (P2, and D4's "a format that carries
27
+ * coordinates but no pixels").
28
+ *
29
+ * `text/plain`, because that is what a DSL is, and `.owm` first — it is the
30
+ * extension a download is given. `.wm` rides behind it: the same bytes are
31
+ * written under both in the wild, and a picker that refused one would refuse a
32
+ * valid map for the sake of a filename. What the file actually IS is decided by
33
+ * the reader.
34
+ *
35
+ * The two directions share the FORMAT object, deliberately. `owm` is the id
36
+ * under which foreign matter rides on an element (D2), so a reader and a writer
37
+ * that disagreed about it would write payloads the other could not find.
38
+ */
39
+ export declare const WARDLEY_OWM_EXTENSION = ".owm";
40
+ export declare const WARDLEY_OWM_MIME = "text/plain";
41
+ export declare const WARDLEY_OWM_FORMAT: InterchangeFormat;
42
+ /** `wardley:owm:export` — the row that replaces labre-mcp's own serializer. */
43
+ export declare const WARDLEY_OWM_EXPORT: InterchangeExportCapability;
44
+ /** `wardley:owm:import` — an `.owm` file as a map. */
45
+ export declare const WARDLEY_OWM_IMPORT: InterchangeImportCapability;
46
+ /**
47
+ * SVG. **Visual** — the file carries a rendering, not a model, so it makes
48
+ * exactly one promise: the picture arrives as editable elements.
49
+ *
50
+ * Wardley's own format object, and NOT one shared with BPMN's, because ADR 0012
51
+ * rejects "one capability per format, with the framework inferred from the
52
+ * file": a `.svg` is read by several frameworks, and deciding which one a
53
+ * picture is a picture OF is the guess this platform refuses everywhere else.
54
+ * The three constants are the parser package's, so the declarations cannot
55
+ * drift into filtering a picker on different extensions.
56
+ */
57
+ export declare const WARDLEY_SVG_FORMAT: InterchangeFormat;
58
+ /**
59
+ * `wardley:svg:import` — an SVG as a sketch, best effort.
60
+ *
61
+ * **The heuristics statement and the known failure modes this capability owes
62
+ * (ADR 0012, open question 2) are the module documentation of
63
+ * `packages/affine/blocks/surface/src/extensions/svg-sketch.ts`.** Written once
64
+ * there because Wardley and BPMN wrap the SAME parser and therefore make the
65
+ * same guesses — and because the ADR's question is about what a visual
66
+ * capability is ALLOWED to guess, which for both of them is: geometry, and
67
+ * nothing else. A circle is a circle; whether it is a component is the
68
+ * author's sentence, not this reader's.
69
+ *
70
+ * What lands is an ADR 0007 level-1 sketch — plain shapes, brush strokes and
71
+ * editable free text — which the author then PROMOTES onto a map. In
72
+ * particular the two axes and the evolution bands are NOT recovered: a map's
73
+ * coordinates are its meaning, and reading them off a picture would be
74
+ * inventing a position and presenting it as read. {@link WARDLEY_OWM_IMPORT}
75
+ * beside it is the route a user should be pointed at, exactly as P2 says —
76
+ * this one is for the picture somebody sent you from a tool that writes no OWM.
77
+ */
78
+ export declare const WARDLEY_SVG_IMPORT: InterchangeImportCapability;
79
+ /** Everything Wardley registers, in one list the view extension hands over. */
80
+ export declare const WARDLEY_INTERCHANGE: readonly InterchangeCapability[];
@@ -0,0 +1,138 @@
1
+ import { interchangeCapabilityId, parseSvgSketch, SVG_SKETCH_EXTENSION, SVG_SKETCH_FORMAT_ID, SVG_SKETCH_MIME, } from '@formicoidea/labre-core/blocks/surface';
2
+ import { exportWardleyOwmWithWarnings, WARDLEY_OWM_FORMAT_ID, wardleyBoardFrom, wardleySafeFilename, } from './export.js';
3
+ import { importWardleyOwm } from './import.js';
4
+ /**
5
+ * Wardley's entries in the interchange registry (`docs/adr/0012`, P1).
6
+ *
7
+ * Two formats and three rows. Both directions of the OWM DSL — the export is
8
+ * the row the ADR records as **owed** (a Wardley serializer exists today in
9
+ * labre-mcp, outside this repo, and is the one violation of P3 the ADR names;
10
+ * it exists here now, so that repo becomes a caller and its copy is deleted),
11
+ * and the import is the row the ADR calls **the reference Wardley import**,
12
+ * because the OWM DSL is the settled Wardley vocabulary while mermaid's Wardley
13
+ * diagram type is still experimental upstream. Then SVG IN, the visual-tier
14
+ * FALLBACK, which promises recognition and nothing else.
15
+ *
16
+ * The file is laid out as one section per FORMAT, each holding its format
17
+ * object then its capabilities, with {@link WARDLEY_INTERCHANGE} at the bottom
18
+ * collecting them. A format is added by adding a section, not by editing one.
19
+ *
20
+ * Everything here is pure. No half has ever had a `std` in sight, and this file
21
+ * adds no editor to any of them: it picks the artefacts the writer speaks about
22
+ * out of a surface's elements, and hands a reader's output straight back.
23
+ */
24
+ /* ── OWM (semantic) ───────────────────────────────────────────────────── */
25
+ /**
26
+ * The OnlineWardleyMaps DSL. **Semantic** — the file carries a model, not a
27
+ * picture: a `[visibility, evolution]` pair IS a position on the value chain
28
+ * and on the evolution axis, so the whole preservation contract applies and the
29
+ * import needs no invented axis (P2, and D4's "a format that carries
30
+ * coordinates but no pixels").
31
+ *
32
+ * `text/plain`, because that is what a DSL is, and `.owm` first — it is the
33
+ * extension a download is given. `.wm` rides behind it: the same bytes are
34
+ * written under both in the wild, and a picker that refused one would refuse a
35
+ * valid map for the sake of a filename. What the file actually IS is decided by
36
+ * the reader.
37
+ *
38
+ * The two directions share the FORMAT object, deliberately. `owm` is the id
39
+ * under which foreign matter rides on an element (D2), so a reader and a writer
40
+ * that disagreed about it would write payloads the other could not find.
41
+ */
42
+ export const WARDLEY_OWM_EXTENSION = '.owm';
43
+ export const WARDLEY_OWM_MIME = 'text/plain';
44
+ export const WARDLEY_OWM_FORMAT = {
45
+ id: WARDLEY_OWM_FORMAT_ID,
46
+ tier: 'semantic',
47
+ extensions: [WARDLEY_OWM_EXTENSION, '.wm'],
48
+ mime: WARDLEY_OWM_MIME,
49
+ };
50
+ /**
51
+ * The board as an OWM document.
52
+ *
53
+ * A thin adapter and nothing else: it picks the Wardley artefacts out of the
54
+ * surface, names the file, and passes the writer's losses straight through.
55
+ * There is no second door — `wardley.exportOwm` calls THIS, so the command and
56
+ * the registry cannot produce different bytes, filenames or warnings.
57
+ *
58
+ * `warnings` is omitted rather than empty when the map came out whole, so a
59
+ * caller can ask `if (result.warnings)` and mean it.
60
+ */
61
+ const runWardleyOwmExport = (elements, context) => {
62
+ const name = wardleySafeFilename(context.name);
63
+ const { text, warnings } = exportWardleyOwmWithWarnings(wardleyBoardFrom(elements), { name });
64
+ return {
65
+ text,
66
+ filename: `${name}${WARDLEY_OWM_EXTENSION}`,
67
+ mime: WARDLEY_OWM_MIME,
68
+ ...(warnings.length > 0 ? { warnings } : {}),
69
+ };
70
+ };
71
+ /** `wardley:owm:export` — the row that replaces labre-mcp's own serializer. */
72
+ export const WARDLEY_OWM_EXPORT = {
73
+ id: interchangeCapabilityId('wardley', WARDLEY_OWM_FORMAT.id, 'export'),
74
+ framework: 'wardley',
75
+ format: WARDLEY_OWM_FORMAT,
76
+ direction: 'export',
77
+ run: runWardleyOwmExport,
78
+ };
79
+ /** `wardley:owm:import` — an `.owm` file as a map. */
80
+ export const WARDLEY_OWM_IMPORT = {
81
+ id: interchangeCapabilityId('wardley', WARDLEY_OWM_FORMAT.id, 'import'),
82
+ framework: 'wardley',
83
+ format: WARDLEY_OWM_FORMAT,
84
+ direction: 'import',
85
+ run: importWardleyOwm,
86
+ };
87
+ /* ── SVG (visual) ─────────────────────────────────────────────────────── */
88
+ /**
89
+ * SVG. **Visual** — the file carries a rendering, not a model, so it makes
90
+ * exactly one promise: the picture arrives as editable elements.
91
+ *
92
+ * Wardley's own format object, and NOT one shared with BPMN's, because ADR 0012
93
+ * rejects "one capability per format, with the framework inferred from the
94
+ * file": a `.svg` is read by several frameworks, and deciding which one a
95
+ * picture is a picture OF is the guess this platform refuses everywhere else.
96
+ * The three constants are the parser package's, so the declarations cannot
97
+ * drift into filtering a picker on different extensions.
98
+ */
99
+ export const WARDLEY_SVG_FORMAT = {
100
+ id: SVG_SKETCH_FORMAT_ID,
101
+ tier: 'visual',
102
+ extensions: [SVG_SKETCH_EXTENSION],
103
+ mime: SVG_SKETCH_MIME,
104
+ };
105
+ /**
106
+ * `wardley:svg:import` — an SVG as a sketch, best effort.
107
+ *
108
+ * **The heuristics statement and the known failure modes this capability owes
109
+ * (ADR 0012, open question 2) are the module documentation of
110
+ * `packages/affine/blocks/surface/src/extensions/svg-sketch.ts`.** Written once
111
+ * there because Wardley and BPMN wrap the SAME parser and therefore make the
112
+ * same guesses — and because the ADR's question is about what a visual
113
+ * capability is ALLOWED to guess, which for both of them is: geometry, and
114
+ * nothing else. A circle is a circle; whether it is a component is the
115
+ * author's sentence, not this reader's.
116
+ *
117
+ * What lands is an ADR 0007 level-1 sketch — plain shapes, brush strokes and
118
+ * editable free text — which the author then PROMOTES onto a map. In
119
+ * particular the two axes and the evolution bands are NOT recovered: a map's
120
+ * coordinates are its meaning, and reading them off a picture would be
121
+ * inventing a position and presenting it as read. {@link WARDLEY_OWM_IMPORT}
122
+ * beside it is the route a user should be pointed at, exactly as P2 says —
123
+ * this one is for the picture somebody sent you from a tool that writes no OWM.
124
+ */
125
+ export const WARDLEY_SVG_IMPORT = {
126
+ id: interchangeCapabilityId('wardley', WARDLEY_SVG_FORMAT.id, 'import'),
127
+ framework: 'wardley',
128
+ format: WARDLEY_SVG_FORMAT,
129
+ direction: 'import',
130
+ run: parseSvgSketch,
131
+ };
132
+ /* ── The list the view extension registers ────────────────────────────── */
133
+ /** Everything Wardley registers, in one list the view extension hands over. */
134
+ export const WARDLEY_INTERCHANGE = [
135
+ WARDLEY_OWM_EXPORT,
136
+ WARDLEY_OWM_IMPORT,
137
+ WARDLEY_SVG_IMPORT,
138
+ ];
@@ -1,6 +1,6 @@
1
1
  import { ElementRendererExtension, } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
3
- import { DefaultTheme } from '@formicoidea/labre-core/model';
3
+ import { DefaultTheme, } from '@formicoidea/labre-core/model';
4
4
  import { ANCHOR, ECOSYSTEM, METHOD, NODE_FILL } from './consts.js';
5
5
  /**
6
6
  * Renderer for a Wardley node. The circle is drawn by REUSING the native shape
package/dist/rules.js CHANGED
@@ -56,6 +56,10 @@ const changeArrowAgainstEvolution = {
56
56
  suggestionKey: 'com.labre.wardley.validation.change-arrow-against-evolution.suggestion',
57
57
  suggestionFallback: 'Evolution runs left to right — turn the arrow towards the commodity end, or draw a dependency instead.',
58
58
  version: 1,
59
+ provenance: {
60
+ source: 'recommendation',
61
+ reference: 'Wardley mapping — the evolution axis runs left to right, and a component moves with it',
62
+ },
59
63
  backgroundRole: WARDLEY_ROLE.map,
60
64
  // The frame's own declaration, carried as data exactly like `roles` is: it is
61
65
  // where "which way does evolution run" is written, and the engine reads it
@@ -117,6 +121,10 @@ const inertiaOffTransition = {
117
121
  // 3: the carrier condition is gone and the position is judged on the bar's
118
122
  // extent — a different verdict on the same map, so a new version.
119
123
  version: 3,
124
+ provenance: {
125
+ source: 'labre-convention',
126
+ reference: 'Labre convention (PO, recette of 02/08/2026) — Wardley names the zone, not the rule',
127
+ },
120
128
  backgroundRole: WARDLEY_ROLE.map,
121
129
  background: WARDLEY_BACKGROUND,
122
130
  attachment: {
@@ -186,6 +194,10 @@ const overlappingArtefacts = {
186
194
  // calibrated with a penetration threshold — the same mistakes, fewer of the
187
195
  // things that were never mistakes.
188
196
  version: 2,
197
+ provenance: {
198
+ source: 'labre-convention',
199
+ reference: 'Labre readability convention — no Wardley mapping rule speaks of overlapping ink',
200
+ },
189
201
  // Not a frame the rule measures against — an overlap is an overlap wherever
190
202
  // it happens — but the map a finding is ATTRIBUTED to, so the arbitration
191
203
  // "ignore this rule on the whole map" has one map to be written on.
@@ -251,6 +263,10 @@ const providerAboveConsumer = {
251
263
  suggestionKey: 'com.labre.wardley.validation.provider-above-consumer.suggestion',
252
264
  suggestionFallback: 'Needs run downwards on a Wardley map: move the provider below its consumer — or, if the link was drawn the wrong way round, reverse it.',
253
265
  version: 1,
266
+ provenance: {
267
+ source: 'recommendation',
268
+ reference: 'Wardley mapping — the value-chain grammar of the map',
269
+ },
254
270
  backgroundRole: WARDLEY_ROLE.map,
255
271
  background: WARDLEY_BACKGROUND,
256
272
  relativeOrder: {
@@ -27,7 +27,12 @@ const VARIANT_DEFAULTS = {
27
27
  };
28
28
  const bg = (variant, w = REF_WIDTH) => {
29
29
  const h = Math.round((w * 9) / 16);
30
- return { type: 'wardley', variant, ...VARIANT_DEFAULTS[variant], xywh: `[0,0,${w},${h}]` };
30
+ return {
31
+ type: 'wardley',
32
+ variant,
33
+ ...VARIANT_DEFAULTS[variant],
34
+ xywh: `[0,0,${w},${h}]`,
35
+ };
31
36
  };
32
37
  /** A wardley node ellipse positioned by top-left. */
33
38
  function node(kind, x, y, d = NODE_SIZE, fill = NODE_FILL, strokeWidth = NODE_STROKE_WIDTH,
@@ -178,7 +183,9 @@ function market() {
178
183
  label: label(MARKET_SIZE + 8, 2, 'Market'),
179
184
  };
180
185
  }
181
- const single = (el) => ({ a: el });
186
+ const single = (el) => ({
187
+ a: el,
188
+ });
182
189
  const nodeWithLabel = (kind, d, fill, name) => ({
183
190
  n: node(kind, 0, 0, d, fill),
184
191
  l: label(d + 8, d / 2 - 13, name),
@@ -187,7 +194,12 @@ const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www
187
194
  const bgPreview = (extra = '') => `<svg ${ATTRS} fill="none"><path d="M22 12 V64 H120" stroke="#3b3d42" stroke-width="2"/><path d="M44 12 V64 M68 12 V64 M94 12 V64" stroke="#9aa0a6" stroke-width="0.8"/>${extra}</svg>`;
188
195
  const dotPreview = (fill, sw = 2) => `<svg ${ATTRS} fill="none"><circle cx="67" cy="40" r="13" fill="${fill}" stroke="#1f2328" stroke-width="${sw}"/></svg>`;
189
196
  function tpl(name, preview, elements) {
190
- return { name, type: 'template', preview, content: makeTemplateSnapshot(elements, name) };
197
+ return {
198
+ name,
199
+ type: 'template',
200
+ preview,
201
+ content: makeTemplateSnapshot(elements, name),
202
+ };
191
203
  }
192
204
  export const wardleyTemplateCategory = {
193
205
  name: 'Wardley',
@@ -125,7 +125,11 @@ function lbl(e, v, text, o = {}) {
125
125
  color: o.color ?? NODE_STROKE,
126
126
  fontFamily: FontFamily.Inter,
127
127
  fontSize: o.size ?? LABEL_FONT_SIZE,
128
- textAlign: align === 'right' ? TextAlign.Right : align === 'center' ? TextAlign.Center : TextAlign.Left,
128
+ textAlign: align === 'right'
129
+ ? TextAlign.Right
130
+ : align === 'center'
131
+ ? TextAlign.Center
132
+ : TextAlign.Left,
129
133
  xywh: `[${x},${cy + dy},${w},26]`,
130
134
  };
131
135
  }
@@ -222,7 +226,12 @@ function title(str) {
222
226
  const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
223
227
  const mapPreview = (extra) => `<svg ${ATTRS} fill="none"><path d="M22 12 V64 H120" stroke="#3b3d42" stroke-width="2"/>${extra}</svg>`;
224
228
  function tpl(name, preview, elements) {
225
- return { name, type: 'template', preview, content: makeTemplateSnapshot(elements, name) };
229
+ return {
230
+ name,
231
+ type: 'template',
232
+ preview,
233
+ content: makeTemplateSnapshot(elements, name),
234
+ };
226
235
  }
227
236
  function ann(e, v) {
228
237
  const cx = ex(e);
@@ -260,7 +269,11 @@ function teaShop() {
260
269
  annBox: panel(120, 200, 420, 64),
261
270
  annText: freeText(132, 208, 400, 'Annotations:\n1. Standardising power lets kettles evolve faster\n2. Hot water is obvious and well known', 13),
262
271
  business: stake(0.62, 0.93),
263
- businessL: lbl(0.62, 0.93, 'Business', { align: 'center', dy: -28, w: 120 }),
272
+ businessL: lbl(0.62, 0.93, 'Business', {
273
+ align: 'center',
274
+ dy: -28,
275
+ w: 120,
276
+ }),
264
277
  public: stake(0.78, 0.93),
265
278
  publicL: lbl(0.78, 0.93, 'Public', { align: 'center', dy: -28, w: 120 }),
266
279
  cupOfTea: comp(0.62, 0.74),
@@ -284,9 +297,16 @@ function teaShop() {
284
297
  // ABOVE the link it annotates, not across it. Written on the line it reads
285
298
  // as a label nobody can read — which is the finding W3 raises, and it was
286
299
  // raising it on the map that ships as the canonical example.
287
- limitedBy: lbl(0.56, 0.43, 'limited by', { align: 'center', w: 120, size: 13, dy: -34 }),
288
- ann1a: ann(0.5, 0.385), ann1t: annTxt(0.5, 0.385, '1'),
289
- ann2a: ann(0.84, 0.45), ann2t: annTxt(0.84, 0.45, '2'),
300
+ limitedBy: lbl(0.56, 0.43, 'limited by', {
301
+ align: 'center',
302
+ w: 120,
303
+ size: 13,
304
+ dy: -34,
305
+ }),
306
+ ann1a: ann(0.5, 0.385),
307
+ ann1t: annTxt(0.5, 0.385, '1'),
308
+ ann2a: ann(0.84, 0.45),
309
+ ann2t: annTxt(0.84, 0.45, '2'),
290
310
  l1: link('business', 'cupOfTea'),
291
311
  l2: link('public', 'cupOfTea'),
292
312
  l3: link('cupOfTea', 'cup'),
@@ -187,7 +187,9 @@ export const wardleyToolbarConfig = {
187
187
  if (!bg)
188
188
  return;
189
189
  createWardleyLegend(ctx.std, bg);
190
- ctx.std.getOptional(TelemetryProvider)?.track('FrameworkLegendCreated', {
190
+ ctx.std
191
+ .getOptional(TelemetryProvider)
192
+ ?.track('FrameworkLegendCreated', {
191
193
  framework: 'wardley',
192
194
  element: 'legend',
193
195
  page: 'whiteboard editor',
@@ -24,7 +24,27 @@ export declare const wardleyMethodIcon: import("lit-html").TemplateResult<2>;
24
24
  export declare const wardleyLegendIcon: import("lit-html").TemplateResult<2>;
25
25
  /** Opportunity gradient background: axes + green differential hump + red operational bump. */
26
26
  export declare const wardleyOpportunityIcon: import("lit-html").TemplateResult<2>;
27
+ /**
28
+ * Import an SVG sketch — a framed picture with an arrow coming IN.
29
+ *
30
+ * Deliberately the same glyph BPMN gives the same command: the two wrap ONE
31
+ * parser and make one promise, and a reader who has met one should recognise
32
+ * the other without reading its label.
33
+ */
34
+ export declare const wardleyImportSvgIcon: import("lit-html").TemplateResult<2>;
27
35
  /** Benefit/Investment gradient background: axes + zero line + green J-curve. */
28
36
  export declare const wardleyBenefitIcon: import("lit-html").TemplateResult<2>;
29
37
  /** Evolution-gradient background (Wardley's S-curve presentation): grey at both edges fading to white center. */
30
38
  export declare const wardleyEvolutionGradientIcon: import("lit-html").TemplateResult<2>;
39
+ /**
40
+ * Export the map as an OWM document — a page with the map's own axes drawn on
41
+ * it, and an arrow going OUT.
42
+ *
43
+ * The page and the arrow are BPMN's, deliberately: interchange is a platform
44
+ * capability, and a reader who has met one framework's pair should recognise
45
+ * the next without reading its label. What differs is what is drawn ON the page
46
+ * — the value-chain corner, which is what makes it a map rather than a process.
47
+ */
48
+ export declare const wardleyExportOwmIcon: import("lit-html").TemplateResult<2>;
49
+ /** Import an OWM document — the same page and the same arrow, mirrored back IN. */
50
+ export declare const wardleyImportOwmIcon: import("lit-html").TemplateResult<2>;
@@ -114,6 +114,18 @@ export const wardleyOpportunityIcon = svg `<svg width="24" height="24" viewBox="
114
114
  <path d="M5 12 C6 7, 8.5 7, 9.5 11 C11 17, 14 19, 18 20" stroke="${GREEN}" stroke-width="1.7" fill="none" stroke-linecap="round"/>
115
115
  <path d="M14.5 19.5 C15.5 16.5, 17 16.5, 18 19" stroke="${RED}" stroke-width="1.7" fill="none" stroke-linecap="round"/>
116
116
  </svg>`;
117
+ /**
118
+ * Import an SVG sketch — a framed picture with an arrow coming IN.
119
+ *
120
+ * Deliberately the same glyph BPMN gives the same command: the two wrap ONE
121
+ * parser and make one promise, and a reader who has met one should recognise
122
+ * the other without reading its label.
123
+ */
124
+ export const wardleyImportSvgIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
125
+ <rect x="3" y="5" width="14" height="11" rx="1.5" stroke="currentColor" stroke-width="1.5"/>
126
+ <path d="M3.5 13 7 9.5l3 3 2-2 3.5 3.5" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/>
127
+ <path d="M19 14.5v6M16.5 17l2.5-2.5 2.5 2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
128
+ </svg>`;
117
129
  /** Benefit/Investment gradient background: axes + zero line + green J-curve. */
118
130
  export const wardleyBenefitIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
119
131
  <path d="M4 20V4M4 20h16" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/>
@@ -132,3 +144,25 @@ export const wardleyEvolutionGradientIcon = svg `<svg width="24" height="24" vie
132
144
  </defs>
133
145
  <rect x="4" y="4.5" width="16" height="15" rx="2" fill="url(#wardleyGreyU)" stroke="currentColor" stroke-width="1.2"/>
134
146
  </svg>`;
147
+ /**
148
+ * Export the map as an OWM document — a page with the map's own axes drawn on
149
+ * it, and an arrow going OUT.
150
+ *
151
+ * The page and the arrow are BPMN's, deliberately: interchange is a platform
152
+ * capability, and a reader who has met one framework's pair should recognise
153
+ * the next without reading its label. What differs is what is drawn ON the page
154
+ * — the value-chain corner, which is what makes it a map rather than a process.
155
+ */
156
+ export const wardleyExportOwmIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
157
+ <path d="M13.5 3.5H7a1.5 1.5 0 0 0-1.5 1.5v14A1.5 1.5 0 0 0 7 20.5h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
158
+ <path d="M13.5 3.5 18.5 8.5V12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
159
+ <path d="M8.5 7v6.5H13" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" opacity="0.6"/>
160
+ <path d="M16 20.5v-6M13.5 18l2.5 2.5 2.5-2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
161
+ </svg>`;
162
+ /** Import an OWM document — the same page and the same arrow, mirrored back IN. */
163
+ export const wardleyImportOwmIcon = svg `<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
164
+ <path d="M13.5 3.5H7a1.5 1.5 0 0 0-1.5 1.5v14A1.5 1.5 0 0 0 7 20.5h3" stroke="currentColor" stroke-width="1.5" stroke-linecap="round"/>
165
+ <path d="M13.5 3.5 18.5 8.5V12" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
166
+ <path d="M8.5 7v6.5H13" stroke="currentColor" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round" opacity="0.6"/>
167
+ <path d="M16 14.5v6M13.5 17l2.5-2.5 2.5 2.5" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
168
+ </svg>`;
@@ -3,6 +3,7 @@ import { html } from 'lit';
3
3
  export const wardleySeniorTool = SeniorToolExtension('wardley', ({ block }) => {
4
4
  return {
5
5
  name: 'Wardley map',
6
+ labelKey: 'com.labre.framework.wardley',
6
7
  content: html `<edgeless-wardley-senior-button
7
8
  .edgeless=${block}
8
9
  ></edgeless-wardley-senior-button>`,
@@ -1,5 +1,6 @@
1
1
  import { DefaultTool } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
3
+ import { translateKey } from '@formicoidea/labre-core/shared/services';
3
4
  import { EdgelessToolbarToolMixin } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
4
5
  import { SignalWatcher } from '@formicoidea/labre-core/global/lit';
5
6
  import { css, html, LitElement } from 'lit';
@@ -46,12 +47,15 @@ export class EdgelessWardleySeniorButton extends EdgelessToolbarToolMixin(Signal
46
47
  bottom: 0;
47
48
  width: 54px;
48
49
  height: 54px;
49
- transform: translateY(var(--y)) scale(var(--s)); /* base */
50
- translate: var(--active-x, 0) var(--active-y, 0); /* actif */
50
+ transform: translateY(var(--y)) scale(var(--s)); /* base */
51
+ translate: var(--active-x, 0) var(--active-y, 0); /* actif */
51
52
  rotate: var(--active-r, -2deg);
52
- scale: var(--active-s, 1);
53
- transition: transform 0.3s ease, translate 0.3s ease,
54
- rotate 0.3s ease, scale 0.3s ease;
53
+ scale: var(--active-s, 1);
54
+ transition:
55
+ transform 0.3s ease,
56
+ translate 0.3s ease,
57
+ rotate 0.3s ease,
58
+ scale 0.3s ease;
55
59
  }
56
60
  .wardley-card svg {
57
61
  display: block;
@@ -79,7 +83,9 @@ export class EdgelessWardleySeniorButton extends EdgelessToolbarToolMixin(Signal
79
83
  render() {
80
84
  return html `<edgeless-toolbar-button
81
85
  class="wardley-button"
82
- .tooltip=${this.popper ? '' : 'Wardley map'}
86
+ .tooltip=${this.popper
87
+ ? ''
88
+ : translateKey(this.edgeless.std, 'com.labre.framework.wardley', 'Wardley map')}
83
89
  .tooltipOffset=${4}
84
90
  .active=${!!this.popper}
85
91
  @click=${this._toggleMenu}
package/dist/view.js CHANGED
@@ -1,4 +1,4 @@
1
- import { ReadingProfileExtension, tagsToolbarConfig, validationToolbarConfig, QualityNudgeExtension, ValidationProfileExtension, ValidationRuleExtension, } from '@formicoidea/labre-core/blocks/surface';
1
+ import { InterchangeExtension, ReadingProfileExtension, tagsToolbarConfig, validationToolbarConfig, QualityNudgeExtension, ValidationProfileExtension, ValidationRuleExtension, } from '@formicoidea/labre-core/blocks/surface';
2
2
  import { AuditCriterionExtension, ToolbarModuleExtension, UniverseTagDefsExtension, } from '@formicoidea/labre-core/shared/services';
3
3
  import { ViewExtensionProvider, } from '@formicoidea/labre-core/ext-loader';
4
4
  import { extendTemplateCategory } from '@formicoidea/labre-core/gfx/template';
@@ -7,6 +7,7 @@ import { RoleVocabularyExtension } from '@formicoidea/labre-core/std/gfx';
7
7
  import { WARDLEY_AUDIT_CRITERIA } from './audit-criteria.js';
8
8
  import { wardleyCommandIcons, wardleyCommands } from './commands.js';
9
9
  import { effects } from './effects.js';
10
+ import { WARDLEY_INTERCHANGE } from './interchange.js';
10
11
  import { WARDLEY_TAG_DEFS } from './natures.js';
11
12
  import { WARDLEY_PROFILES } from './profiles.js';
12
13
  import { WARDLEY_NUDGES } from './nudges.js';
@@ -75,6 +76,15 @@ export class WardleyViewExtension extends ViewExtensionProvider {
75
76
  if (this.isEdgeless(context.scope)) {
76
77
  context.register(ValidationRuleExtension(WARDLEY_RULES));
77
78
  context.register(ValidationProfileExtension(WARDLEY_PROFILES));
79
+ // Both directions of the OWM DSL, and the SVG fallback that reads a
80
+ // picture of a map (`docs/adr/0012`, P1) — ONE registration, because the
81
+ // unit of declaration is the capability and `WARDLEY_INTERCHANGE` is the
82
+ // list of them. HERE, beside the rules, because offering to read or write
83
+ // a file is TOOLING: turning the wardley flag off removes the three
84
+ // commands and leaves every element a past import created — and every
85
+ // byte of `interchange` they carry — exactly where it is
86
+ // (`docs/adr/0009`).
87
+ context.register(InterchangeExtension(WARDLEY_INTERCHANGE));
78
88
  // Map quality (PF13.9): the four nudges the tool cannot judge, and
79
89
  // nothing else — the on-demand check-up Wardley used to expose beside
80
90
  // them was dropped on the PO recette of 02/08/2026. The engine keeps the
@@ -132,7 +142,8 @@ export class WardleyViewExtension extends ViewExtensionProvider {
132
142
  // the flag without either side naming the other.
133
143
  context.register(ReadingProfileExtension(WARDLEY_READING));
134
144
  context.register(wardleySeniorTool);
135
- // The 13 Wardley commands, ONE registration for both faces: the
145
+ // The Wardley commands thirteen artefacts plus the two directions of
146
+ // the OWM DSL — ONE registration for both faces: the
136
147
  // enumerable registry the sub-menu renders from, and — through
137
148
  // `toShortcutDescriptor` — the edgeless chords (w+c, w+l, ...).
138
149
  // Registering here inherits both the wardley flag gating and the
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@formicoidea/labre-framework-wardley",
3
3
  "description": "Labre wardley framework for @formicoidea/labre-core.",
4
- "version": "0.32.0",
4
+ "version": "0.33.0",
5
5
  "type": "module",
6
6
  "sideEffects": false,
7
7
  "author": "lajola",
@@ -27,7 +27,7 @@
27
27
  "dist"
28
28
  ],
29
29
  "dependencies": {
30
- "@formicoidea/labre-core": "0.32.0",
30
+ "@formicoidea/labre-core": "0.33.0",
31
31
  "lit": "^3.2.0"
32
32
  }
33
33
  }