@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.
- package/dist/actions.d.ts +63 -20
- package/dist/actions.js +190 -57
- package/dist/audit-criteria.d.ts +31 -0
- package/dist/audit-criteria.js +90 -0
- package/dist/background.d.ts +14 -0
- package/dist/background.js +338 -0
- package/dist/commands.d.ts +9 -0
- package/dist/commands.js +352 -0
- package/dist/consts.d.ts +10 -4
- package/dist/consts.js +10 -9
- package/dist/descriptor.d.ts +8 -3
- package/dist/descriptor.js +6 -3
- package/dist/element-renderer.d.ts +8 -5
- package/dist/element-renderer.js +12 -147
- package/dist/element-view.d.ts +8 -4
- package/dist/element-view.js +30 -22
- package/dist/export.d.ts +211 -0
- package/dist/export.js +655 -0
- package/dist/gradient.d.ts +6 -11
- package/dist/gradient.js +59 -48
- package/dist/import.d.ts +116 -0
- package/dist/import.js +905 -0
- package/dist/index.d.ts +27 -1
- package/dist/index.js +31 -1
- package/dist/interchange.d.ts +80 -0
- package/dist/interchange.js +138 -0
- package/dist/legend.js +8 -0
- package/dist/natures.d.ts +50 -0
- package/dist/natures.js +93 -0
- package/dist/node/node-renderer.js +1 -1
- package/dist/nudges.d.ts +41 -0
- package/dist/nudges.js +69 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +87 -0
- package/dist/reading.d.ts +3 -0
- package/dist/reading.js +129 -0
- package/dist/roles.d.ts +50 -0
- package/dist/roles.js +132 -0
- package/dist/rules.d.ts +2 -0
- package/dist/rules.js +286 -0
- package/dist/templates/index.js +66 -10
- package/dist/templates/maps.js +146 -22
- package/dist/toolbar/config.js +3 -1
- package/dist/toolbar/icons.d.ts +20 -0
- package/dist/toolbar/icons.js +34 -0
- package/dist/toolbar/senior-tool.js +1 -0
- package/dist/toolbar/wardley-menu.d.ts +8 -15
- package/dist/toolbar/wardley-menu.js +8 -136
- package/dist/toolbar/wardley-senior-button.js +12 -6
- package/dist/translations.d.ts +16 -0
- package/dist/translations.js +24 -0
- package/dist/view.d.ts +17 -0
- package/dist/view.js +127 -13
- package/package.json +2 -2
- package/dist/label-layout.d.ts +0 -20
- package/dist/label-layout.js +0 -72
- package/dist/shortcuts.d.ts +0 -2
- package/dist/shortcuts.js +0 -37
package/dist/templates/index.js
CHANGED
|
@@ -1,8 +1,15 @@
|
|
|
1
1
|
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
2
2
|
import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
3
3
|
import { REF_WIDTH } from '../consts.js';
|
|
4
|
+
import { WARDLEY_ROLE } from '../roles.js';
|
|
4
5
|
import { wardleyMaps } from './maps.js';
|
|
5
6
|
import { ECOSYSTEM_SIZE, HANDLE_SIZE, INERTIA_COLOR, INERTIA_SIZE, LABEL_FONT_SIZE, LINK_GREY, LINK_STROKE_WIDTH, MARKET_DOT_RING, MARKET_DOT_SIZE, MARKET_DOT_STROKE_WIDTH, MARKET_LINK_COLOR, MARKET_LINK_WIDTH, MARKET_SIZE, METHOD_FILL, METHOD_SIZE, NODE_FILL, NODE_SIZE, NODE_STROKE, NODE_STROKE_WIDTH, PIPELINE_FILL, PIPELINE_HEIGHT, PIPELINE_WIDTH, WARDLEY_RED, } from '../node/consts.js';
|
|
7
|
+
/**
|
|
8
|
+
* Verbatim copy of `BACKGROUND_VARIANT_DEFAULTS` in `../actions.ts` — see the
|
|
9
|
+
* TODO there: both copies write English prose into the document for the two
|
|
10
|
+
* value-chain variants, and both go away together when the variant becomes
|
|
11
|
+
* part of the declaration.
|
|
12
|
+
*/
|
|
6
13
|
const VARIANT_DEFAULTS = {
|
|
7
14
|
classic: {},
|
|
8
15
|
opportunity: {
|
|
@@ -20,13 +27,24 @@ const VARIANT_DEFAULTS = {
|
|
|
20
27
|
};
|
|
21
28
|
const bg = (variant, w = REF_WIDTH) => {
|
|
22
29
|
const h = Math.round((w * 9) / 16);
|
|
23
|
-
return {
|
|
30
|
+
return {
|
|
31
|
+
type: 'wardley',
|
|
32
|
+
variant,
|
|
33
|
+
...VARIANT_DEFAULTS[variant],
|
|
34
|
+
xywh: `[0,0,${w},${h}]`,
|
|
35
|
+
};
|
|
24
36
|
};
|
|
25
37
|
/** A wardley node ellipse positioned by top-left. */
|
|
26
|
-
function node(kind, x, y, d = NODE_SIZE, fill = NODE_FILL, strokeWidth = NODE_STROKE_WIDTH
|
|
38
|
+
function node(kind, x, y, d = NODE_SIZE, fill = NODE_FILL, strokeWidth = NODE_STROKE_WIDTH,
|
|
39
|
+
// Neutral for the market's inner dots, exactly as `createWardleyMarket` does:
|
|
40
|
+
// glyph wiring, not artefacts.
|
|
41
|
+
neutral = false) {
|
|
27
42
|
return {
|
|
28
43
|
type: 'wardleyNode',
|
|
29
44
|
kind,
|
|
45
|
+
// A template must produce the same typed artefacts as the toolbox, so a
|
|
46
|
+
// map started from a preset validates like a hand-drawn one.
|
|
47
|
+
role: neutral ? undefined : WARDLEY_ROLE[kind],
|
|
30
48
|
shapeType: 'ellipse',
|
|
31
49
|
filled: true,
|
|
32
50
|
fillColor: fill,
|
|
@@ -41,6 +59,7 @@ function label(x, y, str, align = 'left') {
|
|
|
41
59
|
return {
|
|
42
60
|
type: 'text',
|
|
43
61
|
text: surfaceText(str),
|
|
62
|
+
role: WARDLEY_ROLE.label,
|
|
44
63
|
color: NODE_STROKE,
|
|
45
64
|
fontFamily: FontFamily.Inter,
|
|
46
65
|
fontSize: LABEL_FONT_SIZE,
|
|
@@ -48,15 +67,35 @@ function label(x, y, str, align = 'left') {
|
|
|
48
67
|
xywh: `[${x},${y},140,26]`,
|
|
49
68
|
};
|
|
50
69
|
}
|
|
70
|
+
/**
|
|
71
|
+
* A Wardley connector for the palette samples.
|
|
72
|
+
*
|
|
73
|
+
* `evolution` is the ONE predicate that decides what the stroke means, here and
|
|
74
|
+
* in `maps.ts` alike — `docs/adr/0010` § Compatibility. The two kits used to
|
|
75
|
+
* test different things (`opts.red` here, `o.arrow` there), which was a style
|
|
76
|
+
* inconsistency until W4 started reading these edges and became a SEMANTIC one:
|
|
77
|
+
* what a rule governs must not depend on which authoring helper a template
|
|
78
|
+
* borrowed. Colour is a consequence of the meaning, never its source.
|
|
79
|
+
*
|
|
80
|
+
* `typed: false` drops the role altogether — for a sample that makes no claim
|
|
81
|
+
* about anything (see the "Link" swatch below).
|
|
82
|
+
*/
|
|
51
83
|
function connect(source, target, opts = {}) {
|
|
84
|
+
const role = opts.evolution
|
|
85
|
+
? WARDLEY_ROLE.changeArrow
|
|
86
|
+
: WARDLEY_ROLE.dependency;
|
|
52
87
|
return {
|
|
53
88
|
type: 'connector',
|
|
54
89
|
mode: ConnectorMode.Straight,
|
|
55
|
-
|
|
56
|
-
|
|
90
|
+
// A template must produce the same typed artefacts as the toolbox, or a map
|
|
91
|
+
// started from a preset would validate differently from a hand-drawn one.
|
|
92
|
+
// `undefined` writes nothing: a neutral stroke keeps no `role` key.
|
|
93
|
+
role: opts.typed === false ? undefined : role,
|
|
94
|
+
stroke: opts.evolution ? WARDLEY_RED : LINK_GREY,
|
|
95
|
+
strokeStyle: opts.evolution ? StrokeStyle.Dash : StrokeStyle.Solid,
|
|
57
96
|
strokeWidth: LINK_STROKE_WIDTH,
|
|
58
97
|
frontEndpointStyle: PointStyle.None,
|
|
59
|
-
rearEndpointStyle: opts.
|
|
98
|
+
rearEndpointStyle: opts.evolution ? PointStyle.Triangle : PointStyle.None,
|
|
60
99
|
source,
|
|
61
100
|
target,
|
|
62
101
|
};
|
|
@@ -64,6 +103,7 @@ function connect(source, target, opts = {}) {
|
|
|
64
103
|
const inertia = (x = 0, y = 0) => ({
|
|
65
104
|
type: 'shape',
|
|
66
105
|
shapeType: 'rect',
|
|
106
|
+
role: WARDLEY_ROLE.inertia,
|
|
67
107
|
filled: true,
|
|
68
108
|
fillColor: INERTIA_COLOR,
|
|
69
109
|
strokeColor: INERTIA_COLOR,
|
|
@@ -80,6 +120,7 @@ function pipeline() {
|
|
|
80
120
|
body: {
|
|
81
121
|
type: 'wardleyNode',
|
|
82
122
|
kind: 'pipeline',
|
|
123
|
+
role: WARDLEY_ROLE.pipeline,
|
|
83
124
|
shapeType: 'rect',
|
|
84
125
|
filled: true,
|
|
85
126
|
fillColor: PIPELINE_FILL,
|
|
@@ -93,6 +134,7 @@ function pipeline() {
|
|
|
93
134
|
handle: {
|
|
94
135
|
type: 'wardleyNode',
|
|
95
136
|
kind: 'handle',
|
|
137
|
+
role: WARDLEY_ROLE.handle,
|
|
96
138
|
shapeType: 'rect',
|
|
97
139
|
filled: true,
|
|
98
140
|
fillColor: NODE_FILL,
|
|
@@ -116,7 +158,9 @@ function market() {
|
|
|
116
158
|
[rho * sin60, rho / 2],
|
|
117
159
|
[-rho * sin60, rho / 2],
|
|
118
160
|
];
|
|
119
|
-
const dotAt = (vx, vy) => node('component', c + vx - MARKET_DOT_SIZE / 2, c + vy - MARKET_DOT_SIZE / 2, MARKET_DOT_SIZE, NODE_FILL, MARKET_DOT_STROKE_WIDTH);
|
|
161
|
+
const dotAt = (vx, vy) => node('component', c + vx - MARKET_DOT_SIZE / 2, c + vy - MARKET_DOT_SIZE / 2, MARKET_DOT_SIZE, NODE_FILL, MARKET_DOT_STROKE_WIDTH, true);
|
|
162
|
+
// Neutral on purpose: the triangle is the market glyph's own wiring, not a
|
|
163
|
+
// dependency the user drew (same rule as `createWardleyMarket`).
|
|
120
164
|
const tri = (a, b) => ({
|
|
121
165
|
type: 'connector',
|
|
122
166
|
mode: ConnectorMode.Straight,
|
|
@@ -139,7 +183,9 @@ function market() {
|
|
|
139
183
|
label: label(MARKET_SIZE + 8, 2, 'Market'),
|
|
140
184
|
};
|
|
141
185
|
}
|
|
142
|
-
const single = (el) => ({
|
|
186
|
+
const single = (el) => ({
|
|
187
|
+
a: el,
|
|
188
|
+
});
|
|
143
189
|
const nodeWithLabel = (kind, d, fill, name) => ({
|
|
144
190
|
n: node(kind, 0, 0, d, fill),
|
|
145
191
|
l: label(d + 8, d / 2 - 13, name),
|
|
@@ -148,7 +194,12 @@ const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www
|
|
|
148
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>`;
|
|
149
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>`;
|
|
150
196
|
function tpl(name, preview, elements) {
|
|
151
|
-
return {
|
|
197
|
+
return {
|
|
198
|
+
name,
|
|
199
|
+
type: 'template',
|
|
200
|
+
preview,
|
|
201
|
+
content: makeTemplateSnapshot(elements, name),
|
|
202
|
+
};
|
|
152
203
|
}
|
|
153
204
|
export const wardleyTemplateCategory = {
|
|
154
205
|
name: 'Wardley',
|
|
@@ -165,7 +216,12 @@ export const wardleyTemplateCategory = {
|
|
|
165
216
|
tpl('Pipeline', `<svg ${ATTRS} fill="none"><rect x="34" y="40" width="66" height="14" fill="#fff" stroke="#1f2328"/><rect x="60" y="33" width="14" height="14" fill="#fff" stroke="#1f2328"/></svg>`, pipeline()),
|
|
166
217
|
tpl('Market', `<svg ${ATTRS} fill="none"><circle cx="67" cy="40" r="16" fill="#fff" stroke="#1f2328"/><circle cx="67" cy="30" r="3.5" fill="#fff" stroke="#1f2328" stroke-width="1.5"/><circle cx="75" cy="46" r="3.5" fill="#fff" stroke="#1f2328" stroke-width="1.5"/><circle cx="59" cy="46" r="3.5" fill="#fff" stroke="#1f2328" stroke-width="1.5"/><path d="M67 30 L75 46 L59 46 Z" stroke="#1f2328" stroke-width="0.8" fill="none"/></svg>`, market()),
|
|
167
218
|
tpl('Inertia', `<svg ${ATTRS} fill="none"><rect x="63" y="22" width="8" height="36" fill="#1f2328"/></svg>`, single(inertia())),
|
|
168
|
-
|
|
169
|
-
|
|
219
|
+
// NEUTRAL on purpose (`docs/adr/0010` § Compatibility). This is a horizontal
|
|
220
|
+
// stroke bound to nothing — a sample of a STYLE, in a palette. It was typed
|
|
221
|
+
// `wardley:dependency` only because the helper defaults to that role, and a
|
|
222
|
+
// sample of a stroke makes no claim about who depends on whom. Same call the
|
|
223
|
+
// market glyph's own wiring already makes.
|
|
224
|
+
tpl('Link', `<svg ${ATTRS} fill="none"><path d="M24 40 H110" stroke="#666" stroke-width="2.4"/></svg>`, single(connect({ position: [0, 0] }, { position: [160, 0] }, { typed: false }))),
|
|
225
|
+
tpl('Evolution arrow', `<svg ${ATTRS} fill="none"><path d="M24 40 H100" stroke="#d6455d" stroke-width="2.4" stroke-dasharray="6 4"/><path d="M98 33 L112 40 L98 47 Z" fill="#d6455d"/></svg>`, single(connect({ position: [0, 0] }, { position: [160, 0] }, { evolution: true, typed: false }))),
|
|
170
226
|
],
|
|
171
227
|
};
|
package/dist/templates/maps.js
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { makeTemplateSnapshot, surfaceText, } from '@formicoidea/labre-core/gfx/template';
|
|
2
2
|
import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
3
3
|
import { INERTIA_COLOR, LABEL_FONT_SIZE, LINK_GREY, LINK_STROKE_WIDTH, NODE_FILL, NODE_SIZE, NODE_STROKE, WARDLEY_RED, } from '../node/consts.js';
|
|
4
|
+
import { backgroundPlot, backgroundZoneBoundaries, } from '@formicoidea/labre-core/blocks/surface';
|
|
5
|
+
import { WARDLEY_BACKGROUND } from '../background.js';
|
|
6
|
+
import { WARDLEY_ROLE } from '../roles.js';
|
|
4
7
|
/**
|
|
5
8
|
* Authoring kit for canonical Wardley maps. Positions are given as
|
|
6
9
|
* (evolution 0..1, value 0..1) and mapped into the plot interior of a fixed
|
|
@@ -11,19 +14,69 @@ import { INERTIA_COLOR, LABEL_FONT_SIZE, LINK_GREY, LINK_STROKE_WIDTH, NODE_FILL
|
|
|
11
14
|
* arrow is the red dashed connector. Legends are produced by the editor's
|
|
12
15
|
* auto-legend action rather than baked into the template.
|
|
13
16
|
*/
|
|
14
|
-
const W =
|
|
15
|
-
const H =
|
|
16
|
-
|
|
17
|
+
const W = WARDLEY_BACKGROUND.geometry.width;
|
|
18
|
+
const H = WARDLEY_BACKGROUND.geometry.height;
|
|
19
|
+
/**
|
|
20
|
+
* The plot these templates lay their nodes out in — THE DECLARATION'S, read off
|
|
21
|
+
* `geometry.margin` rather than copied.
|
|
22
|
+
*
|
|
23
|
+
* It used to be a hand-written `{x:70, y:56, w:1470, h:786}`, inset further than
|
|
24
|
+
* the drawn plot (`x 40 → 1570, y 30 → 862`), with a comment calling the drift
|
|
25
|
+
* "harmless (everything lands inside the map) but not the same number".
|
|
26
|
+
*
|
|
27
|
+
* It stopped being harmless the moment a RULE measured against the plot. W2
|
|
28
|
+
* asks whether an inertia bar sits on a declared phase transition, and a
|
|
29
|
+
* transition is a ratio OF THE PLOT: two plots means an evolution of `0.7` in a
|
|
30
|
+
* template lands 25 units away from the `0.7` the background draws. The
|
|
31
|
+
* templates were laid out in one frame of reference and judged in another.
|
|
32
|
+
*
|
|
33
|
+
* Deriving it removes the possibility. The nodes of every preset move by a few
|
|
34
|
+
* units, which is a visual change to FACTORY CONTENT — acceptable, and the
|
|
35
|
+
* reason this could be fixed at the source rather than worked around: no user
|
|
36
|
+
* document is touched, because a template is data we ship, not data they wrote.
|
|
37
|
+
*/
|
|
38
|
+
const PLOT = backgroundPlot(WARDLEY_BACKGROUND, W, H);
|
|
39
|
+
const PL = { x: PLOT.x0, y: PLOT.y0, w: PLOT.width, h: PLOT.height };
|
|
17
40
|
const ex = (e) => PL.x + e * PL.w;
|
|
18
41
|
const vy = (v) => PL.y + (1 - v) * PL.h;
|
|
19
42
|
const D = NODE_SIZE; // 18
|
|
20
|
-
|
|
43
|
+
/** The evolution transitions, as plot ratios, straight from the declaration. */
|
|
44
|
+
const PHASES = backgroundZoneBoundaries(WARDLEY_BACKGROUND).x;
|
|
45
|
+
/**
|
|
46
|
+
* Where the segment `from → to` crosses the evolution transition at `PHASES[i]`,
|
|
47
|
+
* in (evolution, value) coordinates.
|
|
48
|
+
*
|
|
49
|
+
* This is where an inertia bar belongs: astride the boundary the thing refuses
|
|
50
|
+
* to cross — which is all W2 asks since the PO spelled it out (02/08/2026) — and
|
|
51
|
+
* on the dependency that would have to move, which the rule no longer demands
|
|
52
|
+
* and a well-drawn map still shows. Computed rather than eyeballed, so the
|
|
53
|
+
* symbol stays on both whatever the declaration says either of them is.
|
|
54
|
+
*/
|
|
55
|
+
function crossing(from, to, i) {
|
|
56
|
+
const at = PHASES[i];
|
|
57
|
+
const t = (at - from[0]) / (to[0] - from[0]);
|
|
58
|
+
return [at, from[1] + t * (to[1] - from[1])];
|
|
59
|
+
}
|
|
60
|
+
// The map carries `wardley:map`: rules position artefacts against the ROLE, so
|
|
61
|
+
// a templated map is a first-class frame like a hand-drawn one — and since
|
|
62
|
+
// PF13.4 every artefact these presets lay on it (nodes, labels, links, change
|
|
63
|
+
// arrows, inertia bars) carries the same role the toolbox writes.
|
|
64
|
+
const bg = (variant = 'classic') => ({
|
|
65
|
+
type: WARDLEY_BACKGROUND.type,
|
|
66
|
+
role: WARDLEY_BACKGROUND.role,
|
|
67
|
+
resizeEnabled: WARDLEY_BACKGROUND.geometry.resizable,
|
|
68
|
+
variant,
|
|
69
|
+
xywh: `[0,0,${W},${H}]`,
|
|
70
|
+
});
|
|
21
71
|
function dot(e, v, sw, stroke = NODE_STROKE, fill = NODE_FILL) {
|
|
22
72
|
const cx = ex(e);
|
|
23
73
|
const cy = vy(v);
|
|
24
74
|
return {
|
|
25
75
|
type: 'wardleyNode',
|
|
26
76
|
kind: 'component',
|
|
77
|
+
// Templates carry the same semantic roles as the toolbox, so a map started
|
|
78
|
+
// from a preset validates exactly like a hand-drawn one.
|
|
79
|
+
role: WARDLEY_ROLE.component,
|
|
27
80
|
shapeType: 'ellipse',
|
|
28
81
|
filled: true,
|
|
29
82
|
fillColor: fill,
|
|
@@ -43,6 +96,7 @@ function stake(e, v) {
|
|
|
43
96
|
return {
|
|
44
97
|
type: 'wardleyNode',
|
|
45
98
|
kind: 'anchor',
|
|
99
|
+
role: WARDLEY_ROLE.anchor,
|
|
46
100
|
shapeType: 'ellipse',
|
|
47
101
|
filled: true,
|
|
48
102
|
fillColor: NODE_FILL,
|
|
@@ -64,22 +118,53 @@ function lbl(e, v, text, o = {}) {
|
|
|
64
118
|
return {
|
|
65
119
|
type: 'text',
|
|
66
120
|
text: surfaceText(text),
|
|
121
|
+
// The NAME of an artefact, so it carries the label role W3 is written on.
|
|
122
|
+
// The free texts these presets also use for notes and legends stay neutral:
|
|
123
|
+
// they name nothing and nothing measures them.
|
|
124
|
+
role: WARDLEY_ROLE.label,
|
|
67
125
|
color: o.color ?? NODE_STROKE,
|
|
68
126
|
fontFamily: FontFamily.Inter,
|
|
69
127
|
fontSize: o.size ?? LABEL_FONT_SIZE,
|
|
70
|
-
textAlign: align === 'right'
|
|
128
|
+
textAlign: align === 'right'
|
|
129
|
+
? TextAlign.Right
|
|
130
|
+
: align === 'center'
|
|
131
|
+
? TextAlign.Center
|
|
132
|
+
: TextAlign.Left,
|
|
71
133
|
xywh: `[${x},${cy + dy},${w},26]`,
|
|
72
134
|
};
|
|
73
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* A link between two nodes of a shipped map.
|
|
138
|
+
*
|
|
139
|
+
* `evolution` is the ONE predicate that decides what the stroke MEANS — the
|
|
140
|
+
* same flag `templates/index.ts` reads, aligned by `docs/adr/0010`
|
|
141
|
+
* § Compatibility. It used to be `arrow` here and `red` there, so a red SOLID
|
|
142
|
+
* Kodak link was a typed dependency while a red sample in the palette was not:
|
|
143
|
+
* two answers to "is this a dependency?" in one framework, in neighbouring
|
|
144
|
+
* files. Harmless as a style inconsistency, semantic the moment W4 reads these
|
|
145
|
+
* edges.
|
|
146
|
+
*
|
|
147
|
+
* `red` stays, and stays orthogonal: it colours a DEPENDENCY red (Kodak's
|
|
148
|
+
* future chain) without changing what it is. Colour is never what decides a
|
|
149
|
+
* relation's type.
|
|
150
|
+
*
|
|
151
|
+
* The direction is meaning, not decoration: `a` is the consumer, `b` is what it
|
|
152
|
+
* needs, and every one of the twelve links these presets ship respects it
|
|
153
|
+
* (a corpus test walks them and fails loudly the day one does not).
|
|
154
|
+
*/
|
|
74
155
|
function link(a, b, o = {}) {
|
|
75
156
|
return {
|
|
76
157
|
type: 'connector',
|
|
77
158
|
mode: ConnectorMode.Straight,
|
|
78
|
-
|
|
79
|
-
|
|
159
|
+
// An evolution arrow is a movement annotation, not a dependency — same
|
|
160
|
+
// split as the two Wardley connector tools, and since PF13.4 both sides of
|
|
161
|
+
// that split carry a role.
|
|
162
|
+
role: o.evolution ? WARDLEY_ROLE.changeArrow : WARDLEY_ROLE.dependency,
|
|
163
|
+
stroke: o.red || o.evolution ? WARDLEY_RED : LINK_GREY,
|
|
164
|
+
strokeStyle: o.evolution ? StrokeStyle.Dash : StrokeStyle.Solid,
|
|
80
165
|
strokeWidth: LINK_STROKE_WIDTH,
|
|
81
166
|
frontEndpointStyle: PointStyle.None,
|
|
82
|
-
rearEndpointStyle: o.
|
|
167
|
+
rearEndpointStyle: o.evolution ? PointStyle.Triangle : PointStyle.None,
|
|
83
168
|
source: { id: a },
|
|
84
169
|
target: { id: b },
|
|
85
170
|
};
|
|
@@ -90,6 +175,7 @@ function inertia(e, v) {
|
|
|
90
175
|
return {
|
|
91
176
|
type: 'shape',
|
|
92
177
|
shapeType: 'rect',
|
|
178
|
+
role: WARDLEY_ROLE.inertia,
|
|
93
179
|
filled: true,
|
|
94
180
|
fillColor: INERTIA_COLOR,
|
|
95
181
|
strokeColor: INERTIA_COLOR,
|
|
@@ -140,7 +226,12 @@ function title(str) {
|
|
|
140
226
|
const ATTRS = 'width="100%" height="100%" viewBox="0 0 135 80" xmlns="http://www.w3.org/2000/svg"';
|
|
141
227
|
const mapPreview = (extra) => `<svg ${ATTRS} fill="none"><path d="M22 12 V64 H120" stroke="#3b3d42" stroke-width="2"/>${extra}</svg>`;
|
|
142
228
|
function tpl(name, preview, elements) {
|
|
143
|
-
return {
|
|
229
|
+
return {
|
|
230
|
+
name,
|
|
231
|
+
type: 'template',
|
|
232
|
+
preview,
|
|
233
|
+
content: makeTemplateSnapshot(elements, name),
|
|
234
|
+
};
|
|
144
235
|
}
|
|
145
236
|
function ann(e, v) {
|
|
146
237
|
const cx = ex(e);
|
|
@@ -178,7 +269,11 @@ function teaShop() {
|
|
|
178
269
|
annBox: panel(120, 200, 420, 64),
|
|
179
270
|
annText: freeText(132, 208, 400, 'Annotations:\n1. Standardising power lets kettles evolve faster\n2. Hot water is obvious and well known', 13),
|
|
180
271
|
business: stake(0.62, 0.93),
|
|
181
|
-
businessL: lbl(0.62, 0.93, 'Business', {
|
|
272
|
+
businessL: lbl(0.62, 0.93, 'Business', {
|
|
273
|
+
align: 'center',
|
|
274
|
+
dy: -28,
|
|
275
|
+
w: 120,
|
|
276
|
+
}),
|
|
182
277
|
public: stake(0.78, 0.93),
|
|
183
278
|
publicL: lbl(0.78, 0.93, 'Public', { align: 'center', dy: -28, w: 120 }),
|
|
184
279
|
cupOfTea: comp(0.62, 0.74),
|
|
@@ -199,9 +294,19 @@ function teaShop() {
|
|
|
199
294
|
powerL: lbl(0.7, 0.1, 'Power', { align: 'right', dy: 6 }),
|
|
200
295
|
powerFut: future(0.88, 0.1),
|
|
201
296
|
powerFutL: lbl(0.88, 0.1, 'Power'),
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
297
|
+
// ABOVE the link it annotates, not across it. Written on the line it reads
|
|
298
|
+
// as a label nobody can read — which is the finding W3 raises, and it was
|
|
299
|
+
// raising it on the map that ships as the canonical example.
|
|
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'),
|
|
205
310
|
l1: link('business', 'cupOfTea'),
|
|
206
311
|
l2: link('public', 'cupOfTea'),
|
|
207
312
|
l3: link('cupOfTea', 'cup'),
|
|
@@ -210,34 +315,53 @@ function teaShop() {
|
|
|
210
315
|
l6: link('hotWater', 'water'),
|
|
211
316
|
l7: link('hotWater', 'kettle'),
|
|
212
317
|
l8: link('kettle', 'power'),
|
|
213
|
-
a1: link('kettle', 'electric', {
|
|
214
|
-
a2: link('power', 'powerFut', {
|
|
318
|
+
a1: link('kettle', 'electric', { evolution: true }),
|
|
319
|
+
a2: link('power', 'powerFut', { evolution: true }),
|
|
215
320
|
};
|
|
216
321
|
}
|
|
217
322
|
// ── Kodak inertia (2005) ──────────────────────────────────────────────
|
|
218
323
|
function kodak() {
|
|
324
|
+
// The future dependency `capture → storage` is the movement Kodak resisted,
|
|
325
|
+
// and the inertia bar belongs where that dependency crosses into commodity —
|
|
326
|
+
// the boundary the capability refused to cross. It used to sit 105 units away
|
|
327
|
+
// from any transition: the template named after inertia was the
|
|
328
|
+
// counter-example to the inertia rule. Computed from the two node positions
|
|
329
|
+
// and the declared transitions, so it cannot drift again — and the bar comes
|
|
330
|
+
// out centred on the divider, which is exactly what W2 asks of it now that
|
|
331
|
+
// "astride the transition" is the whole of the rule.
|
|
332
|
+
const CAPTURE = [0.53, 0.8];
|
|
333
|
+
const STORAGE = [0.84, 0.4];
|
|
334
|
+
const [barE, barV] = crossing(CAPTURE, STORAGE, 2);
|
|
219
335
|
return {
|
|
220
336
|
bg: bg(),
|
|
221
337
|
title: title("Wardley map of Kodak's 2005 inertia to digital"),
|
|
222
338
|
user: stake(0.54, 0.92),
|
|
223
339
|
userL: lbl(0.54, 0.92, 'User'),
|
|
224
|
-
capture: dot(0
|
|
225
|
-
|
|
340
|
+
capture: dot(CAPTURE[0], CAPTURE[1], 3),
|
|
341
|
+
// To the LEFT, like the other two capability names: to the right of this
|
|
342
|
+
// node runs the future dependency towards digital storage, and a name
|
|
343
|
+
// written across the line that carries the whole argument is exactly the
|
|
344
|
+
// case W3 exists for.
|
|
345
|
+
captureL: lbl(CAPTURE[0], CAPTURE[1], 'Capture a moment', {
|
|
346
|
+
align: 'right',
|
|
347
|
+
}),
|
|
226
348
|
film: comp(0.52, 0.62),
|
|
227
349
|
filmL: lbl(0.52, 0.62, 'Film camera', { align: 'right' }),
|
|
228
350
|
digital: future(0.74, 0.62),
|
|
229
351
|
digitalL: lbl(0.74, 0.62, 'Digital camera', { color: WARDLEY_RED }),
|
|
230
352
|
roll: comp(0.52, 0.4),
|
|
231
353
|
rollL: lbl(0.52, 0.4, 'Photographic film', { align: 'right' }),
|
|
232
|
-
storage: future(0
|
|
233
|
-
storageL: lbl(0
|
|
234
|
-
|
|
354
|
+
storage: future(STORAGE[0], STORAGE[1]),
|
|
355
|
+
storageL: lbl(STORAGE[0], STORAGE[1], 'Digital storage', {
|
|
356
|
+
color: WARDLEY_RED,
|
|
357
|
+
}),
|
|
358
|
+
inertiaBar: inertia(barE, barV),
|
|
235
359
|
l1: link('user', 'capture'),
|
|
236
360
|
l2: link('capture', 'film'),
|
|
237
361
|
l3: link('film', 'roll'),
|
|
238
362
|
r1: link('capture', 'storage', { red: true }),
|
|
239
|
-
a1: link('film', 'digital', {
|
|
240
|
-
a2: link('roll', 'storage', {
|
|
363
|
+
a1: link('film', 'digital', { evolution: true }),
|
|
364
|
+
a2: link('roll', 'storage', { evolution: true }),
|
|
241
365
|
};
|
|
242
366
|
}
|
|
243
367
|
export const wardleyMaps = [
|
package/dist/toolbar/config.js
CHANGED
|
@@ -187,7 +187,9 @@ export const wardleyToolbarConfig = {
|
|
|
187
187
|
if (!bg)
|
|
188
188
|
return;
|
|
189
189
|
createWardleyLegend(ctx.std, bg);
|
|
190
|
-
ctx.std
|
|
190
|
+
ctx.std
|
|
191
|
+
.getOptional(TelemetryProvider)
|
|
192
|
+
?.track('FrameworkLegendCreated', {
|
|
191
193
|
framework: 'wardley',
|
|
192
194
|
element: 'legend',
|
|
193
195
|
page: 'whiteboard editor',
|
package/dist/toolbar/icons.d.ts
CHANGED
|
@@ -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>;
|
package/dist/toolbar/icons.js
CHANGED
|
@@ -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,20 +1,13 @@
|
|
|
1
1
|
import { EmptyTool } from '@formicoidea/labre-core/gfx/pointer';
|
|
2
|
-
import {
|
|
3
|
-
declare const EdgelessWardleyMenu_base: typeof LitElement & import("@formicoidea/labre-core/_pkgs/global/utils").Constructor<import("@formicoidea/labre-core/_pkgs/affine-widget-edgeless-toolbar").EdgelessToolbarToolClass>;
|
|
2
|
+
import { EdgelessCommandMenu } from '@formicoidea/labre-core/widgets/edgeless-toolbar';
|
|
4
3
|
/**
|
|
5
|
-
* The popover that opens above the toolbar for the Wardley toolbox.
|
|
6
|
-
*
|
|
7
|
-
*
|
|
4
|
+
* The popover that opens above the toolbar for the Wardley toolbox.
|
|
5
|
+
*
|
|
6
|
+
* Since PF3 it holds no artefact list of its own: {@link EdgelessCommandMenu}
|
|
7
|
+
* renders whatever `wardleyCommands` declares for the `senior-menu` surface, so
|
|
8
|
+
* the menu and the shortcut manifest can no longer drift (they did: 13 vs 7).
|
|
8
9
|
*/
|
|
9
|
-
export declare class EdgelessWardleyMenu extends
|
|
10
|
-
|
|
10
|
+
export declare class EdgelessWardleyMenu extends EdgelessCommandMenu {
|
|
11
|
+
protected owner: "wardley";
|
|
11
12
|
type: typeof EmptyTool;
|
|
12
|
-
private _createBackground;
|
|
13
|
-
private _createNode;
|
|
14
|
-
private readonly _createInertia;
|
|
15
|
-
private readonly _createPipeline;
|
|
16
|
-
private readonly _createMarket;
|
|
17
|
-
private _activateConnector;
|
|
18
|
-
render(): import("lit-html").TemplateResult<1>;
|
|
19
13
|
}
|
|
20
|
-
export {};
|