@formicoidea/labre-framework-wardley 0.60.0 → 1.0.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/commands.js +2 -2
- package/dist/dark-palette.d.ts +4 -1
- package/dist/dark-palette.js +7 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +9 -2
- package/dist/interchange-headless.d.ts +32 -6
- package/dist/interchange-headless.js +65 -6
- package/dist/interchange.d.ts +1 -1
- package/dist/interchange.js +8 -3
- package/dist/legend.d.ts +1 -0
- package/dist/legend.js +66 -1
- package/dist/method.d.ts +53 -0
- package/dist/method.js +88 -0
- package/dist/natures.d.ts +64 -2
- package/dist/natures.js +154 -6
- package/dist/node/consts.d.ts +133 -1
- package/dist/node/consts.js +219 -1
- package/dist/node/node-renderer.d.ts +2 -2
- package/dist/node/node-renderer.js +42 -1
- package/dist/renderer-json-export.d.ts +40 -0
- package/dist/renderer-json-export.js +669 -0
- package/dist/renderer-json-import.d.ts +71 -0
- package/dist/renderer-json-import.js +759 -0
- package/dist/renderer-json.d.ts +202 -0
- package/dist/renderer-json.js +366 -0
- package/dist/roles.d.ts +2 -2
- package/dist/roles.js +13 -0
- package/dist/toolbar/node-config.d.ts +0 -23
- package/dist/toolbar/node-config.js +1 -114
- package/dist/translations.js +6 -3
- package/package.json +2 -2
package/dist/natures.js
CHANGED
|
@@ -20,8 +20,8 @@ import { WARDLEY_ROLE } from './roles.js';
|
|
|
20
20
|
*
|
|
21
21
|
* ## Why it applies to `wardley:component` and not to `'*'`
|
|
22
22
|
*
|
|
23
|
-
* A nature qualifies a component. `market` and `
|
|
24
|
-
* `wardley:component`, so they get it for free through `roleIsA` — that is the
|
|
23
|
+
* A nature qualifies a component. `market`, `ecosystem` and `method`
|
|
24
|
+
* specialise `wardley:component`, so they get it for free through `roleIsA` — that is the
|
|
25
25
|
* entire reason role hierarchy is data. The `anchor` (a user / need) is
|
|
26
26
|
* deliberately NOT a child of `component` and is deliberately NOT qualified
|
|
27
27
|
* here: a need has no nature, it has a demand. The map itself, the change
|
|
@@ -78,6 +78,68 @@ export const WARDLEY_COMPETITION = {
|
|
|
78
78
|
struggle: `${WARDLEY_COMPETITION_TAG_ID}/struggle`,
|
|
79
79
|
establish: `${WARDLEY_COMPETITION_TAG_ID}/establish`,
|
|
80
80
|
};
|
|
81
|
+
/**
|
|
82
|
+
* The **method** tag — how a component + method is sourced, run or staffed.
|
|
83
|
+
*
|
|
84
|
+
* The third tag of this pack, written on `wardley:method` alone. Since that
|
|
85
|
+
* role specialises `wardley:component` (ADR 0046), a method node is offered
|
|
86
|
+
* the nature AND this tag in the same dropdown, while a plain component — the
|
|
87
|
+
* parent, which a child's tag never reaches — is offered the nature only.
|
|
88
|
+
*
|
|
89
|
+
* ## Nine values in one tag, not a category tag and a recommendation tag
|
|
90
|
+
*
|
|
91
|
+
* Simon Wardley's three method families each say one thing per evolution
|
|
92
|
+
* phase: a buying policy makes, buys or outsources; project management runs
|
|
93
|
+
* agile, lean or six sigma; attitudes are pioneers, settlers or town planners.
|
|
94
|
+
* The wardley-map-renderer JSON spells the same thing as a pair —
|
|
95
|
+
* `method: { category, recommendation }`, recommendation one of Uncharted /
|
|
96
|
+
* Transitional / Industrialized — and the two encodings are the same nine
|
|
97
|
+
* facts. One value per fact is the shape that admits nothing else: two tags
|
|
98
|
+
* would let a board say "Transitional" with no family, which nothing can paint
|
|
99
|
+
* and no reader can name. The pair stays one pure lookup away in `method.ts`,
|
|
100
|
+
* both directions.
|
|
101
|
+
*
|
|
102
|
+
* The family is still shown twice (PO decision, 2026-10-28): each value has its
|
|
103
|
+
* `group` — the family's title, which the picker prints as a sub-title above
|
|
104
|
+
* its three values — and each of the nine has its own prefabricated ring
|
|
105
|
+
* (`METHOD_STYLE`), which is how the map tells them apart. No family beyond
|
|
106
|
+
* these three: a category the renderer config invented is not a method here.
|
|
107
|
+
*
|
|
108
|
+
* `cardinality: 'single'` because the renderer gives a component ONE method,
|
|
109
|
+
* and because the drawing has one ring to show it with.
|
|
110
|
+
*
|
|
111
|
+
* Labels are the practitioners' own words — Make, Buy, Outsource in the PO's
|
|
112
|
+
* wording — short on purpose: the trigger of the dropdown prints the values
|
|
113
|
+
* in force ("Activity, Buy").
|
|
114
|
+
*/
|
|
115
|
+
export const WARDLEY_METHOD_TAG_ID = 'wardley:method';
|
|
116
|
+
/** The nine methods, as value ids. Ids are forever; a def is only deprecated. */
|
|
117
|
+
export const WARDLEY_METHOD = {
|
|
118
|
+
make: `${WARDLEY_METHOD_TAG_ID}/make`,
|
|
119
|
+
buy: `${WARDLEY_METHOD_TAG_ID}/buy`,
|
|
120
|
+
outsource: `${WARDLEY_METHOD_TAG_ID}/outsource`,
|
|
121
|
+
agile: `${WARDLEY_METHOD_TAG_ID}/agile`,
|
|
122
|
+
lean: `${WARDLEY_METHOD_TAG_ID}/lean`,
|
|
123
|
+
sixSigma: `${WARDLEY_METHOD_TAG_ID}/six-sigma`,
|
|
124
|
+
pioneers: `${WARDLEY_METHOD_TAG_ID}/pioneers`,
|
|
125
|
+
settlers: `${WARDLEY_METHOD_TAG_ID}/settlers`,
|
|
126
|
+
townPlanners: `${WARDLEY_METHOD_TAG_ID}/town-planners`,
|
|
127
|
+
};
|
|
128
|
+
/** The three families' titles: the picker's sub-titles and the legend's. */
|
|
129
|
+
export const WARDLEY_METHOD_GROUP = {
|
|
130
|
+
buyingPolicy: {
|
|
131
|
+
label: 'Buying policy',
|
|
132
|
+
labelKey: 'com.labre.wardley.tag.method.group.buying-policy',
|
|
133
|
+
},
|
|
134
|
+
projectManagement: {
|
|
135
|
+
label: 'Project management',
|
|
136
|
+
labelKey: 'com.labre.wardley.tag.method.group.project-management',
|
|
137
|
+
},
|
|
138
|
+
attitudes: {
|
|
139
|
+
label: 'Attitudes',
|
|
140
|
+
labelKey: 'com.labre.wardley.tag.method.group.attitudes',
|
|
141
|
+
},
|
|
142
|
+
};
|
|
81
143
|
export const WARDLEY_TAG_DEFS = {
|
|
82
144
|
formatVersion: 1,
|
|
83
145
|
// The id of this PACK, not of the framework: several packs may extend
|
|
@@ -96,12 +158,13 @@ export const WARDLEY_TAG_DEFS = {
|
|
|
96
158
|
descriptionKey: 'com.labre.wardley.tag.nature.description',
|
|
97
159
|
cardinality: 'single',
|
|
98
160
|
appliesTo: [WARDLEY_ROLE.component],
|
|
99
|
-
// No `order`, on
|
|
161
|
+
// No `order`, on any tag of this pack: an absent order sorts by SEED
|
|
100
162
|
// order, which is what puts the library's pack ahead of a client's
|
|
101
163
|
// extension without either having to know about the other. A number here
|
|
102
|
-
// would be a claim about packs that do not exist yet
|
|
103
|
-
//
|
|
104
|
-
//
|
|
164
|
+
// would be a claim about packs that do not exist yet. Seed order is also
|
|
165
|
+
// the one dropdown that holds two of them — a method node's — right:
|
|
166
|
+
// what the component IS (this tag) reads before how it is sourced
|
|
167
|
+
// (`wardley:method`, last).
|
|
105
168
|
values: [
|
|
106
169
|
{
|
|
107
170
|
id: WARDLEY_NATURE.activity,
|
|
@@ -168,5 +231,90 @@ export const WARDLEY_TAG_DEFS = {
|
|
|
168
231
|
},
|
|
169
232
|
],
|
|
170
233
|
},
|
|
234
|
+
{
|
|
235
|
+
id: WARDLEY_METHOD_TAG_ID,
|
|
236
|
+
label: 'Method',
|
|
237
|
+
labelKey: 'com.labre.wardley.tag.method',
|
|
238
|
+
description: 'How this component is sourced, run or staffed: a buying policy, a project management method or an attitude, at the phase it suits.',
|
|
239
|
+
descriptionKey: 'com.labre.wardley.tag.method.description',
|
|
240
|
+
cardinality: 'single',
|
|
241
|
+
appliesTo: [WARDLEY_ROLE.method],
|
|
242
|
+
// Family by family, and within one family in evolution order: the
|
|
243
|
+
// dropdown reads as the three rows of Wardley's own table.
|
|
244
|
+
values: [
|
|
245
|
+
{
|
|
246
|
+
id: WARDLEY_METHOD.make,
|
|
247
|
+
group: WARDLEY_METHOD_GROUP.buyingPolicy,
|
|
248
|
+
label: 'Make',
|
|
249
|
+
labelKey: 'com.labre.wardley.tag.method.make',
|
|
250
|
+
description: 'Buying policy, uncharted: make it yourself.',
|
|
251
|
+
descriptionKey: 'com.labre.wardley.tag.method.make.description',
|
|
252
|
+
},
|
|
253
|
+
{
|
|
254
|
+
id: WARDLEY_METHOD.buy,
|
|
255
|
+
group: WARDLEY_METHOD_GROUP.buyingPolicy,
|
|
256
|
+
label: 'Buy',
|
|
257
|
+
labelKey: 'com.labre.wardley.tag.method.buy',
|
|
258
|
+
description: 'Buying policy, transitional: buy a product.',
|
|
259
|
+
descriptionKey: 'com.labre.wardley.tag.method.buy.description',
|
|
260
|
+
},
|
|
261
|
+
{
|
|
262
|
+
id: WARDLEY_METHOD.outsource,
|
|
263
|
+
group: WARDLEY_METHOD_GROUP.buyingPolicy,
|
|
264
|
+
label: 'Outsource',
|
|
265
|
+
labelKey: 'com.labre.wardley.tag.method.outsource',
|
|
266
|
+
description: 'Buying policy, industrialized: rent it as a commodity or a utility.',
|
|
267
|
+
descriptionKey: 'com.labre.wardley.tag.method.outsource.description',
|
|
268
|
+
},
|
|
269
|
+
{
|
|
270
|
+
id: WARDLEY_METHOD.agile,
|
|
271
|
+
group: WARDLEY_METHOD_GROUP.projectManagement,
|
|
272
|
+
label: 'Agile',
|
|
273
|
+
labelKey: 'com.labre.wardley.tag.method.agile',
|
|
274
|
+
description: 'Project management, uncharted: explore and adapt.',
|
|
275
|
+
descriptionKey: 'com.labre.wardley.tag.method.agile.description',
|
|
276
|
+
},
|
|
277
|
+
{
|
|
278
|
+
id: WARDLEY_METHOD.lean,
|
|
279
|
+
group: WARDLEY_METHOD_GROUP.projectManagement,
|
|
280
|
+
label: 'Lean',
|
|
281
|
+
labelKey: 'com.labre.wardley.tag.method.lean',
|
|
282
|
+
description: 'Project management, transitional: remove waste.',
|
|
283
|
+
descriptionKey: 'com.labre.wardley.tag.method.lean.description',
|
|
284
|
+
},
|
|
285
|
+
{
|
|
286
|
+
id: WARDLEY_METHOD.sixSigma,
|
|
287
|
+
group: WARDLEY_METHOD_GROUP.projectManagement,
|
|
288
|
+
label: 'Six Sigma',
|
|
289
|
+
labelKey: 'com.labre.wardley.tag.method.six-sigma',
|
|
290
|
+
description: 'Project management, industrialized: remove deviation.',
|
|
291
|
+
descriptionKey: 'com.labre.wardley.tag.method.six-sigma.description',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
id: WARDLEY_METHOD.pioneers,
|
|
295
|
+
group: WARDLEY_METHOD_GROUP.attitudes,
|
|
296
|
+
label: 'Pioneers',
|
|
297
|
+
labelKey: 'com.labre.wardley.tag.method.pioneers',
|
|
298
|
+
description: 'Attitude, uncharted: people who explore the unknown.',
|
|
299
|
+
descriptionKey: 'com.labre.wardley.tag.method.pioneers.description',
|
|
300
|
+
},
|
|
301
|
+
{
|
|
302
|
+
id: WARDLEY_METHOD.settlers,
|
|
303
|
+
group: WARDLEY_METHOD_GROUP.attitudes,
|
|
304
|
+
label: 'Settlers',
|
|
305
|
+
labelKey: 'com.labre.wardley.tag.method.settlers',
|
|
306
|
+
description: 'Attitude, transitional: people who turn a prototype into a product.',
|
|
307
|
+
descriptionKey: 'com.labre.wardley.tag.method.settlers.description',
|
|
308
|
+
},
|
|
309
|
+
{
|
|
310
|
+
id: WARDLEY_METHOD.townPlanners,
|
|
311
|
+
group: WARDLEY_METHOD_GROUP.attitudes,
|
|
312
|
+
label: 'Town planners',
|
|
313
|
+
labelKey: 'com.labre.wardley.tag.method.town-planners',
|
|
314
|
+
description: 'Attitude, industrialized: people who industrialise into a commodity.',
|
|
315
|
+
descriptionKey: 'com.labre.wardley.tag.method.town-planners.description',
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
},
|
|
171
319
|
],
|
|
172
320
|
};
|
package/dist/node/consts.d.ts
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { NamedPalette } from '@formicoidea/labre-core/components/color-picker';
|
|
2
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
1
3
|
/**
|
|
2
4
|
* Visual constants for Wardley nodes (component + anchor) and the connector /
|
|
3
5
|
* inertia presets created from the Wardley menu.
|
|
@@ -121,7 +123,10 @@ export declare const ECOSYSTEM_LABEL = "Ecosystem";
|
|
|
121
123
|
* Method node: a component inscribed in a slightly larger outer circle whose
|
|
122
124
|
* FILL color encodes the chosen method (editable via the toolbar). Glyph = the
|
|
123
125
|
* outer circle (colored fill + border, the base ellipse) + a white component
|
|
124
|
-
* circle drawn at its center. Default fill is a neutral grey
|
|
126
|
+
* circle drawn at its center. Default fill is a neutral grey, still written at
|
|
127
|
+
* creation, but no longer what paints the ring: the ring is the method's
|
|
128
|
+
* prefabricated style, `make` when no `wardley:method` tag names one
|
|
129
|
+
* (`METHOD_STYLE`, ADR 0046).
|
|
125
130
|
*/
|
|
126
131
|
export declare const METHOD_SIZE = 35;
|
|
127
132
|
export declare const METHOD_FILL = "#d9d9d9";
|
|
@@ -129,6 +134,133 @@ export declare const METHOD: {
|
|
|
129
134
|
centerRatio: number;
|
|
130
135
|
};
|
|
131
136
|
export declare const METHOD_LABEL = "Component";
|
|
137
|
+
/**
|
|
138
|
+
* The nine Wardley swatches' own names, resolved by `resolvePaletteLabel`
|
|
139
|
+
* (`@labre/affine-components/color-picker`) through each swatch's own
|
|
140
|
+
* {@link NamedPalette.labelWording} — the framework-agnostic mechanism a
|
|
141
|
+
* gfx module uses to name a palette without touching `Palette`
|
|
142
|
+
* (`packages/affine/model`, RED ZONE) or importing another framework.
|
|
143
|
+
*
|
|
144
|
+
* Exported so `../translations.ts` can list them in the manifest under
|
|
145
|
+
* source `chrome`, the same treatment `WARDLEY_TOOLBAR_WORDINGS` already
|
|
146
|
+
* gets — a swatch's name is toolbar chrome, re-rendered on every locale
|
|
147
|
+
* switch, never seeded into a document.
|
|
148
|
+
*/
|
|
149
|
+
export declare const WARDLEY_PALETTE_WORDING_WONDER: ChromeWording;
|
|
150
|
+
export declare const WARDLEY_PALETTE_WORDING_PEACE: ChromeWording;
|
|
151
|
+
export declare const WARDLEY_PALETTE_WORDING_WAR: ChromeWording;
|
|
152
|
+
export declare const WARDLEY_PALETTE_WORDING_WONDER_LIGHT: ChromeWording;
|
|
153
|
+
export declare const WARDLEY_PALETTE_WORDING_PEACE_LIGHT: ChromeWording;
|
|
154
|
+
export declare const WARDLEY_PALETTE_WORDING_WAR_LIGHT: ChromeWording;
|
|
155
|
+
export declare const WARDLEY_PALETTE_WORDING_RED: ChromeWording;
|
|
156
|
+
export declare const WARDLEY_PALETTE_WORDING_INERTIA: ChromeWording;
|
|
157
|
+
export declare const WARDLEY_PALETTE_WORDING_METHOD_GREY: ChromeWording;
|
|
158
|
+
export declare const WARDLEY_PALETTE_WORDINGS: readonly ChromeWording[];
|
|
159
|
+
/**
|
|
160
|
+
* The Wardley **evolution cycle**, surfaced as ready-made swatches in the node
|
|
161
|
+
* colour picker: Wonder / Peace / War, the three climatic phases a component
|
|
162
|
+
* travels through in Simon Wardley's pattern — saturated first, then the light
|
|
163
|
+
* shades for a fill under a label — followed by the three colours the notation
|
|
164
|
+
* itself already uses (the evolution arrow's red, the inertia bar, a method's
|
|
165
|
+
* neutral fill).
|
|
166
|
+
*
|
|
167
|
+
* They are SHORTCUTS, never constraints: nothing in the map reads a colour, so
|
|
168
|
+
* an author is free to ignore them, and the custom picker stays one click away.
|
|
169
|
+
*/
|
|
170
|
+
export declare const WARDLEY_PALETTES: NamedPalette[];
|
|
171
|
+
/**
|
|
172
|
+
* The two greys of the buying-policy ramp below Method grey (`METHOD_FILL`,
|
|
173
|
+
* which is "make"): the three steps of Wardley's own make / buy / outsource
|
|
174
|
+
* shading, light to dark, about 85 %, 55 % and 25 % of white. The darkest is
|
|
175
|
+
* one notch off OWM's `#444444` on purpose: that literal is C4's stencil grey,
|
|
176
|
+
* the dark tables are one table across frameworks, and Wardley's ramp must
|
|
177
|
+
* not take its dark value from another framework's (`notation-dark-palettes`).
|
|
178
|
+
* Both have a dark entry in `../dark-palette.ts` that keeps the ramp's order
|
|
179
|
+
* on the dark card.
|
|
180
|
+
*/
|
|
181
|
+
export declare const METHOD_BUY_FILL = "#8c8c8c";
|
|
182
|
+
export declare const METHOD_OUTSOURCE_FILL = "#404040";
|
|
183
|
+
/**
|
|
184
|
+
* The swatch keys the method rings are made of. `swatch` accepts these and
|
|
185
|
+
* no other, so a key used below is always one the guard test checks.
|
|
186
|
+
*/
|
|
187
|
+
export declare const METHOD_SWATCH_KEYS: readonly ["Peace", "Peace light", "War", "War light"];
|
|
188
|
+
/** One method's prefabricated ring: what fills it, what draws its line. */
|
|
189
|
+
export interface WardleyMethodStyle {
|
|
190
|
+
fill: string;
|
|
191
|
+
stroke: string;
|
|
192
|
+
/** The ring's line width, never thinner than the node's own rim. */
|
|
193
|
+
lineWidth: number;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* The nine methods' rings, PO decision of 2026-10-28: in Labre the colour
|
|
197
|
+
* defines the method, family by family (ADR 0046).
|
|
198
|
+
*
|
|
199
|
+
* - **Buying policy** — Wardley's own style: the ink border, thick, around a
|
|
200
|
+
* grey that darkens with the phase (make, buy, outsource).
|
|
201
|
+
* - **Project management** — intensity of grey: the ink itself at 15 %, 35 %
|
|
202
|
+
* and 55 %, with no border of another colour. Ink with an alpha tail, so the
|
|
203
|
+
* dark card paints the same intensities in its own light ink (a stored
|
|
204
|
+
* `#rrggbbaa` keeps its alpha through the tint).
|
|
205
|
+
* - **Attitudes** — the Wardley palette's own swatches, a "transparent" fill
|
|
206
|
+
* being the light variant of the border's swatch (PO decision,
|
|
207
|
+
* 2026-10-28): pioneers Peace light in a Peace border, settlers solid
|
|
208
|
+
* Peace, town planners War light in a War border.
|
|
209
|
+
*
|
|
210
|
+
* Told apart family by family by the border (ink, none, hue) and within a
|
|
211
|
+
* family by the fill, and none of them is a plain component's white disc.
|
|
212
|
+
*
|
|
213
|
+
* A method node with no method tag paints `make`, the buying-policy family's
|
|
214
|
+
* first value: the default style of the node IS that family (PO decision,
|
|
215
|
+
* 2026-10-28), so its stored fill no longer shows on any method node.
|
|
216
|
+
*/
|
|
217
|
+
export declare const METHOD_STYLE: {
|
|
218
|
+
readonly make: {
|
|
219
|
+
readonly fill: "#d9d9d9";
|
|
220
|
+
readonly stroke: string;
|
|
221
|
+
readonly lineWidth: 2;
|
|
222
|
+
};
|
|
223
|
+
readonly buy: {
|
|
224
|
+
readonly fill: "#8c8c8c";
|
|
225
|
+
readonly stroke: string;
|
|
226
|
+
readonly lineWidth: 2;
|
|
227
|
+
};
|
|
228
|
+
readonly outsource: {
|
|
229
|
+
readonly fill: "#404040";
|
|
230
|
+
readonly stroke: string;
|
|
231
|
+
readonly lineWidth: 2;
|
|
232
|
+
};
|
|
233
|
+
readonly agile: {
|
|
234
|
+
readonly fill: string;
|
|
235
|
+
readonly stroke: string;
|
|
236
|
+
readonly lineWidth: 1.5;
|
|
237
|
+
};
|
|
238
|
+
readonly lean: {
|
|
239
|
+
readonly fill: string;
|
|
240
|
+
readonly stroke: string;
|
|
241
|
+
readonly lineWidth: 1.5;
|
|
242
|
+
};
|
|
243
|
+
readonly sixSigma: {
|
|
244
|
+
readonly fill: string;
|
|
245
|
+
readonly stroke: string;
|
|
246
|
+
readonly lineWidth: 1.5;
|
|
247
|
+
};
|
|
248
|
+
readonly pioneers: {
|
|
249
|
+
readonly fill: string;
|
|
250
|
+
readonly stroke: string;
|
|
251
|
+
readonly lineWidth: 2;
|
|
252
|
+
};
|
|
253
|
+
readonly settlers: {
|
|
254
|
+
readonly fill: string;
|
|
255
|
+
readonly stroke: string;
|
|
256
|
+
readonly lineWidth: 2;
|
|
257
|
+
};
|
|
258
|
+
readonly townPlanners: {
|
|
259
|
+
readonly fill: string;
|
|
260
|
+
readonly stroke: string;
|
|
261
|
+
readonly lineWidth: 2;
|
|
262
|
+
};
|
|
263
|
+
};
|
|
132
264
|
/**
|
|
133
265
|
* Porter's forces (composite): a large white circle carrying ONE letter, pushed
|
|
134
266
|
* on from the four cardinal directions by solid red arrows.
|
package/dist/node/consts.js
CHANGED
|
@@ -116,7 +116,10 @@ export const ECOSYSTEM_LABEL = 'Ecosystem';
|
|
|
116
116
|
* Method node: a component inscribed in a slightly larger outer circle whose
|
|
117
117
|
* FILL color encodes the chosen method (editable via the toolbar). Glyph = the
|
|
118
118
|
* outer circle (colored fill + border, the base ellipse) + a white component
|
|
119
|
-
* circle drawn at its center. Default fill is a neutral grey
|
|
119
|
+
* circle drawn at its center. Default fill is a neutral grey, still written at
|
|
120
|
+
* creation, but no longer what paints the ring: the ring is the method's
|
|
121
|
+
* prefabricated style, `make` when no `wardley:method` tag names one
|
|
122
|
+
* (`METHOD_STYLE`, ADR 0046).
|
|
120
123
|
*/
|
|
121
124
|
export const METHOD_SIZE = 35;
|
|
122
125
|
export const METHOD_FILL = '#d9d9d9';
|
|
@@ -124,6 +127,221 @@ export const METHOD = {
|
|
|
124
127
|
centerRatio: 0.5, // inner white component radius / R
|
|
125
128
|
};
|
|
126
129
|
export const METHOD_LABEL = 'Component';
|
|
130
|
+
/*
|
|
131
|
+
* The Wardley swatch table, moved here word for word from
|
|
132
|
+
* `../toolbar/node-config.ts` (only `export` added, ADR 0046): the method
|
|
133
|
+
* rings below are painted from Peace and War, and the toolbar module already
|
|
134
|
+
* imports this file, so the ring styles could not import the table back from
|
|
135
|
+
* there without an import loop — nor pull toolbar code into the node painter
|
|
136
|
+
* and the headless entry. The colour picker still builds its list from it.
|
|
137
|
+
*/
|
|
138
|
+
/**
|
|
139
|
+
* The nine Wardley swatches' own names, resolved by `resolvePaletteLabel`
|
|
140
|
+
* (`@labre/affine-components/color-picker`) through each swatch's own
|
|
141
|
+
* {@link NamedPalette.labelWording} — the framework-agnostic mechanism a
|
|
142
|
+
* gfx module uses to name a palette without touching `Palette`
|
|
143
|
+
* (`packages/affine/model`, RED ZONE) or importing another framework.
|
|
144
|
+
*
|
|
145
|
+
* Exported so `../translations.ts` can list them in the manifest under
|
|
146
|
+
* source `chrome`, the same treatment `WARDLEY_TOOLBAR_WORDINGS` already
|
|
147
|
+
* gets — a swatch's name is toolbar chrome, re-rendered on every locale
|
|
148
|
+
* switch, never seeded into a document.
|
|
149
|
+
*/
|
|
150
|
+
export const WARDLEY_PALETTE_WORDING_WONDER = [
|
|
151
|
+
'com.labre.wardley.palette.wonder',
|
|
152
|
+
'Wonder',
|
|
153
|
+
];
|
|
154
|
+
export const WARDLEY_PALETTE_WORDING_PEACE = [
|
|
155
|
+
'com.labre.wardley.palette.peace',
|
|
156
|
+
'Peace',
|
|
157
|
+
];
|
|
158
|
+
export const WARDLEY_PALETTE_WORDING_WAR = [
|
|
159
|
+
'com.labre.wardley.palette.war',
|
|
160
|
+
'War',
|
|
161
|
+
];
|
|
162
|
+
export const WARDLEY_PALETTE_WORDING_WONDER_LIGHT = [
|
|
163
|
+
'com.labre.wardley.palette.wonder-light',
|
|
164
|
+
'Wonder light',
|
|
165
|
+
];
|
|
166
|
+
export const WARDLEY_PALETTE_WORDING_PEACE_LIGHT = [
|
|
167
|
+
'com.labre.wardley.palette.peace-light',
|
|
168
|
+
'Peace light',
|
|
169
|
+
];
|
|
170
|
+
export const WARDLEY_PALETTE_WORDING_WAR_LIGHT = [
|
|
171
|
+
'com.labre.wardley.palette.war-light',
|
|
172
|
+
'War light',
|
|
173
|
+
];
|
|
174
|
+
export const WARDLEY_PALETTE_WORDING_RED = [
|
|
175
|
+
'com.labre.wardley.palette.wardley-red',
|
|
176
|
+
'Wardley red',
|
|
177
|
+
];
|
|
178
|
+
export const WARDLEY_PALETTE_WORDING_INERTIA = [
|
|
179
|
+
'com.labre.wardley.palette.inertia',
|
|
180
|
+
'Inertia',
|
|
181
|
+
];
|
|
182
|
+
export const WARDLEY_PALETTE_WORDING_METHOD_GREY = [
|
|
183
|
+
'com.labre.wardley.palette.method-grey',
|
|
184
|
+
'Method grey',
|
|
185
|
+
];
|
|
186
|
+
export const WARDLEY_PALETTE_WORDINGS = [
|
|
187
|
+
WARDLEY_PALETTE_WORDING_WONDER,
|
|
188
|
+
WARDLEY_PALETTE_WORDING_PEACE,
|
|
189
|
+
WARDLEY_PALETTE_WORDING_WAR,
|
|
190
|
+
WARDLEY_PALETTE_WORDING_WONDER_LIGHT,
|
|
191
|
+
WARDLEY_PALETTE_WORDING_PEACE_LIGHT,
|
|
192
|
+
WARDLEY_PALETTE_WORDING_WAR_LIGHT,
|
|
193
|
+
WARDLEY_PALETTE_WORDING_RED,
|
|
194
|
+
WARDLEY_PALETTE_WORDING_INERTIA,
|
|
195
|
+
WARDLEY_PALETTE_WORDING_METHOD_GREY,
|
|
196
|
+
];
|
|
197
|
+
/**
|
|
198
|
+
* The Wardley **evolution cycle**, surfaced as ready-made swatches in the node
|
|
199
|
+
* colour picker: Wonder / Peace / War, the three climatic phases a component
|
|
200
|
+
* travels through in Simon Wardley's pattern — saturated first, then the light
|
|
201
|
+
* shades for a fill under a label — followed by the three colours the notation
|
|
202
|
+
* itself already uses (the evolution arrow's red, the inertia bar, a method's
|
|
203
|
+
* neutral fill).
|
|
204
|
+
*
|
|
205
|
+
* They are SHORTCUTS, never constraints: nothing in the map reads a colour, so
|
|
206
|
+
* an author is free to ignore them, and the custom picker stays one click away.
|
|
207
|
+
*/
|
|
208
|
+
export const WARDLEY_PALETTES = [
|
|
209
|
+
{
|
|
210
|
+
key: 'Wonder',
|
|
211
|
+
value: '#3ec9f2',
|
|
212
|
+
labelWording: WARDLEY_PALETTE_WORDING_WONDER,
|
|
213
|
+
},
|
|
214
|
+
{
|
|
215
|
+
key: 'Peace',
|
|
216
|
+
value: '#5b9cf6',
|
|
217
|
+
labelWording: WARDLEY_PALETTE_WORDING_PEACE,
|
|
218
|
+
},
|
|
219
|
+
{ key: 'War', value: '#9d6df0', labelWording: WARDLEY_PALETTE_WORDING_WAR },
|
|
220
|
+
{
|
|
221
|
+
key: 'Wonder light',
|
|
222
|
+
value: '#b9e9fa',
|
|
223
|
+
labelWording: WARDLEY_PALETTE_WORDING_WONDER_LIGHT,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
key: 'Peace light',
|
|
227
|
+
value: '#c6dbfc',
|
|
228
|
+
labelWording: WARDLEY_PALETTE_WORDING_PEACE_LIGHT,
|
|
229
|
+
},
|
|
230
|
+
{
|
|
231
|
+
key: 'War light',
|
|
232
|
+
value: '#d9c9fa',
|
|
233
|
+
labelWording: WARDLEY_PALETTE_WORDING_WAR_LIGHT,
|
|
234
|
+
},
|
|
235
|
+
{
|
|
236
|
+
key: 'Wardley red',
|
|
237
|
+
value: WARDLEY_RED,
|
|
238
|
+
labelWording: WARDLEY_PALETTE_WORDING_RED,
|
|
239
|
+
},
|
|
240
|
+
{
|
|
241
|
+
key: 'Inertia',
|
|
242
|
+
value: INERTIA_COLOR,
|
|
243
|
+
labelWording: WARDLEY_PALETTE_WORDING_INERTIA,
|
|
244
|
+
},
|
|
245
|
+
{
|
|
246
|
+
key: 'Method grey',
|
|
247
|
+
value: METHOD_FILL,
|
|
248
|
+
labelWording: WARDLEY_PALETTE_WORDING_METHOD_GREY,
|
|
249
|
+
},
|
|
250
|
+
];
|
|
251
|
+
/**
|
|
252
|
+
* The two greys of the buying-policy ramp below Method grey (`METHOD_FILL`,
|
|
253
|
+
* which is "make"): the three steps of Wardley's own make / buy / outsource
|
|
254
|
+
* shading, light to dark, about 85 %, 55 % and 25 % of white. The darkest is
|
|
255
|
+
* one notch off OWM's `#444444` on purpose: that literal is C4's stencil grey,
|
|
256
|
+
* the dark tables are one table across frameworks, and Wardley's ramp must
|
|
257
|
+
* not take its dark value from another framework's (`notation-dark-palettes`).
|
|
258
|
+
* Both have a dark entry in `../dark-palette.ts` that keeps the ramp's order
|
|
259
|
+
* on the dark card.
|
|
260
|
+
*/
|
|
261
|
+
export const METHOD_BUY_FILL = '#8c8c8c';
|
|
262
|
+
export const METHOD_OUTSOURCE_FILL = '#404040';
|
|
263
|
+
/**
|
|
264
|
+
* The swatch keys the method rings are made of. `swatch` accepts these and
|
|
265
|
+
* no other, so a key used below is always one the guard test checks.
|
|
266
|
+
*/
|
|
267
|
+
export const METHOD_SWATCH_KEYS = [
|
|
268
|
+
'Peace',
|
|
269
|
+
'Peace light',
|
|
270
|
+
'War',
|
|
271
|
+
'War light',
|
|
272
|
+
];
|
|
273
|
+
/**
|
|
274
|
+
* A Wardley swatch's colour, by the key the colour picker names it with.
|
|
275
|
+
* Throws on a key the table does not hold: a renamed swatch must fail the
|
|
276
|
+
* build (`method.unit.spec.ts` pins every key used here), never paint a ring
|
|
277
|
+
* in no colour.
|
|
278
|
+
*/
|
|
279
|
+
function swatch(key) {
|
|
280
|
+
const value = WARDLEY_PALETTES.find(palette => palette.key === key)?.value;
|
|
281
|
+
if (typeof value !== 'string') {
|
|
282
|
+
throw new Error(`No Wardley swatch "${key}" with a plain colour.`);
|
|
283
|
+
}
|
|
284
|
+
return value;
|
|
285
|
+
}
|
|
286
|
+
/** `'#rrggbb'` at `alpha` (two hex digits), as a stored 8-digit hex. */
|
|
287
|
+
const withAlpha = (hex, alpha) => `${hex}${alpha}`;
|
|
288
|
+
/**
|
|
289
|
+
* The nine methods' rings, PO decision of 2026-10-28: in Labre the colour
|
|
290
|
+
* defines the method, family by family (ADR 0046).
|
|
291
|
+
*
|
|
292
|
+
* - **Buying policy** — Wardley's own style: the ink border, thick, around a
|
|
293
|
+
* grey that darkens with the phase (make, buy, outsource).
|
|
294
|
+
* - **Project management** — intensity of grey: the ink itself at 15 %, 35 %
|
|
295
|
+
* and 55 %, with no border of another colour. Ink with an alpha tail, so the
|
|
296
|
+
* dark card paints the same intensities in its own light ink (a stored
|
|
297
|
+
* `#rrggbbaa` keeps its alpha through the tint).
|
|
298
|
+
* - **Attitudes** — the Wardley palette's own swatches, a "transparent" fill
|
|
299
|
+
* being the light variant of the border's swatch (PO decision,
|
|
300
|
+
* 2026-10-28): pioneers Peace light in a Peace border, settlers solid
|
|
301
|
+
* Peace, town planners War light in a War border.
|
|
302
|
+
*
|
|
303
|
+
* Told apart family by family by the border (ink, none, hue) and within a
|
|
304
|
+
* family by the fill, and none of them is a plain component's white disc.
|
|
305
|
+
*
|
|
306
|
+
* A method node with no method tag paints `make`, the buying-policy family's
|
|
307
|
+
* first value: the default style of the node IS that family (PO decision,
|
|
308
|
+
* 2026-10-28), so its stored fill no longer shows on any method node.
|
|
309
|
+
*/
|
|
310
|
+
export const METHOD_STYLE = {
|
|
311
|
+
make: { fill: METHOD_FILL, stroke: NODE_STROKE, lineWidth: 2 },
|
|
312
|
+
buy: { fill: METHOD_BUY_FILL, stroke: NODE_STROKE, lineWidth: 2 },
|
|
313
|
+
outsource: { fill: METHOD_OUTSOURCE_FILL, stroke: NODE_STROKE, lineWidth: 2 },
|
|
314
|
+
agile: {
|
|
315
|
+
fill: withAlpha(NODE_STROKE, '26'),
|
|
316
|
+
stroke: withAlpha(NODE_STROKE, '26'),
|
|
317
|
+
lineWidth: 1.5,
|
|
318
|
+
},
|
|
319
|
+
lean: {
|
|
320
|
+
fill: withAlpha(NODE_STROKE, '59'),
|
|
321
|
+
stroke: withAlpha(NODE_STROKE, '59'),
|
|
322
|
+
lineWidth: 1.5,
|
|
323
|
+
},
|
|
324
|
+
sixSigma: {
|
|
325
|
+
fill: withAlpha(NODE_STROKE, '8c'),
|
|
326
|
+
stroke: withAlpha(NODE_STROKE, '8c'),
|
|
327
|
+
lineWidth: 1.5,
|
|
328
|
+
},
|
|
329
|
+
pioneers: {
|
|
330
|
+
fill: swatch('Peace light'),
|
|
331
|
+
stroke: swatch('Peace'),
|
|
332
|
+
lineWidth: 2,
|
|
333
|
+
},
|
|
334
|
+
settlers: {
|
|
335
|
+
fill: swatch('Peace'),
|
|
336
|
+
stroke: swatch('Peace'),
|
|
337
|
+
lineWidth: 2,
|
|
338
|
+
},
|
|
339
|
+
townPlanners: {
|
|
340
|
+
fill: swatch('War light'),
|
|
341
|
+
stroke: swatch('War'),
|
|
342
|
+
lineWidth: 2,
|
|
343
|
+
},
|
|
344
|
+
};
|
|
127
345
|
/**
|
|
128
346
|
* Porter's forces (composite): a large white circle carrying ONE letter, pushed
|
|
129
347
|
* on from the four cardinal directions by solid red arrows.
|
|
@@ -11,6 +11,6 @@ import { type WardleyNodeElementModel } from '@formicoidea/labre-core/model';
|
|
|
11
11
|
* the hollow center come from the base fill, so colors stay editable.
|
|
12
12
|
*/
|
|
13
13
|
export declare const wardleyNode: ElementRenderer<WardleyNodeElementModel>;
|
|
14
|
-
export declare const WardleyNodeRendererExtension: import("@formicoidea/labre-core/_pkgs/store").ExtensionType & {
|
|
15
|
-
identifier: import("@formicoidea/labre-core/_pkgs/global/di").ServiceIdentifier<ElementRenderer<WardleyNodeElementModel>>;
|
|
14
|
+
export declare const WardleyNodeRendererExtension: import("@formicoidea/labre-core/_pkgs/store/index.js").ExtensionType & {
|
|
15
|
+
identifier: import("@formicoidea/labre-core/_pkgs/global/di/types.js").ServiceIdentifier<ElementRenderer<WardleyNodeElementModel>>;
|
|
16
16
|
};
|
|
@@ -1,7 +1,47 @@
|
|
|
1
1
|
import { ElementRendererExtension, notationColor, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { shape as shapeRenderer } from '@formicoidea/labre-core/gfx/shape';
|
|
3
3
|
import { DefaultTheme, } from '@formicoidea/labre-core/model';
|
|
4
|
-
import {
|
|
4
|
+
import { elementTagValues } from '@formicoidea/labre-core/std/gfx';
|
|
5
|
+
import { wardleyMethodStyle } from '../method.js';
|
|
6
|
+
import { WARDLEY_METHOD_TAG_ID } from '../natures.js';
|
|
7
|
+
import { ANCHOR, ECOSYSTEM, METHOD, METHOD_STYLE, NODE_FILL } from './consts.js';
|
|
8
|
+
/**
|
|
9
|
+
* The outer ring of a method node, painted with its method's prefabricated
|
|
10
|
+
* style (`METHOD_STYLE`, ADR 0046): the one its `wardley:method` tag names,
|
|
11
|
+
* and `make` — the buying-policy family's first value, the node's default
|
|
12
|
+
* style (PO decision, 2026-10-28) — when it carries no tag or a value no pack
|
|
13
|
+
* of this version names.
|
|
14
|
+
*
|
|
15
|
+
* Derived at paint time and never written back. The stored fill and stroke
|
|
16
|
+
* the "colour = phase" palette wrote stay in the document untouched; they no
|
|
17
|
+
* longer reach the ring, which is the method's on every method node.
|
|
18
|
+
*
|
|
19
|
+
* Painted OVER the native ellipse, on its exact path (the shape renderer's
|
|
20
|
+
* own inset of half a stroke, both radii, so a node stretched into an oval is
|
|
21
|
+
* covered too). The paper goes down first, line included, so a wash or a
|
|
22
|
+
* borderless grey reads the same over any stored fill and no dark edge of the
|
|
23
|
+
* native rim survives under it; then the style's fill and line. Every colour
|
|
24
|
+
* goes through the notation tint, so a grey of the ramp and the ink at an
|
|
25
|
+
* alpha take their dark-card value (ADR 0039) and the hues stay.
|
|
26
|
+
*/
|
|
27
|
+
function paintMethodRing(model, ctx, renderer, w, h, strokeWidth) {
|
|
28
|
+
const style = wardleyMethodStyle(elementTagValues(model, WARDLEY_METHOD_TAG_ID)[0]) ??
|
|
29
|
+
METHOD_STYLE.make;
|
|
30
|
+
// Both radii are positive: the caller has already returned on a node too
|
|
31
|
+
// small for its own stroke.
|
|
32
|
+
ctx.beginPath();
|
|
33
|
+
ctx.ellipse(w / 2, h / 2, w / 2 - strokeWidth / 2, h / 2 - strokeWidth / 2, 0, 0, Math.PI * 2);
|
|
34
|
+
const paper = notationColor(renderer, NODE_FILL, 'fill');
|
|
35
|
+
ctx.lineWidth = Math.max(strokeWidth, style.lineWidth);
|
|
36
|
+
ctx.fillStyle = paper;
|
|
37
|
+
ctx.fill();
|
|
38
|
+
ctx.strokeStyle = paper;
|
|
39
|
+
ctx.stroke();
|
|
40
|
+
ctx.fillStyle = notationColor(renderer, style.fill, 'fill');
|
|
41
|
+
ctx.fill();
|
|
42
|
+
ctx.strokeStyle = notationColor(renderer, style.stroke, 'ink');
|
|
43
|
+
ctx.stroke();
|
|
44
|
+
}
|
|
5
45
|
/**
|
|
6
46
|
* Renderer for a Wardley node. The circle is drawn by REUSING the native shape
|
|
7
47
|
* renderer (so stroke width, colors and theme behave exactly like a native
|
|
@@ -71,6 +111,7 @@ export const wardleyNode = (model, ctx, matrix, renderer, rc, bound) => {
|
|
|
71
111
|
}
|
|
72
112
|
// ── Method: a white component inscribed in the colored outer circle ──
|
|
73
113
|
if (model.kind === 'method') {
|
|
114
|
+
paintMethodRing(model, ctx, renderer, w, h, strokeWidth);
|
|
74
115
|
const rInner = R * METHOD.centerRatio;
|
|
75
116
|
ctx.fillStyle = notationColor(renderer, NODE_FILL, 'fill');
|
|
76
117
|
ctx.lineWidth = strokeWidth;
|