@formicoidea/labre-framework-wardley 0.34.2 → 0.36.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 +111 -31
- package/dist/actions.js +184 -98
- package/dist/commands-manifest.js +40 -0
- package/dist/commands.d.ts +18 -1
- package/dist/commands.js +96 -7
- package/dist/export.js +50 -2
- package/dist/import.d.ts +5 -3
- package/dist/import.js +53 -8
- package/dist/index.d.ts +4 -1
- package/dist/index.js +16 -1
- package/dist/legend.d.ts +27 -1
- package/dist/legend.js +277 -3
- package/dist/morph.d.ts +129 -0
- package/dist/morph.js +405 -0
- package/dist/natures.d.ts +31 -0
- package/dist/natures.js +64 -4
- package/dist/node/consts.d.ts +123 -0
- package/dist/node/consts.js +122 -0
- package/dist/node/node-view.d.ts +82 -4
- package/dist/node/node-view.js +108 -4
- package/dist/node/porter-watcher.d.ts +119 -0
- package/dist/node/porter-watcher.js +255 -0
- package/dist/porter-competition.d.ts +36 -0
- package/dist/porter-competition.js +50 -0
- package/dist/presets.d.ts +276 -0
- package/dist/presets.js +554 -0
- package/dist/roles.d.ts +4 -0
- package/dist/roles.js +85 -7
- package/dist/toolbar/icons.d.ts +36 -0
- package/dist/toolbar/icons.js +58 -0
- package/dist/toolbar/node-config.d.ts +20 -0
- package/dist/toolbar/node-config.js +100 -7
- package/dist/view.js +40 -2
- package/package.json +2 -2
package/dist/commands.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { GfxControllerIdentifier } from '@formicoidea/labre-core/std/gfx';
|
|
2
|
-
import { activateWardleyConnector, createWardleyBackground, createWardleyInertia, createWardleyMarket, createWardleyNode, createWardleyPipeline, exportOwmFile, importOwmFile, importWardleySvgFile, wardleyMapsOnBoard, } from './actions.js';
|
|
2
|
+
import { activateWardleyConnector, createWardleyAccelerator, createWardleyArea, createWardleyBackground, createWardleyInertia, createWardleyMarket, createWardleyNode, createWardleyPipeline, createWardleyPorter, exportOwmFile, importOwmFile, importWardleySvgFile, wardleyMapsOnBoard, } from './actions.js';
|
|
3
3
|
import { WARDLEY_ROLE, WARDLEY_ROLES } from './roles.js';
|
|
4
|
-
import { wardleyAnchorIcon, wardleyArrowIcon, wardleyBackgroundIcon, wardleyBenefitIcon, wardleyComponentIcon, wardleyEcosystemIcon, wardleyEvolutionGradientIcon, wardleyExportOwmIcon, wardleyImportOwmIcon, wardleyImportSvgIcon, wardleyInertiaIcon, wardleyLinkIcon, wardleyMarketIcon, wardleyMethodIcon, wardleyOpportunityIcon, wardleyPipelineIcon, } from './toolbar/icons.js';
|
|
4
|
+
import { wardleyAcceleratorIcon, wardleyAnchorIcon, wardleyAreaPolygonIcon, wardleyAreaRectIcon, wardleyArrowIcon, wardleyBackgroundIcon, wardleyDeceleratorIcon, wardleyBenefitIcon, wardleyComponentIcon, wardleyEcosystemIcon, wardleyEvolutionGradientIcon, wardleyExportOwmIcon, wardleyImportOwmIcon, wardleyImportSvgIcon, wardleyInertiaIcon, wardleyLinkIcon, wardleyMarketIcon, wardleyMethodIcon, wardleyOpportunityIcon, wardleyPipelineIcon, wardleyPorterIcon, } from './toolbar/icons.js';
|
|
5
5
|
const SPECS = [
|
|
6
6
|
{
|
|
7
7
|
id: 'addBackground',
|
|
@@ -12,6 +12,7 @@ const SPECS = [
|
|
|
12
12
|
category: 'backgrounds',
|
|
13
13
|
kind: 'artefact',
|
|
14
14
|
element: 'background:classic',
|
|
15
|
+
board: true,
|
|
15
16
|
run: gfx => createWardleyBackground(gfx, 'classic'),
|
|
16
17
|
},
|
|
17
18
|
{
|
|
@@ -21,6 +22,7 @@ const SPECS = [
|
|
|
21
22
|
category: 'backgrounds',
|
|
22
23
|
kind: 'artefact',
|
|
23
24
|
element: 'background:opportunity',
|
|
25
|
+
board: true,
|
|
24
26
|
run: gfx => createWardleyBackground(gfx, 'opportunity'),
|
|
25
27
|
},
|
|
26
28
|
{
|
|
@@ -30,6 +32,7 @@ const SPECS = [
|
|
|
30
32
|
category: 'backgrounds',
|
|
31
33
|
kind: 'artefact',
|
|
32
34
|
element: 'background:benefit',
|
|
35
|
+
board: true,
|
|
33
36
|
run: gfx => createWardleyBackground(gfx, 'benefit'),
|
|
34
37
|
},
|
|
35
38
|
{
|
|
@@ -39,6 +42,7 @@ const SPECS = [
|
|
|
39
42
|
category: 'backgrounds',
|
|
40
43
|
kind: 'artefact',
|
|
41
44
|
element: 'background:evolution-gradient',
|
|
45
|
+
board: true,
|
|
42
46
|
run: gfx => createWardleyBackground(gfx, 'evolution-gradient'),
|
|
43
47
|
},
|
|
44
48
|
{
|
|
@@ -137,6 +141,69 @@ const SPECS = [
|
|
|
137
141
|
element: 'node:inertia',
|
|
138
142
|
run: createWardleyInertia,
|
|
139
143
|
},
|
|
144
|
+
// Last of the nodes, and last of the row: a Porter's force is the only
|
|
145
|
+
// artefact on this palette that is not part of the map — it is what presses
|
|
146
|
+
// on it from outside — so it reads as an annotation you reach for once the
|
|
147
|
+
// chain is drawn. Keyless: the `w` chord already seats eight artefacts, and
|
|
148
|
+
// a framework binds past that by host override rather than by default.
|
|
149
|
+
{
|
|
150
|
+
id: 'addPorter',
|
|
151
|
+
label: "Porter's forces",
|
|
152
|
+
iconKey: 'wardley.porter',
|
|
153
|
+
category: 'nodes',
|
|
154
|
+
kind: 'artefact',
|
|
155
|
+
element: 'node:porter',
|
|
156
|
+
run: createWardleyPorter,
|
|
157
|
+
},
|
|
158
|
+
// The two climate annotations, after the force and for the same reason: they
|
|
159
|
+
// are not links in the value chain either, and they are reached for once the
|
|
160
|
+
// chain is drawn and the question becomes how fast it is moving. Keyless,
|
|
161
|
+
// like the porter — the `w` chord seats eight artefacts and a framework binds
|
|
162
|
+
// past that by host override.
|
|
163
|
+
{
|
|
164
|
+
id: 'addAccelerator',
|
|
165
|
+
label: 'Accelerator',
|
|
166
|
+
iconKey: 'wardley.accelerator',
|
|
167
|
+
category: 'nodes',
|
|
168
|
+
kind: 'artefact',
|
|
169
|
+
element: 'node:accelerator',
|
|
170
|
+
run: gfx => createWardleyAccelerator(gfx, 'accelerator'),
|
|
171
|
+
},
|
|
172
|
+
{
|
|
173
|
+
id: 'addDecelerator',
|
|
174
|
+
label: 'Decelerator',
|
|
175
|
+
iconKey: 'wardley.decelerator',
|
|
176
|
+
category: 'nodes',
|
|
177
|
+
kind: 'artefact',
|
|
178
|
+
element: 'node:decelerator',
|
|
179
|
+
run: gfx => createWardleyAccelerator(gfx, 'decelerator'),
|
|
180
|
+
},
|
|
181
|
+
// The zones, last of the toolbox and after the connectors: an area is drawn
|
|
182
|
+
// around a chain that already exists, so it is the last gesture of a map and
|
|
183
|
+
// reads as the last entry of the row. Two entries and ONE kind — the shape is
|
|
184
|
+
// the choice, and offering it as two buttons is what saves the author a trip
|
|
185
|
+
// through "switch type" for the commonest case.
|
|
186
|
+
//
|
|
187
|
+
// Keyless, like the porter and the two climate arrows: the `w` chord seats
|
|
188
|
+
// eight artefacts and a framework binds past that by host override.
|
|
189
|
+
{
|
|
190
|
+
id: 'addAreaRect',
|
|
191
|
+
label: 'Area (rectangle)',
|
|
192
|
+
iconKey: 'wardley.area-rect',
|
|
193
|
+
category: 'areas',
|
|
194
|
+
kind: 'artefact',
|
|
195
|
+
element: 'node:area-rect',
|
|
196
|
+
run: gfx => createWardleyArea(gfx, 'rect'),
|
|
197
|
+
},
|
|
198
|
+
{
|
|
199
|
+
id: 'addAreaPolygon',
|
|
200
|
+
label: 'Area (polygon)',
|
|
201
|
+
iconKey: 'wardley.area-polygon',
|
|
202
|
+
category: 'areas',
|
|
203
|
+
kind: 'artefact',
|
|
204
|
+
element: 'node:area-polygon',
|
|
205
|
+
run: gfx => createWardleyArea(gfx, 'polygon'),
|
|
206
|
+
},
|
|
140
207
|
];
|
|
141
208
|
/**
|
|
142
209
|
* The sentence a tool shows under its label — the role's own gesture hint
|
|
@@ -171,7 +238,7 @@ const toolboxCommands = SPECS.map((spec, order) => ({
|
|
|
171
238
|
: { mac: [], other: [] },
|
|
172
239
|
availability: 'always',
|
|
173
240
|
run: std => spec.run(std.get(GfxControllerIdentifier)),
|
|
174
|
-
telemetry: { framework: 'wardley', element: spec.element },
|
|
241
|
+
telemetry: { framework: 'wardley', element: spec.element, board: spec.board },
|
|
175
242
|
}));
|
|
176
243
|
/**
|
|
177
244
|
* The IMPORT — the first Wardley command whose subject is a whole map rather
|
|
@@ -183,9 +250,9 @@ const toolboxCommands = SPECS.map((spec, order) => ({
|
|
|
183
250
|
* the sub-menu is the first thing a user opens, and "start from the map
|
|
184
251
|
* somebody sent me" belongs in that row beside "start from a component" —
|
|
185
252
|
* asking them to find the catalogue sidepanel first is the friction the
|
|
186
|
-
* decision names. It is
|
|
187
|
-
*
|
|
188
|
-
*
|
|
253
|
+
* decision names. It is the `+1` of ADR 0014's R4 — the single deliberate
|
|
254
|
+
* over-nomination the PO signed off on, and the reason this framework's
|
|
255
|
+
* nomination budget is `SENIOR_MENU_CAP + 1` rather than the cap itself.
|
|
189
256
|
*
|
|
190
257
|
* No `'contextual-toolbar'`: a contextual toolbar is a statement about a
|
|
191
258
|
* SELECTION, and the moment this command is most wanted is on a board with
|
|
@@ -323,8 +390,25 @@ const importSvgCommand = {
|
|
|
323
390
|
telemetry: { framework: 'wardley', element: 'board:import-svg' },
|
|
324
391
|
};
|
|
325
392
|
/**
|
|
326
|
-
* The Wardley registry: the
|
|
393
|
+
* The Wardley registry: the eighteen toolbox entries, then the two directions
|
|
327
394
|
* of the OWM DSL and the SVG fallback (`docs/adr/0012`).
|
|
395
|
+
*
|
|
396
|
+
* ## Nineteen nominations, and the curation decision behind them
|
|
397
|
+
*
|
|
398
|
+
* `addPorter` spent the last seat of ADR 0014 R4's `SENIOR_MENU_CAP + 1`
|
|
399
|
+
* budget, and `addAccelerator` / `addDecelerator` are the two past it. The PO
|
|
400
|
+
* made the call on 2026-09-03 (Amendment 2026-09-03 under R4): **every Wardley
|
|
401
|
+
* artefact nominates the row**. The row itself is unchanged — thirteen
|
|
402
|
+
* arbitrated seats plus "More artefacts…", the cap of fourteen it has had since
|
|
403
|
+
* the OWM pair tipped this framework over — so what the decision buys is that
|
|
404
|
+
* a newly-drawn artefact is ELIGIBLE for a seat, and recency and frequency
|
|
405
|
+
* decide who gets one. Nothing becomes unreachable: the catalogue lists
|
|
406
|
+
* everything, which is the registry's own invariant.
|
|
407
|
+
*
|
|
408
|
+
* `addAreaRect` and `addAreaPolygon` are the two after those, and they land
|
|
409
|
+
* under that same amendment rather than reopening it: they nominate like every
|
|
410
|
+
* other Wardley artefact, the row is still thirteen arbitrated seats plus "More
|
|
411
|
+
* artefacts…", and the catalogue grows by a section of its own.
|
|
328
412
|
*/
|
|
329
413
|
export const wardleyCommands = [
|
|
330
414
|
...toolboxCommands,
|
|
@@ -347,6 +431,11 @@ export const wardleyCommandIcons = {
|
|
|
347
431
|
'wardley.link': wardleyLinkIcon,
|
|
348
432
|
'wardley.arrow': wardleyArrowIcon,
|
|
349
433
|
'wardley.inertia': wardleyInertiaIcon,
|
|
434
|
+
'wardley.porter': wardleyPorterIcon,
|
|
435
|
+
'wardley.accelerator': wardleyAcceleratorIcon,
|
|
436
|
+
'wardley.decelerator': wardleyDeceleratorIcon,
|
|
437
|
+
'wardley.area-rect': wardleyAreaRectIcon,
|
|
438
|
+
'wardley.area-polygon': wardleyAreaPolygonIcon,
|
|
350
439
|
'wardley.import-owm': wardleyImportOwmIcon,
|
|
351
440
|
'wardley.export-owm': wardleyExportOwmIcon,
|
|
352
441
|
'wardley.import-svg': wardleyImportSvgIcon,
|
package/dist/export.js
CHANGED
|
@@ -336,6 +336,10 @@ function labelAnchors(node, label) {
|
|
|
336
336
|
*
|
|
337
337
|
* A pipeline HANDLE never claims a label: it sits on the body's top edge, right
|
|
338
338
|
* under the name the body owns, and would win every pipeline's name from it.
|
|
339
|
+
* A Porter's-forces glyph never claims one either, and for a sharper reason: it
|
|
340
|
+
* HAS no name — its one letter is the shape's own inner text — so any label it
|
|
341
|
+
* matched would be some neighbouring artefact's, taken out of that artefact's
|
|
342
|
+
* mouth and then thrown away, since a force is not written to the file at all.
|
|
339
343
|
*/
|
|
340
344
|
function matchLabels(nodes, labels) {
|
|
341
345
|
const pairs = [];
|
|
@@ -343,8 +347,15 @@ function matchLabels(nodes, labels) {
|
|
|
343
347
|
// A handle sits on the pipeline body's top edge, directly under the name
|
|
344
348
|
// the BODY owns, and would win it from the body every time. A roleless node
|
|
345
349
|
// is a glyph's own wiring (a market's three inner dots) and names nothing.
|
|
346
|
-
|
|
350
|
+
// An AREA claims none either, and it is the one that would do real damage:
|
|
351
|
+
// a zone is drawn around the components it groups, so its box sits next to
|
|
352
|
+
// every label on the map and would win one from the artefact it belongs to.
|
|
353
|
+
if (node.kind === 'handle' ||
|
|
354
|
+
node.kind === 'porter' ||
|
|
355
|
+
node.kind === 'area' ||
|
|
356
|
+
node.role === undefined) {
|
|
347
357
|
return;
|
|
358
|
+
}
|
|
348
359
|
const nodeBox = boxOf(node);
|
|
349
360
|
labels.forEach((label, labelIndex) => {
|
|
350
361
|
const labelBox = boxOf(label);
|
|
@@ -380,7 +391,17 @@ export function textOf(element) {
|
|
|
380
391
|
* method is the `(build)` / `(buy)` / `(outsource)` decorator on a component
|
|
381
392
|
* line — so a Labre method node is written as a component and the export warns
|
|
382
393
|
* that the method itself could not be said. `pipeline` and `handle` are absent
|
|
383
|
-
* because a pipeline is written from its BODY, in its own section
|
|
394
|
+
* because a pipeline is written from its BODY, in its own section — and
|
|
395
|
+
* `porter` because OWM has no word for an external competition force at all —
|
|
396
|
+
* and `area` for the same silence, since the format has no word for a zone of
|
|
397
|
+
* the map either. The node loop skips both before this table is consulted, so a
|
|
398
|
+
* kind's absence here is never the reason it is left out.
|
|
399
|
+
*
|
|
400
|
+
* The climate annotations DO have a word, and OWM's own spelling of the second
|
|
401
|
+
* one is `deaccelerator` — not a typo on this side but the keyword the
|
|
402
|
+
* reference parser claims, so writing `decelerator` would produce a line no
|
|
403
|
+
* other Wardley tool reads. Both are written like a component line, `keyword
|
|
404
|
+
* name [visibility, evolution]`, which is exactly what they are in that format.
|
|
384
405
|
*/
|
|
385
406
|
const OWM_KEYWORD_OF_KIND = {
|
|
386
407
|
component: 'component',
|
|
@@ -388,6 +409,8 @@ const OWM_KEYWORD_OF_KIND = {
|
|
|
388
409
|
market: 'market',
|
|
389
410
|
ecosystem: 'ecosystem',
|
|
390
411
|
method: 'component',
|
|
412
|
+
accelerator: 'accelerator',
|
|
413
|
+
decelerator: 'deaccelerator',
|
|
391
414
|
};
|
|
392
415
|
/** What one element carried from the file it came out of, if anything. */
|
|
393
416
|
function carriedOf(element) {
|
|
@@ -506,11 +529,30 @@ export function exportWardleyOwmWithWarnings(board, options = {}) {
|
|
|
506
529
|
const nodeLines = [];
|
|
507
530
|
const pipelineLines = [];
|
|
508
531
|
const methodNodes = [];
|
|
532
|
+
const porterNodes = [];
|
|
533
|
+
const areaNodes = [];
|
|
509
534
|
for (const node of board.nodes) {
|
|
510
535
|
if (node.role === undefined)
|
|
511
536
|
continue; // a glyph's own wiring, not an artefact
|
|
512
537
|
if (node.kind === 'handle')
|
|
513
538
|
continue;
|
|
539
|
+
// Skipped BEFORE `nameOf`, and that is the whole reason it is a branch of
|
|
540
|
+
// its own rather than a missing entry in `OWM_KEYWORD_OF_KIND`: a force
|
|
541
|
+
// carries no name, so asking for one would christen it "Component 3" and
|
|
542
|
+
// count it among the artefacts that lost theirs — a warning about a loss
|
|
543
|
+
// that never happened, on a line the file does not contain.
|
|
544
|
+
if (node.kind === 'porter') {
|
|
545
|
+
porterNodes.push(node.id);
|
|
546
|
+
continue;
|
|
547
|
+
}
|
|
548
|
+
// Same branch, same reason: an area's name is its own inner text rather
|
|
549
|
+
// than a label beside it, so `nameOf` would find none and christen the zone
|
|
550
|
+
// "Component 4" — counting a nameless artefact among the ones that lost
|
|
551
|
+
// their name, on a line the file does not contain either way.
|
|
552
|
+
if (node.kind === 'area') {
|
|
553
|
+
areaNodes.push(node.id);
|
|
554
|
+
continue;
|
|
555
|
+
}
|
|
514
556
|
if (twins.has(node.id))
|
|
515
557
|
continue; // written by its `evolve` line
|
|
516
558
|
const name = nameOf(node);
|
|
@@ -597,6 +639,12 @@ export function exportWardleyOwmWithWarnings(board, options = {}) {
|
|
|
597
639
|
if (methodNodes.length > 0) {
|
|
598
640
|
warnings.push(`${methodNodes.length} component${methodNodes.length === 1 ? ' carries' : 's carry'} a method (build / buy / outsource). OWM writes a method as a decorator this export cannot tell apart, so ${methodNodes.length === 1 ? 'it was' : 'they were'} written as plain components.`);
|
|
599
641
|
}
|
|
642
|
+
if (porterNodes.length > 0) {
|
|
643
|
+
warnings.push(`${porterNodes.length} Porter's forces glyph${porterNodes.length === 1 ? '' : 's'} could not be written: OWM has no word for an external competition force, so ${porterNodes.length === 1 ? 'it was' : 'they were'} left out.`);
|
|
644
|
+
}
|
|
645
|
+
if (areaNodes.length > 0) {
|
|
646
|
+
warnings.push(`${areaNodes.length} area${areaNodes.length === 1 ? '' : 's'} could not be written: OWM has no word for a zone of the map, so ${areaNodes.length === 1 ? 'it was' : 'they were'} left out.`);
|
|
647
|
+
}
|
|
600
648
|
if (looseLinks.length > 0) {
|
|
601
649
|
warnings.push(`${looseLinks.length} link${looseLinks.length === 1 ? ' has an end' : 's have ends'} that is loose or attached to something that is not a Wardley artefact. A link names two components, so ${looseLinks.length === 1 ? 'it was' : 'they were'} left out.`);
|
|
602
650
|
}
|
package/dist/import.d.ts
CHANGED
|
@@ -44,10 +44,11 @@ import { OWM_SCOPE, WARDLEY_OWM_FORMAT_ID } from './export.js';
|
|
|
44
44
|
* ## Three states, and the middle one is where the file survives
|
|
45
45
|
*
|
|
46
46
|
* **Mapped** is what the pack draws: `component`, `anchor`, `market`,
|
|
47
|
-
* `ecosystem`, `
|
|
47
|
+
* `ecosystem`, `accelerator`, `deaccelerator`, `pipeline`, `note`, `evolve`,
|
|
48
|
+
* `title`, and the `->` links.
|
|
48
49
|
* **Carried** is every other statement — `style`, `annotation`, `attitudes`
|
|
49
50
|
* (`pioneers` / `settlers` / `townplanners`), `submap`, `url`, `size`,
|
|
50
|
-
*
|
|
51
|
+
* the axis-label overrides, the flow links (`+>`, `+<`, `+<>`,
|
|
51
52
|
* `+'…'>`), a link carrying a `;` context, and every `//` comment — kept
|
|
52
53
|
* verbatim in `interchange.owm.children['@document']` on the map's background
|
|
53
54
|
* element (D6) and written back, in order, at the end of the next export.
|
|
@@ -67,6 +68,7 @@ import { OWM_SCOPE, WARDLEY_OWM_FORMAT_ID } from './export.js';
|
|
|
67
68
|
* | what | state | after a round trip |
|
|
68
69
|
* | ---------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
69
70
|
* | `component` / `anchor` / `market` / `ecosystem` and their coordinates | mapped | drawn, and written back from the drawing, to two decimals |
|
|
71
|
+
* | `accelerator` / `deaccelerator` and their coordinates | mapped | drawn as the climate arrow, and written back under OWM's own spelling (`deaccelerator` for a decelerator) |
|
|
70
72
|
* | the NAME, which in this format is the identity | mapped | given back verbatim, quoted exactly as it needs to be — the fixed point |
|
|
71
73
|
* | `pipeline Name [m1, m2]` | mapped | drawn as the body + handle composite; `[m1, m2]` written back from the body's two edges |
|
|
72
74
|
* | `note Text [v, e]` | mapped | a free text on the map, written back from its centre |
|
|
@@ -74,7 +76,7 @@ import { OWM_SCOPE, WARDLEY_OWM_FORMAT_ID } from './export.js';
|
|
|
74
76
|
* | `A->B` links | mapped | a `wardley:dependency` connector, consumer → what it needs, never inverted |
|
|
75
77
|
* | `title` | mapped | consumed as the board's own name; written back from the board's name (see the two rows below) |
|
|
76
78
|
* | 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`,
|
|
79
|
+
* | `style`, `annotation(s)`, `attitudes`, `submap`, `url`, `size`, the axis-label overrides | carried | invisible on the canvas, re-emitted verbatim at the end of the file |
|
|
78
80
|
* | flow links (`+>`, `+<`, `+<>`, `+'…'>`) and a link with a `;` context | carried | not drawn — a flow is not a dependency — and re-emitted verbatim |
|
|
79
81
|
* | a `pipeline` with a `{ … }` body | carried | the block is kept whole and written back whole; the pack draws no pipeline children |
|
|
80
82
|
* | `//` comments | carried | re-emitted verbatim, at the end |
|
package/dist/import.js
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { ConnectorMode, FontFamily, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
1
|
+
import { ConnectorMode, FontFamily, FontWeight, PointStyle, ShapeStyle, StrokeStyle, TextAlign, } from '@formicoidea/labre-core/model';
|
|
2
2
|
import { WARDLEY_BACKGROUND } from './background.js';
|
|
3
3
|
import { OWM_DEFAULT_MAP_HEIGHT, OWM_DEFAULT_MAP_WIDTH, OWM_LABEL_HEIGHT, OWM_LABEL_WIDTH, OWM_SCOPE, OWM_TAIL_ATTR, OWM_KEYWORDS, OWM_TITLE_ATTR, owmDefaultPlot, owmPointOf, WARDLEY_OWM_FORMAT_ID, } from './export.js';
|
|
4
4
|
import { HANDLE_SIZE, LABEL_GAP, 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, ECOSYSTEM_SIZE, NODE_FILL, NODE_SIZE, NODE_STROKE, NODE_STROKE_WIDTH, PIPELINE_FILL, PIPELINE_HEIGHT, WARDLEY_RED, } from './node/consts.js';
|
|
5
|
+
import { WARDLEY_NODE_SIZE, wardleyNodeProps } from './presets.js';
|
|
5
6
|
import { WARDLEY_ROLE } from './roles.js';
|
|
6
7
|
/**
|
|
7
8
|
* An OnlineWardleyMaps (OWM) DSL document, read as a Wardley map — the inverse
|
|
@@ -47,10 +48,11 @@ import { WARDLEY_ROLE } from './roles.js';
|
|
|
47
48
|
* ## Three states, and the middle one is where the file survives
|
|
48
49
|
*
|
|
49
50
|
* **Mapped** is what the pack draws: `component`, `anchor`, `market`,
|
|
50
|
-
* `ecosystem`, `
|
|
51
|
+
* `ecosystem`, `accelerator`, `deaccelerator`, `pipeline`, `note`, `evolve`,
|
|
52
|
+
* `title`, and the `->` links.
|
|
51
53
|
* **Carried** is every other statement — `style`, `annotation`, `attitudes`
|
|
52
54
|
* (`pioneers` / `settlers` / `townplanners`), `submap`, `url`, `size`,
|
|
53
|
-
*
|
|
55
|
+
* the axis-label overrides, the flow links (`+>`, `+<`, `+<>`,
|
|
54
56
|
* `+'…'>`), a link carrying a `;` context, and every `//` comment — kept
|
|
55
57
|
* verbatim in `interchange.owm.children['@document']` on the map's background
|
|
56
58
|
* element (D6) and written back, in order, at the end of the next export.
|
|
@@ -70,6 +72,7 @@ import { WARDLEY_ROLE } from './roles.js';
|
|
|
70
72
|
* | what | state | after a round trip |
|
|
71
73
|
* | ---------------------------------------------------------------------- | ----------- | ---------------------------------------------------------------------------------------------------------------------- |
|
|
72
74
|
* | `component` / `anchor` / `market` / `ecosystem` and their coordinates | mapped | drawn, and written back from the drawing, to two decimals |
|
|
75
|
+
* | `accelerator` / `deaccelerator` and their coordinates | mapped | drawn as the climate arrow, and written back under OWM's own spelling (`deaccelerator` for a decelerator) |
|
|
73
76
|
* | the NAME, which in this format is the identity | mapped | given back verbatim, quoted exactly as it needs to be — the fixed point |
|
|
74
77
|
* | `pipeline Name [m1, m2]` | mapped | drawn as the body + handle composite; `[m1, m2]` written back from the body's two edges |
|
|
75
78
|
* | `note Text [v, e]` | mapped | a free text on the map, written back from its centre |
|
|
@@ -77,7 +80,7 @@ import { WARDLEY_ROLE } from './roles.js';
|
|
|
77
80
|
* | `A->B` links | mapped | a `wardley:dependency` connector, consumer → what it needs, never inverted |
|
|
78
81
|
* | `title` | mapped | consumed as the board's own name; written back from the board's name (see the two rows below) |
|
|
79
82
|
* | 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 |
|
|
80
|
-
* | `style`, `annotation(s)`, `attitudes`, `submap`, `url`, `size`,
|
|
83
|
+
* | `style`, `annotation(s)`, `attitudes`, `submap`, `url`, `size`, the axis-label overrides | carried | invisible on the canvas, re-emitted verbatim at the end of the file |
|
|
81
84
|
* | flow links (`+>`, `+<`, `+<>`, `+'…'>`) and a link with a `;` context | carried | not drawn — a flow is not a dependency — and re-emitted verbatim |
|
|
82
85
|
* | a `pipeline` with a `{ … }` body | carried | the block is kept whole and written back whole; the pack draws no pipeline children |
|
|
83
86
|
* | `//` comments | carried | re-emitted verbatim, at the end |
|
|
@@ -217,12 +220,21 @@ const MATURITY = String.raw `[0-9]*\.[0-9]+`;
|
|
|
217
220
|
const BEFORE_MATURITY = new RegExp(String.raw `^(.*?)(\s+${MATURITY})(\s.*)?$`);
|
|
218
221
|
const LEADING_MATURITY = new RegExp(String.raw `^\s*(${MATURITY})`);
|
|
219
222
|
/* ── The statements a document is made of ─────────────────────────────── */
|
|
220
|
-
/**
|
|
223
|
+
/**
|
|
224
|
+
* The keywords that declare a positioned artefact, and the kind each is.
|
|
225
|
+
*
|
|
226
|
+
* `deaccelerator` is OWM's own spelling of a decelerator — the keyword the
|
|
227
|
+
* reference parser claims — so the KEY is the format's word and the VALUE is
|
|
228
|
+
* this canvas's kind. It is the one row where the two differ, and the reason
|
|
229
|
+
* this is a table rather than an identity.
|
|
230
|
+
*/
|
|
221
231
|
const NODE_KEYWORDS = {
|
|
222
232
|
component: 'component',
|
|
223
233
|
anchor: 'anchor',
|
|
224
234
|
market: 'market',
|
|
225
235
|
ecosystem: 'ecosystem',
|
|
236
|
+
accelerator: 'accelerator',
|
|
237
|
+
deaccelerator: 'decelerator',
|
|
226
238
|
};
|
|
227
239
|
/* ── The reader ───────────────────────────────────────────────────────── */
|
|
228
240
|
/**
|
|
@@ -724,7 +736,12 @@ function payload(parts) {
|
|
|
724
736
|
return { [WARDLEY_OWM_FORMAT_ID]: carried };
|
|
725
737
|
}
|
|
726
738
|
/** A name, as the free text element this canvas writes one as. */
|
|
727
|
-
function label(text, x, y, textAlign, color = NODE_STROKE
|
|
739
|
+
function label(text, x, y, textAlign, color = NODE_STROKE,
|
|
740
|
+
// Regular by default, because that is what a value-chain name is. The one
|
|
741
|
+
// caller that passes anything else is the climate arrow below, and it does so
|
|
742
|
+
// for the reason `createWardleyAccelerator` does: an imported map and a drawn
|
|
743
|
+
// one must be the same document, down to the weight of the words.
|
|
744
|
+
fontWeight = FontWeight.Regular) {
|
|
728
745
|
return {
|
|
729
746
|
type: 'text',
|
|
730
747
|
text,
|
|
@@ -734,12 +751,40 @@ function label(text, x, y, textAlign, color = NODE_STROKE) {
|
|
|
734
751
|
color,
|
|
735
752
|
fontFamily: FontFamily.Inter,
|
|
736
753
|
fontSize: LABEL_FONT_SIZE,
|
|
754
|
+
fontWeight,
|
|
737
755
|
textAlign,
|
|
738
756
|
xywh: `[${x},${y},${OWM_LABEL_WIDTH},${OWM_LABEL_HEIGHT}]`,
|
|
739
757
|
};
|
|
740
758
|
}
|
|
741
759
|
/** The circle (or composite) one positioned statement draws, plus its name. */
|
|
742
760
|
function artefact(keyword, name, cx, cy, tail, mintHandle) {
|
|
761
|
+
// The two climate annotations are the only statements this reader draws as
|
|
762
|
+
// something other than a circle: a fat polygon, at the canonical size the
|
|
763
|
+
// palette gives one, with its name on the side its shaft is on. The presets
|
|
764
|
+
// own both facts, so a map that arrives from a file and one drawn by hand are
|
|
765
|
+
// the same element.
|
|
766
|
+
const kind = NODE_KEYWORDS[keyword];
|
|
767
|
+
if (kind === 'accelerator' || kind === 'decelerator') {
|
|
768
|
+
const { w, h } = WARDLEY_NODE_SIZE[kind];
|
|
769
|
+
const arrow = {
|
|
770
|
+
...wardleyNodeProps(kind, {
|
|
771
|
+
xywh: `[${cx - w / 2},${cy - h / 2},${w},${h}]`,
|
|
772
|
+
}),
|
|
773
|
+
interchange: payload({ id: name, tail }),
|
|
774
|
+
};
|
|
775
|
+
const rightwards = kind === 'accelerator';
|
|
776
|
+
return [
|
|
777
|
+
arrow,
|
|
778
|
+
label(name, rightwards
|
|
779
|
+
? cx + w / 2 + LABEL_GAP
|
|
780
|
+
: cx - w / 2 - LABEL_GAP - OWM_LABEL_WIDTH, cy - OWM_LABEL_HEIGHT / 2, rightwards ? TextAlign.Left : TextAlign.Right, NODE_STROKE,
|
|
781
|
+
// SemiBold, exactly as the sub-menu draws one. The recette of #212
|
|
782
|
+
// caught the two paths disagreeing: a climate arrow is an annotation
|
|
783
|
+
// laid over a map already full of names, and where it came from is not
|
|
784
|
+
// something the reader should be able to see.
|
|
785
|
+
FontWeight.SemiBold),
|
|
786
|
+
];
|
|
787
|
+
}
|
|
743
788
|
const diameter = keyword === 'anchor'
|
|
744
789
|
? OWM_ANCHOR_SIZE
|
|
745
790
|
: keyword === 'market'
|
|
@@ -749,8 +794,8 @@ function artefact(keyword, name, cx, cy, tail, mintHandle) {
|
|
|
749
794
|
: NODE_SIZE;
|
|
750
795
|
const circle = {
|
|
751
796
|
type: 'wardleyNode',
|
|
752
|
-
kind
|
|
753
|
-
role: WARDLEY_ROLE[
|
|
797
|
+
kind,
|
|
798
|
+
role: WARDLEY_ROLE[kind],
|
|
754
799
|
shapeType: 'ellipse',
|
|
755
800
|
filled: true,
|
|
756
801
|
fillColor: NODE_FILL,
|
package/dist/index.d.ts
CHANGED
|
@@ -2,11 +2,14 @@ export { WARDLEY_ROLE, WARDLEY_ROLES, type WardleyRole, type WardleyRoleId, } fr
|
|
|
2
2
|
export { WARDLEY_AUDIT_CRITERIA } from './audit-criteria.js';
|
|
3
3
|
export { wardleyCommandIcons, wardleyCommands } from './commands.js';
|
|
4
4
|
export { wardleyTranslationEntries } from './translations.js';
|
|
5
|
-
export { WARDLEY_NATURE, WARDLEY_NATURE_TAG_ID, WARDLEY_TAG_DEFS, } from './natures.js';
|
|
5
|
+
export { WARDLEY_COMPETITION, WARDLEY_COMPETITION_TAG_ID, WARDLEY_NATURE, WARDLEY_NATURE_TAG_ID, WARDLEY_TAG_DEFS, } from './natures.js';
|
|
6
|
+
export { competitionOfPorterLetter, porterLetterOfCompetition, type WardleyPorterLetter, } from './porter-competition.js';
|
|
6
7
|
export { WARDLEY_PROFILES } from './profiles.js';
|
|
7
8
|
export { WARDLEY_NUDGES } from './nudges.js';
|
|
8
9
|
export { WARDLEY_NAMING_CONVENTIONS, WARDLEY_READING } from './reading.js';
|
|
9
10
|
export { WARDLEY_RULES } from './rules.js';
|
|
11
|
+
export { WARDLEY_AREA_SIZE, wardleyAreaBox, wardleyAreaProps, type WardleyAreaShape, type WardleyArtefactKind, wardleyCanonicalBox, wardleyCenteredBox, wardleyHandleBox, wardleyHandleProps, wardleyMarketDotBoxes, wardleyMarketDotProps, wardleyMarketLinkPairs, wardleyMarketLinkProps, WARDLEY_NODE_LABEL, WARDLEY_NODE_SIZE, type WardleyLabelledKind, WARDLEY_MORPHABLE_KINDS, wardleyMorphClears, wardleyMorphProps, wardleyNodeProps, type WardleyPorterArrow, wardleyPorterArrowProps, wardleyPorterArrows, wardleyPorterLetterProps, } from './presets.js';
|
|
12
|
+
export { WARDLEY_MORPH_FAMILIES, WARDLEY_MORPH_SPEC, type WardleyMorphKind, wardleyMorphComposite, wardleyMorphedLabel, wardleyNodeOfComponent, } from './morph.js';
|
|
10
13
|
/**
|
|
11
14
|
* The map's own frame, declared. Public because a host that lays elements out
|
|
12
15
|
* against the plot — labre-mcp does, and so does anything that generates a map
|
package/dist/index.js
CHANGED
|
@@ -2,11 +2,26 @@ export { WARDLEY_ROLE, WARDLEY_ROLES, } from './roles.js';
|
|
|
2
2
|
export { WARDLEY_AUDIT_CRITERIA } from './audit-criteria.js';
|
|
3
3
|
export { wardleyCommandIcons, wardleyCommands } from './commands.js';
|
|
4
4
|
export { wardleyTranslationEntries } from './translations.js';
|
|
5
|
-
export { WARDLEY_NATURE, WARDLEY_NATURE_TAG_ID, WARDLEY_TAG_DEFS, } from './natures.js';
|
|
5
|
+
export { WARDLEY_COMPETITION, WARDLEY_COMPETITION_TAG_ID, WARDLEY_NATURE, WARDLEY_NATURE_TAG_ID, WARDLEY_TAG_DEFS, } from './natures.js';
|
|
6
|
+
// The letter and the tag, related in both directions. Exported for the reason
|
|
7
|
+
// the presets are: a host laying out or reading a map without an editor —
|
|
8
|
+
// labre-mcp does — must be able to say which force a glyph marks without
|
|
9
|
+
// re-deriving the mapping from the three labels.
|
|
10
|
+
export { competitionOfPorterLetter, porterLetterOfCompetition, } from './porter-competition.js';
|
|
6
11
|
export { WARDLEY_PROFILES } from './profiles.js';
|
|
7
12
|
export { WARDLEY_NUDGES } from './nudges.js';
|
|
8
13
|
export { WARDLEY_NAMING_CONVENTIONS, WARDLEY_READING } from './reading.js';
|
|
9
14
|
export { WARDLEY_RULES } from './rules.js';
|
|
15
|
+
// What a Wardley artefact is BORN as, shared by the palette and — since the
|
|
16
|
+
// morph — by the toolbar that says one artefact differently, so the two can
|
|
17
|
+
// never disagree about what a market looks like. The sizes are the notation
|
|
18
|
+
// itself, which is why a host laying out a map without an editor needs them.
|
|
19
|
+
export { WARDLEY_AREA_SIZE, wardleyAreaBox, wardleyAreaProps, wardleyCanonicalBox, wardleyCenteredBox, wardleyHandleBox, wardleyHandleProps, wardleyMarketDotBoxes, wardleyMarketDotProps, wardleyMarketLinkPairs, wardleyMarketLinkProps, WARDLEY_NODE_LABEL, WARDLEY_NODE_SIZE, WARDLEY_MORPHABLE_KINDS, wardleyMorphClears, wardleyMorphProps, wardleyNodeProps, wardleyPorterArrowProps, wardleyPorterArrows, wardleyPorterLetterProps, } from './presets.js';
|
|
20
|
+
// What an artefact may BECOME: the declared family, the spec the generic morph
|
|
21
|
+
// module is registered with, the resolution from a selected group to the node
|
|
22
|
+
// inside it, and the two rules a composite morph owes its own parts. Data a
|
|
23
|
+
// host can read without an editor.
|
|
24
|
+
export { WARDLEY_MORPH_FAMILIES, WARDLEY_MORPH_SPEC, wardleyMorphComposite, wardleyMorphedLabel, wardleyNodeOfComponent, } from './morph.js';
|
|
10
25
|
/**
|
|
11
26
|
* The map's own frame, declared. Public because a host that lays elements out
|
|
12
27
|
* against the plot — labre-mcp does, and so does anything that generates a map
|
package/dist/legend.d.ts
CHANGED
|
@@ -1,11 +1,37 @@
|
|
|
1
1
|
import { type WardleyBackgroundElementModel } from '@formicoidea/labre-core/model';
|
|
2
2
|
import type { BlockStdScope } from '@formicoidea/labre-core/std';
|
|
3
|
+
/** A box in the panel, relative to the panel's top-left, plus its words. */
|
|
4
|
+
interface PorterPanelBox {
|
|
5
|
+
xywh: [number, number, number, number];
|
|
6
|
+
label: string;
|
|
7
|
+
}
|
|
8
|
+
interface PorterPanelLayout {
|
|
9
|
+
w: number;
|
|
10
|
+
h: number;
|
|
11
|
+
title: [number, number, number, number];
|
|
12
|
+
/** The glyph's centre. */
|
|
13
|
+
center: [number, number];
|
|
14
|
+
radius: number;
|
|
15
|
+
boxes: PorterPanelBox[];
|
|
16
|
+
caption: [number, number, number, number];
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The panel's geometry for a given width — the PO's reference drawing, as
|
|
20
|
+
* numbers.
|
|
21
|
+
*
|
|
22
|
+
* A pure function so the layout can be asserted without a surface, and so the
|
|
23
|
+
* one thing that must hold — that none of the four boxes overlaps the glyph
|
|
24
|
+
* they surround — is checkable rather than eyeballed.
|
|
25
|
+
*/
|
|
26
|
+
export declare function porterPanelLayout(w: number): PorterPanelLayout;
|
|
3
27
|
/**
|
|
4
28
|
* Build a "Legend" group from real, editable elements (white rect frame +
|
|
5
29
|
* "Legend" text + one row of [real component glyph + description text] per
|
|
6
30
|
* Wardley component TYPE present inside the background's perimeter + a
|
|
7
|
-
* gradient-meaning block when the background is a gradient variant
|
|
31
|
+
* gradient-meaning block when the background is a gradient variant + the
|
|
32
|
+
* five-forces panel when a Porter's-forces glyph is on the map). A snapshot
|
|
8
33
|
* is created on each call; everything is grouped so it can be moved / resized /
|
|
9
34
|
* edited and is dropped bottom-left of the background.
|
|
10
35
|
*/
|
|
11
36
|
export declare function createWardleyLegend(std: BlockStdScope, bg: WardleyBackgroundElementModel): void;
|
|
37
|
+
export {};
|