@formicoidea/labre-framework-wardley 0.31.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.
Files changed (58) hide show
  1. package/dist/actions.d.ts +63 -20
  2. package/dist/actions.js +190 -57
  3. package/dist/audit-criteria.d.ts +31 -0
  4. package/dist/audit-criteria.js +90 -0
  5. package/dist/background.d.ts +14 -0
  6. package/dist/background.js +338 -0
  7. package/dist/commands.d.ts +9 -0
  8. package/dist/commands.js +352 -0
  9. package/dist/consts.d.ts +10 -4
  10. package/dist/consts.js +10 -9
  11. package/dist/descriptor.d.ts +8 -3
  12. package/dist/descriptor.js +6 -3
  13. package/dist/element-renderer.d.ts +8 -5
  14. package/dist/element-renderer.js +12 -147
  15. package/dist/element-view.d.ts +8 -4
  16. package/dist/element-view.js +30 -22
  17. package/dist/export.d.ts +211 -0
  18. package/dist/export.js +655 -0
  19. package/dist/gradient.d.ts +6 -11
  20. package/dist/gradient.js +59 -48
  21. package/dist/import.d.ts +116 -0
  22. package/dist/import.js +905 -0
  23. package/dist/index.d.ts +27 -1
  24. package/dist/index.js +31 -1
  25. package/dist/interchange.d.ts +80 -0
  26. package/dist/interchange.js +138 -0
  27. package/dist/legend.js +8 -0
  28. package/dist/natures.d.ts +50 -0
  29. package/dist/natures.js +93 -0
  30. package/dist/node/node-renderer.js +1 -1
  31. package/dist/nudges.d.ts +41 -0
  32. package/dist/nudges.js +69 -0
  33. package/dist/profiles.d.ts +2 -0
  34. package/dist/profiles.js +87 -0
  35. package/dist/reading.d.ts +3 -0
  36. package/dist/reading.js +129 -0
  37. package/dist/roles.d.ts +50 -0
  38. package/dist/roles.js +132 -0
  39. package/dist/rules.d.ts +2 -0
  40. package/dist/rules.js +286 -0
  41. package/dist/templates/index.js +66 -10
  42. package/dist/templates/maps.js +146 -22
  43. package/dist/toolbar/config.js +3 -1
  44. package/dist/toolbar/icons.d.ts +20 -0
  45. package/dist/toolbar/icons.js +34 -0
  46. package/dist/toolbar/senior-tool.js +1 -0
  47. package/dist/toolbar/wardley-menu.d.ts +8 -15
  48. package/dist/toolbar/wardley-menu.js +8 -136
  49. package/dist/toolbar/wardley-senior-button.js +12 -6
  50. package/dist/translations.d.ts +16 -0
  51. package/dist/translations.js +24 -0
  52. package/dist/view.d.ts +17 -0
  53. package/dist/view.js +127 -13
  54. package/package.json +2 -2
  55. package/dist/label-layout.d.ts +0 -20
  56. package/dist/label-layout.js +0 -72
  57. package/dist/shortcuts.d.ts +0 -2
  58. package/dist/shortcuts.js +0 -37
package/dist/gradient.js CHANGED
@@ -5,6 +5,11 @@
5
5
  * its own min and max — i.e. the gradient is strongest where the curve peaks and
6
6
  * fades to nothing at its minimum. Validated against the reference images at
7
7
  * `../wardley-mockups/gradient-backgrounds.html`.
8
+ *
9
+ * The curves are TABULATED ONCE, here, at module load: what the declaration
10
+ * ships — and what the primitive paints — is a table of `[offset, alpha]`
11
+ * stops, not a function. Nothing is evaluated at paint time, and the wash is
12
+ * data like the rest of the declaration (PF2.1).
8
13
  */
9
14
  const bell = (x, mu, s) => Math.exp(-0.5 * ((x - mu) / s) ** 2);
10
15
  const asym = (x, mu, sL, sR) => Math.exp(-0.5 * ((x - mu) / (x < mu ? sL : sR)) ** 2);
@@ -40,7 +45,7 @@ const RG = rangeOf(fDiff, DIFF_DOM[0], DIFF_DOM[1]);
40
45
  const RR = rangeOf(fOper, OPER_DOM[0], OPER_DOM[1]);
41
46
  const RB = rangeOf(fBen, 0, 1);
42
47
  const clamp01 = (v) => Math.max(0, Math.min(1, v));
43
- const norm = (v, lo, hi) => (hi > lo ? (v - lo) / (hi - lo) : 0);
48
+ const norm = (v, lo, hi) => hi > lo ? (v - lo) / (hi - lo) : 0;
44
49
  export const GRADIENT_GREEN = '#1f9e4d';
45
50
  export const GRADIENT_RED = '#d6455d';
46
51
  const GRADIENT_GREY = '#7c8389';
@@ -48,62 +53,68 @@ const GRADIENT_GREY = '#7c8389';
48
53
  const GRADIENT_MAX_OPACITY = 0.45;
49
54
  /** Peak opacity for the grey evolution-gradient variant. */
50
55
  const GREY_MAX_OPACITY = 0.38;
51
- function rgba(hex, alpha) {
52
- const r = parseInt(hex.slice(1, 3), 16);
53
- const g = parseInt(hex.slice(3, 5), 16);
54
- const b = parseInt(hex.slice(5, 7), 16);
55
- return `rgba(${r},${g},${b},${alpha})`;
56
- }
57
56
  /**
58
- * Add stops to a horizontal gradient (offset 0..1 spanning the plot width) for
59
- * a function-driven opacity profile within [x0, x1] (zero outside).
57
+ * Tabulate one opacity profile as gradient stops spanning the plot width:
58
+ * 49 samples inside [x0, x1], bracketed by a zero stop wherever the profile
59
+ * does not reach the edge of the plot.
60
60
  */
61
- function addStops(grad, hex, opacityFn, x0, x1, maxOp = GRADIENT_MAX_OPACITY) {
61
+ function stopTable(opacityFn, x0, x1, maxOp = GRADIENT_MAX_OPACITY) {
62
62
  const eps = 0.001;
63
+ const stops = [];
63
64
  if (x0 > eps)
64
- grad.addColorStop(Math.max(0, x0 - eps), rgba(hex, 0));
65
+ stops.push([Math.max(0, x0 - eps), 0]);
65
66
  const N = 48;
66
67
  for (let i = 0; i <= N; i++) {
67
68
  const x = x0 + ((x1 - x0) * i) / N;
68
- grad.addColorStop(clamp01(x), rgba(hex, clamp01(opacityFn(x)) * maxOp));
69
+ stops.push([clamp01(x), clamp01(opacityFn(x)) * maxOp]);
69
70
  }
70
71
  if (x1 < 1 - eps)
71
- grad.addColorStop(Math.min(1, x1 + eps), rgba(hex, 0));
72
+ stops.push([Math.min(1, x1 + eps), 0]);
73
+ return stops;
72
74
  }
75
+ // benefit: green where the curve is positive, red where negative.
76
+ const BEN_MAX_POS = RB.hi;
77
+ const BEN_MAX_NEG = -RB.lo;
73
78
  /**
74
- * Paint the curve-driven gradient over the plot rectangle [px0,px1]×[py0,py1]
75
- * in element-local coordinates. `classic` paints nothing.
79
+ * The washes the Wardley declaration ships, in painting order. Only those whose
80
+ * `variants` name the background's current `variant` are painted, and only
81
+ * while `showGradient` is on — so `classic` paints none of them and the frame
82
+ * stays plain white, exactly as before.
76
83
  */
77
- export function paintGradientBackground(ctx, variant, px0, px1, py0, py1) {
78
- const w = px1 - px0;
79
- const h = py1 - py0;
80
- if (variant === 'evolution-gradient') {
81
- const grey = ctx.createLinearGradient(px0, 0, px1, 0);
82
- addStops(grey, GRADIENT_GREY, fGrey, 0, 1, GREY_MAX_OPACITY);
83
- ctx.fillStyle = grey;
84
- ctx.fillRect(px0, py0, w, h);
85
- return;
86
- }
87
- if (variant === 'opportunity') {
88
- const green = ctx.createLinearGradient(px0, 0, px1, 0);
89
- addStops(green, GRADIENT_GREEN, x => norm(fDiff(x), RG.lo, RG.hi), DIFF_DOM[0], DIFF_DOM[1]);
90
- ctx.fillStyle = green;
91
- ctx.fillRect(px0, py0, w, h);
92
- const red = ctx.createLinearGradient(px0, 0, px1, 0);
93
- addStops(red, GRADIENT_RED, x => norm(fOper(x), RR.lo, RR.hi), OPER_DOM[0], OPER_DOM[1]);
94
- ctx.fillStyle = red;
95
- ctx.fillRect(px0, py0, w, h);
96
- return;
97
- }
98
- // benefit: green where the curve is positive, red where negative.
99
- const maxPos = RB.hi;
100
- const maxNeg = -RB.lo;
101
- const green = ctx.createLinearGradient(px0, 0, px1, 0);
102
- addStops(green, GRADIENT_GREEN, x => Math.max(0, fBen(x)) / maxPos, 0, 1);
103
- ctx.fillStyle = green;
104
- ctx.fillRect(px0, py0, w, h);
105
- const red = ctx.createLinearGradient(px0, 0, px1, 0);
106
- addStops(red, GRADIENT_RED, x => Math.max(0, -fBen(x)) / maxNeg, 0, 1);
107
- ctx.fillStyle = red;
108
- ctx.fillRect(px0, py0, w, h);
109
- }
84
+ export const WARDLEY_WASHES = [
85
+ {
86
+ id: 'evolution-grey',
87
+ variants: ['evolution-gradient'],
88
+ visibleProp: 'showGradient',
89
+ color: GRADIENT_GREY,
90
+ stops: stopTable(fGrey, 0, 1, GREY_MAX_OPACITY),
91
+ },
92
+ {
93
+ id: 'opportunity-differential',
94
+ variants: ['opportunity'],
95
+ visibleProp: 'showGradient',
96
+ color: GRADIENT_GREEN,
97
+ stops: stopTable(x => norm(fDiff(x), RG.lo, RG.hi), DIFF_DOM[0], DIFF_DOM[1]),
98
+ },
99
+ {
100
+ id: 'opportunity-operational',
101
+ variants: ['opportunity'],
102
+ visibleProp: 'showGradient',
103
+ color: GRADIENT_RED,
104
+ stops: stopTable(x => norm(fOper(x), RR.lo, RR.hi), OPER_DOM[0], OPER_DOM[1]),
105
+ },
106
+ {
107
+ id: 'benefit-positive',
108
+ variants: ['benefit'],
109
+ visibleProp: 'showGradient',
110
+ color: GRADIENT_GREEN,
111
+ stops: stopTable(x => Math.max(0, fBen(x)) / BEN_MAX_POS, 0, 1),
112
+ },
113
+ {
114
+ id: 'benefit-investment',
115
+ variants: ['benefit'],
116
+ visibleProp: 'showGradient',
117
+ color: GRADIENT_RED,
118
+ stops: stopTable(x => Math.max(0, -fBen(x)) / BEN_MAX_NEG, 0, 1),
119
+ },
120
+ ];
@@ -0,0 +1,116 @@
1
+ import type { InterchangeImportContext, InterchangeImportResult } from '@formicoidea/labre-core/blocks/surface';
2
+ import { OWM_SCOPE, WARDLEY_OWM_FORMAT_ID } from './export.js';
3
+ /**
4
+ * An OnlineWardleyMaps (OWM) DSL document, read as a Wardley map — the inverse
5
+ * of `export.ts` on the vocabulary Labre draws, and an honest accounting of
6
+ * everything else (`docs/adr/0012`, D1–D6).
7
+ *
8
+ * ADR 0012 calls this row **the reference Wardley import**, and says why: the
9
+ * OWM DSL is the one Wardley vocabulary that is settled, so it is what a user
10
+ * should be pointed at while mermaid's Wardley diagram type is still
11
+ * experimental upstream.
12
+ *
13
+ * ## Pure by construction, like its mirror
14
+ *
15
+ * A string in, element PROPS out. No `BlockStdScope`, no surface, no DOM, no
16
+ * clock, no randomness — and, unlike `.bpmn`, no parser either: the DSL is
17
+ * line-based, so this reads lines. The caller does the writing (P3).
18
+ *
19
+ * ## What the caller owes, and it is one thing
20
+ *
21
+ * **OWM has no ids: the NAME is the identity.** So every element below carries
22
+ * the name it was declared under, verbatim, in `interchange.owm.id`, and a link
23
+ * arrives with `source` / `target` naming those — which is exactly the map
24
+ * `materializeInterchangeImport` folds the returned array into (D3). Nothing
25
+ * else is needed to finish the import, and the same fold is what makes
26
+ * `boardFromProps` in the spec suite a bridge rather than a mock.
27
+ *
28
+ * A name declared twice is imported twice — nothing is dropped — and every link
29
+ * naming it means the FIRST, which is both the materializer's rule and OWM's
30
+ * own. The report says so by name.
31
+ *
32
+ * ## Where the coordinates come from, and where they do not (D4)
33
+ *
34
+ * A `[visibility, evolution]` pair IS the authoritative position — that is D4's
35
+ * "a format that carries coordinates but no pixels" — so the reader projects it
36
+ * onto the plot of the map it lays down and re-layouts nothing. A statement
37
+ * that carries NO pair inverts the same rule rather than contradicting it:
38
+ * there is nothing to be authoritative, so the reader lays one out and SAYS SO,
39
+ * with an `invented-layout` note naming the artefact. **An invented axis is
40
+ * never presented as read from the file.** That is the whole of D4 for a
41
+ * coordinate format, and it is the case `anchor Client` — one line of the
42
+ * tea-shop corpus, and the shape half the maps in the wild are written in.
43
+ *
44
+ * ## Three states, and the middle one is where the file survives
45
+ *
46
+ * **Mapped** is what the pack draws: `component`, `anchor`, `market`,
47
+ * `ecosystem`, `pipeline`, `note`, `evolve`, `title`, and the `->` links.
48
+ * **Carried** is every other statement — `style`, `annotation`, `attitudes`
49
+ * (`pioneers` / `settlers` / `townplanners`), `submap`, `url`, `size`,
50
+ * `accelerator`, the axis-label overrides, the flow links (`+>`, `+<`, `+<>`,
51
+ * `+'…'>`), a link carrying a `;` context, and every `//` comment — kept
52
+ * verbatim in `interchange.owm.children['@document']` on the map's background
53
+ * element (D6) and written back, in order, at the end of the next export.
54
+ * **Quarantined** is empty, and that is a finding rather than an omission: see
55
+ * the loss table.
56
+ *
57
+ * A mapped line keeps its own tail too — `label [12, -8]`, `(build)`,
58
+ * `inertia`, a trailing comment — under `attrs['@self'].tail` on the artefact
59
+ * it belongs to, so the modifiers this pack does not draw come back on the
60
+ * line they were written on rather than at the bottom of the file.
61
+ *
62
+ * ## The loss table
63
+ *
64
+ * Every semantic capability owes one (ADR 0012), and this is the OWM DSL's.
65
+ * INVISIBLE is not LOST, and the distinction is the deliverable.
66
+ *
67
+ * | what | state | after a round trip |
68
+ * | ---------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
69
+ * | `component` / `anchor` / `market` / `ecosystem` and their coordinates | mapped | drawn, and written back from the drawing, to two decimals |
70
+ * | the NAME, which in this format is the identity | mapped | given back verbatim, quoted exactly as it needs to be — the fixed point |
71
+ * | `pipeline Name [m1, m2]` | mapped | drawn as the body + handle composite; `[m1, m2]` written back from the body's two edges |
72
+ * | `note Text [v, e]` | mapped | a free text on the map, written back from its centre |
73
+ * | `evolve X [-> Y] m` | mapped | an evolved twin at the same height plus a change arrow, written back from the arrow |
74
+ * | `A->B` links | mapped | a `wardley:dependency` connector, consumer → what it needs, never inverted |
75
+ * | `title` | mapped | consumed as the board's own name; written back from the board's name (see the two rows below) |
76
+ * | a mapped line's TAIL — `label [x, y]`, `(build)`, `inertia`, comments | carried | invisible on the canvas, re-appended to the very line it came off |
77
+ * | `style`, `annotation(s)`, `attitudes`, `submap`, `url`, `size`, `accelerator`, the axis-label overrides | carried | invisible on the canvas, re-emitted verbatim at the end of the file |
78
+ * | flow links (`+>`, `+<`, `+<>`, `+'…'>`) and a link with a `;` context | carried | not drawn — a flow is not a dependency — and re-emitted verbatim |
79
+ * | a `pipeline` with a `{ … }` body | carried | the block is kept whole and written back whole; the pack draws no pipeline children |
80
+ * | `//` comments | carried | re-emitted verbatim, at the end |
81
+ * | anything else the reader does not recognise | carried | re-emitted verbatim, at the end |
82
+ * | **nothing** | quarantined | every statement this format writes is a standalone sentence, so nothing carried can contradict the drawing (D5) |
83
+ * | a statement with NO coordinates (`anchor Client`) | **lost** | the reader places it at the reference default (0.9, 0.1) and says so; the export then writes the coordinates we invented, not the silence the file had |
84
+ * | a statement with MALFORMED coordinates | **lost** | same, and the report carries a `warning` as well as the `invented-layout` note |
85
+ * | a statement positioned on ONE axis (`[0.5]`, `[nonsense, 0.5]`) | **half mapped, half lost** | the axis the file gave is read and kept exactly; the other takes the reference default and gets its own `invented-layout` note naming WHICH axis was invented. The statement counts as `mapped` — it was positioned — and the export writes a full pair, so the axis the file left silent comes back as a number |
86
+ * | the file's own line ORDER and its blank lines | **lost** | the writer groups statements into sections. Nothing semantic depends on it, and carrying blank lines would make an untouched Labre file report a carried count |
87
+ * | a `pipeline`'s visibility | **lost** | OWM derives it from the component of the same name; the reader places the body under that component and the writer does not write it back |
88
+ * | an `evolve` twin drawn at a different height | **lost** | `evolve` moves along the evolution axis only; the export warns |
89
+ * | a Labre `method` node (build / buy / outsource) | **lost** | written as a plain component — OWM says a method with a decorator this writer cannot tell apart. The export warns |
90
+ * | the file's `title` | **carried**, and it WINS | kept under `attrs['@document'].title` and written back in preference to the name the caller passes — D3's precedence, the same one `interchange.<fmt>.id` has on every element. A board renamed in Labre therefore still exports under the title its file carried, and the export warns that it did; the caller's name is used only when the file carried none (and is still what the DOWNLOAD is called either way) |
91
+ * | surface identity across a re-import | **lost** | a new map beside the old one, never a merge |
92
+ * | a name whose whitespace matters (`Foo&nbsp;&nbsp;&nbsp;Bar`) | **round-trips here, at risk elsewhere** | this reader keeps a name VERBATIM and this writer gives it back verbatim, so Labre → Labre is exact. onlinewardleymaps does not: `normalizeComponentName` collapses whitespace runs before it matches a link end to a component, so a map that goes Labre → OWM → Labre may come back with its links dangling. Not sanitized here, because sanitizing would lose the author's name to protect another tool's matcher |
93
+ * | a top-level `market` / `ecosystem` statement | **round-trips here, refused elsewhere** | Labre reads and writes both. The reference `Converter` registers no strategy for either keyword, so such a line reaches `LinksExtractionStrategy`, has no `->` in it and is recorded as a PARSE ERROR — onlinewardleymaps does not merely fail to draw it. The interoperable spelling is a component carrying the decorator (`component Suppliers [0.3, 0.7] (market)`), which this pair already round-trips through the carried tail |
94
+ *
95
+ * `sourceVersion` reports the DIALECT, because the DSL declares no version: the
96
+ * OnlineWardleyMaps frontend's own extraction strategies, which is what this
97
+ * reader was written against. It reads `DSL (Labre)` for a file holding nothing
98
+ * this library does not itself write — the honest form of "a file we wrote",
99
+ * since the format has no marker to claim one with.
100
+ */
101
+ export { WARDLEY_OWM_FORMAT_ID, OWM_SCOPE };
102
+ /**
103
+ * OWM's own defaults for an axis a statement did not give
104
+ * (`extractLocation`, `constants/extractionFunctions.ts`). Used rather than a
105
+ * layout of our own, so an artefact this reader places without coordinates
106
+ * lands where the tool that wrote the file would have drawn it.
107
+ */
108
+ export declare const OWM_DEFAULT_VISIBILITY = 0.9;
109
+ export declare const OWM_DEFAULT_EVOLUTION = 0.1;
110
+ /**
111
+ * An OWM document as element props, plus what became of every line.
112
+ *
113
+ * @param source the file, verbatim.
114
+ * @param context the caller's name for it, used only if the file names nothing.
115
+ */
116
+ export declare function importWardleyOwm(source: string, context?: InterchangeImportContext): InterchangeImportResult;