@formicoidea/labre-framework-wardley 0.40.0 → 0.42.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/LICENSE +384 -0
- package/dist/actions.d.ts +80 -13
- package/dist/actions.js +167 -87
- package/dist/commands.js +52 -31
- package/dist/element-view.d.ts +20 -34
- package/dist/element-view.js +23 -139
- package/dist/import.d.ts +28 -0
- package/dist/import.js +66 -7
- package/dist/index.d.ts +1 -0
- package/dist/index.js +4 -0
- package/dist/legend.d.ts +107 -12
- package/dist/legend.js +485 -471
- package/dist/morph.d.ts +15 -3
- package/dist/morph.js +90 -10
- package/dist/natures.js +19 -0
- package/dist/node/consts.d.ts +15 -13
- package/dist/node/consts.js +15 -13
- package/dist/node/node-renderer.js +12 -1
- package/dist/presets.d.ts +71 -1
- package/dist/presets.js +83 -1
- package/dist/templates/index.d.ts +10 -0
- package/dist/templates/index.js +40 -8
- package/dist/templates/maps.d.ts +94 -0
- package/dist/templates/maps.js +148 -27
- package/dist/toolbar/config.d.ts +34 -7
- package/dist/toolbar/config.js +109 -34
- package/dist/toolbar/node-config.d.ts +34 -2
- package/dist/toolbar/node-config.js +112 -9
- package/dist/toolbar/wardley-senior-button.d.ts +8 -0
- package/dist/toolbar/wardley-senior-button.js +12 -2
- package/dist/translations.js +81 -1
- package/dist/view.js +19 -15
- package/package.json +2 -2
package/dist/morph.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type MorphSpec } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { WardleyNodeElementModel } from '@formicoidea/labre-core/model';
|
|
3
|
+
import type { BlockStdScope } from '@formicoidea/labre-core/std';
|
|
3
4
|
import type { GfxPrimitiveElementModel } from '@formicoidea/labre-core/std/gfx';
|
|
4
5
|
/**
|
|
5
6
|
* What a Wardley artefact may BECOME — the declaration behind the "Change type"
|
|
@@ -81,8 +82,17 @@ export declare function wardleyNodeOfComponent(model: GfxPrimitiveElementModel):
|
|
|
81
82
|
*
|
|
82
83
|
* Pure and total over every string: the input is a canvas text element somebody
|
|
83
84
|
* may have typed anything into.
|
|
85
|
+
*
|
|
86
|
+
* `std` is OPTIONAL: "untouched" means equal to the English prompt OR to the
|
|
87
|
+
* host's own resolved wording for `from`'s kind, and the target's prompt is
|
|
88
|
+
* likewise resolved through the host when one is given — mirroring
|
|
89
|
+
* `c4MorphedTypeLine`'s fix for the identical class of gap. With no `std`
|
|
90
|
+
* this behaves exactly as it always has (English only). The morph toolbar's
|
|
91
|
+
* `afterMorph` callback (`MorphSpec`, `packages/affine/blocks/surface`) now
|
|
92
|
+
* hands its callee the inserting editor's `std`, so `rewriteLabel` below
|
|
93
|
+
* passes it straight through.
|
|
84
94
|
*/
|
|
85
|
-
export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyMorphKind, rawText: string | null | undefined): string | null;
|
|
95
|
+
export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyMorphKind, rawText: string | null | undefined, std?: BlockStdScope): string | null;
|
|
86
96
|
/**
|
|
87
97
|
* Everything the morph owes a Wardley composite beyond the props patch — the
|
|
88
98
|
* `afterMorph` half of the spec, run inside the generic module's one
|
|
@@ -98,7 +108,9 @@ export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyM
|
|
|
98
108
|
* dots crammed into it. So the target's canonical size is applied, CENTRED on
|
|
99
109
|
* where the artefact already stands: the thing does not move, it becomes the
|
|
100
110
|
* size its notation says it is. The label keeps its own place, which is the
|
|
101
|
-
* user's
|
|
111
|
+
* user's — with the one exception {@link relocateLabel} states: a name still
|
|
112
|
+
* standing where creation put it follows the target kind when that kind carries
|
|
113
|
+
* its name somewhere else, or the new drawing lands on top of the old words.
|
|
102
114
|
*
|
|
103
115
|
* ## And why the rest is here rather than in the patch
|
|
104
116
|
*
|
|
@@ -110,7 +122,7 @@ export declare function wardleyMorphedLabel(from: WardleyMorphKind, to: WardleyM
|
|
|
110
122
|
* LEAVING comes apart before the one it is arriving at is built, so a
|
|
111
123
|
* market → pipeline never has three dots and a handle at once.
|
|
112
124
|
*/
|
|
113
|
-
export declare function wardleyMorphComposite(selected: GfxPrimitiveElementModel, from: WardleyMorphKind, to: WardleyMorphKind): void;
|
|
125
|
+
export declare function wardleyMorphComposite(selected: GfxPrimitiveElementModel, from: WardleyMorphKind, to: WardleyMorphKind, std?: BlockStdScope): void;
|
|
114
126
|
/**
|
|
115
127
|
* Wardley's morph declaration, handed to the generic `morphToolbarConfig`.
|
|
116
128
|
*
|
package/dist/morph.js
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { morphLabel, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
2
|
import { ConnectorElementModel, GroupElementModel, TextElementModel, WardleyNodeElementModel, } from '@formicoidea/labre-core/model';
|
|
3
|
+
import { translateKey } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
3
5
|
import { wardleyCommandIcons, wardleyCommands } from './commands.js';
|
|
4
|
-
import { wardleyCanonicalBox, wardleyHandleBox, wardleyHandleProps, wardleyMarketDotBoxes, wardleyMarketDotProps, wardleyMarketLinkPairs, wardleyMarketLinkProps, WARDLEY_NODE_LABEL, wardleyMorphClears, wardleyMorphProps, } from './presets.js';
|
|
6
|
+
import { wardleyCanonicalBox, wardleyHandleBox, wardleyHandleProps, WARDLEY_LABEL_H, WARDLEY_LABEL_W, wardleyLabelBoxFor, wardleyMarketDotBoxes, wardleyMarketDotProps, wardleyMarketLinkPairs, wardleyMarketLinkProps, WARDLEY_NODE_LABEL, wardleyMorphClears, wardleyMorphProps, wardleyNodeLabelKey, } from './presets.js';
|
|
5
7
|
import { WARDLEY_ROLE } from './roles.js';
|
|
6
8
|
/**
|
|
7
9
|
* What a Wardley artefact may BECOME — the declaration behind the "Change type"
|
|
@@ -148,11 +150,26 @@ export function wardleyNodeOfComponent(model) {
|
|
|
148
150
|
*
|
|
149
151
|
* Pure and total over every string: the input is a canvas text element somebody
|
|
150
152
|
* may have typed anything into.
|
|
153
|
+
*
|
|
154
|
+
* `std` is OPTIONAL: "untouched" means equal to the English prompt OR to the
|
|
155
|
+
* host's own resolved wording for `from`'s kind, and the target's prompt is
|
|
156
|
+
* likewise resolved through the host when one is given — mirroring
|
|
157
|
+
* `c4MorphedTypeLine`'s fix for the identical class of gap. With no `std`
|
|
158
|
+
* this behaves exactly as it always has (English only). The morph toolbar's
|
|
159
|
+
* `afterMorph` callback (`MorphSpec`, `packages/affine/blocks/surface`) now
|
|
160
|
+
* hands its callee the inserting editor's `std`, so `rewriteLabel` below
|
|
161
|
+
* passes it straight through.
|
|
151
162
|
*/
|
|
152
|
-
export function wardleyMorphedLabel(from, to, rawText) {
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
163
|
+
export function wardleyMorphedLabel(from, to, rawText, std) {
|
|
164
|
+
const text = (rawText ?? '').trim();
|
|
165
|
+
const fromPrompt = std
|
|
166
|
+
? translateKey(std, wardleyNodeLabelKey(from), WARDLEY_NODE_LABEL[from])
|
|
167
|
+
: WARDLEY_NODE_LABEL[from];
|
|
168
|
+
if (text !== WARDLEY_NODE_LABEL[from] && text !== fromPrompt)
|
|
169
|
+
return null;
|
|
170
|
+
return std
|
|
171
|
+
? translateKey(std, wardleyNodeLabelKey(to), WARDLEY_NODE_LABEL[to])
|
|
172
|
+
: WARDLEY_NODE_LABEL[to];
|
|
156
173
|
}
|
|
157
174
|
/** The centre of an element, from the box it currently occupies. */
|
|
158
175
|
function centreOf(model) {
|
|
@@ -309,12 +326,12 @@ function removeHandle(group, carrier) {
|
|
|
309
326
|
* inside the caller's `captureSync`, so the rewrite is part of the same single
|
|
310
327
|
* ctrl+z as the kind that made it necessary.
|
|
311
328
|
*/
|
|
312
|
-
function rewriteLabel(group, from, to) {
|
|
329
|
+
function rewriteLabel(group, from, to, std) {
|
|
313
330
|
const label = labelOfComposite(group);
|
|
314
331
|
if (!label || label.isLocked())
|
|
315
332
|
return;
|
|
316
333
|
const text = label.text.toString().trim();
|
|
317
|
-
const next = wardleyMorphedLabel(from, to, text);
|
|
334
|
+
const next = wardleyMorphedLabel(from, to, text, std);
|
|
318
335
|
if (next === null || next === text)
|
|
319
336
|
return;
|
|
320
337
|
label.surface.store.transact(() => {
|
|
@@ -322,6 +339,66 @@ function rewriteLabel(group, from, to) {
|
|
|
322
339
|
label.text.insert(0, next);
|
|
323
340
|
});
|
|
324
341
|
}
|
|
342
|
+
/**
|
|
343
|
+
* The offset from a label box's left edge to the point its alignment holds
|
|
344
|
+
* fixed — the edge the words grow from.
|
|
345
|
+
*/
|
|
346
|
+
function anchorOffset(w, textAlign) {
|
|
347
|
+
return textAlign === 'center' ? w / 2 : textAlign === 'right' ? w : 0;
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* Carry the name to where the TARGET kind holds it — and only when it is still
|
|
351
|
+
* where the SOURCE kind's creation put it.
|
|
352
|
+
*
|
|
353
|
+
* ## The bug this repairs
|
|
354
|
+
*
|
|
355
|
+
* A component wears its name to the right of an 18-pixel circle. A pipeline is
|
|
356
|
+
* a 120-wide bar: written onto the same artefact, it is drawn straight OVER
|
|
357
|
+
* those words, and a filled shape answers the pointer on its whole interior
|
|
358
|
+
* while a text answers only on its box — so which of the two got the click
|
|
359
|
+
* depended on where in the label you aimed. The name could not be edited, and
|
|
360
|
+
* dragging it moved the pipeline. Rule W3 forbids precisely that overlap.
|
|
361
|
+
*
|
|
362
|
+
* ## Why only across a change of PLACE
|
|
363
|
+
*
|
|
364
|
+
* The timid rule {@link rewriteLabel} states about the words, applied to the
|
|
365
|
+
* placement: what the author put somewhere is theirs. The three circles all
|
|
366
|
+
* carry their name beside them, so a component growing into a market leaves the
|
|
367
|
+
* words exactly where they were — the circle grows underneath them and that is
|
|
368
|
+
* the documented behaviour. Only the crossing to or from the pipeline changes
|
|
369
|
+
* the PLACE, and only that moves anything.
|
|
370
|
+
*
|
|
371
|
+
* The test is on the point the label's own alignment keeps fixed rather than on
|
|
372
|
+
* its top-left, because typing into a label resizes its box around exactly that
|
|
373
|
+
* point (`edgeless-text-editor`): a renamed label is still where creation put
|
|
374
|
+
* it, and a MOVED one is not.
|
|
375
|
+
*/
|
|
376
|
+
function relocateLabel(group, from, to, cx, cy) {
|
|
377
|
+
const source = wardleyLabelBoxFor(from, cx, cy);
|
|
378
|
+
const target = wardleyLabelBoxFor(to, cx, cy);
|
|
379
|
+
if (source.place === target.place)
|
|
380
|
+
return;
|
|
381
|
+
const label = labelOfComposite(group);
|
|
382
|
+
if (!label || label.isLocked())
|
|
383
|
+
return;
|
|
384
|
+
const [x, y, w, h] = label.deserializedXYWH;
|
|
385
|
+
const sourceAnchorX = source.x + anchorOffset(WARDLEY_LABEL_W, source.textAlign);
|
|
386
|
+
// A pixel of tolerance on each axis: a renamed label's line is measured from
|
|
387
|
+
// the font and lands within a hair of the canonical height this compares to.
|
|
388
|
+
if (Math.abs(x + anchorOffset(w, source.textAlign) - sourceAnchorX) > 1) {
|
|
389
|
+
return;
|
|
390
|
+
}
|
|
391
|
+
if (Math.abs(y + h / 2 - (source.y + WARDLEY_LABEL_H / 2)) > 1)
|
|
392
|
+
return;
|
|
393
|
+
label.surface.updateElement(label.id, {
|
|
394
|
+
// The BOX keeps its own size — the words in it may be the author's, and one
|
|
395
|
+
// shrunk back to the canonical width would clip them.
|
|
396
|
+
xywh: new Bound(target.x +
|
|
397
|
+
anchorOffset(WARDLEY_LABEL_W, target.textAlign) -
|
|
398
|
+
anchorOffset(w, target.textAlign), target.y + WARDLEY_LABEL_H / 2 - h / 2, w, h).serialize(),
|
|
399
|
+
textAlign: target.textAlign,
|
|
400
|
+
});
|
|
401
|
+
}
|
|
325
402
|
/**
|
|
326
403
|
* Everything the morph owes a Wardley composite beyond the props patch — the
|
|
327
404
|
* `afterMorph` half of the spec, run inside the generic module's one
|
|
@@ -337,7 +414,9 @@ function rewriteLabel(group, from, to) {
|
|
|
337
414
|
* dots crammed into it. So the target's canonical size is applied, CENTRED on
|
|
338
415
|
* where the artefact already stands: the thing does not move, it becomes the
|
|
339
416
|
* size its notation says it is. The label keeps its own place, which is the
|
|
340
|
-
* user's
|
|
417
|
+
* user's — with the one exception {@link relocateLabel} states: a name still
|
|
418
|
+
* standing where creation put it follows the target kind when that kind carries
|
|
419
|
+
* its name somewhere else, or the new drawing lands on top of the old words.
|
|
341
420
|
*
|
|
342
421
|
* ## And why the rest is here rather than in the patch
|
|
343
422
|
*
|
|
@@ -349,7 +428,7 @@ function rewriteLabel(group, from, to) {
|
|
|
349
428
|
* LEAVING comes apart before the one it is arriving at is built, so a
|
|
350
429
|
* market → pipeline never has three dots and a handle at once.
|
|
351
430
|
*/
|
|
352
|
-
export function wardleyMorphComposite(selected, from, to) {
|
|
431
|
+
export function wardleyMorphComposite(selected, from, to, std) {
|
|
353
432
|
if (!(selected instanceof GroupElementModel))
|
|
354
433
|
return;
|
|
355
434
|
const carrier = wardleyNodeOfComponent(selected);
|
|
@@ -367,7 +446,8 @@ export function wardleyMorphComposite(selected, from, to) {
|
|
|
367
446
|
addMarketGlyph(selected, cx, cy);
|
|
368
447
|
if (to === 'pipeline')
|
|
369
448
|
addHandle(selected, carrier, cx, cy);
|
|
370
|
-
|
|
449
|
+
relocateLabel(selected, from, to, cx, cy);
|
|
450
|
+
rewriteLabel(selected, from, to, std);
|
|
371
451
|
}
|
|
372
452
|
/**
|
|
373
453
|
* Wardley's morph declaration, handed to the generic `morphToolbarConfig`.
|
package/dist/natures.js
CHANGED
|
@@ -86,11 +86,14 @@ export const WARDLEY_TAG_DEFS = {
|
|
|
86
86
|
packId: 'wardley-core',
|
|
87
87
|
framework: 'wardley',
|
|
88
88
|
label: 'Wardley',
|
|
89
|
+
labelKey: 'com.labre.wardley.tag-pack.label',
|
|
89
90
|
tags: [
|
|
90
91
|
{
|
|
91
92
|
id: WARDLEY_NATURE_TAG_ID,
|
|
92
93
|
label: 'Nature',
|
|
94
|
+
labelKey: 'com.labre.wardley.tag.nature',
|
|
93
95
|
description: 'What kind of thing this component is: an activity, data, a practice or knowledge.',
|
|
96
|
+
descriptionKey: 'com.labre.wardley.tag.nature.description',
|
|
94
97
|
cardinality: 'single',
|
|
95
98
|
appliesTo: [WARDLEY_ROLE.component],
|
|
96
99
|
// No `order`, on either tag of this pack: an absent order sorts by SEED
|
|
@@ -103,29 +106,39 @@ export const WARDLEY_TAG_DEFS = {
|
|
|
103
106
|
{
|
|
104
107
|
id: WARDLEY_NATURE.activity,
|
|
105
108
|
label: 'Activity',
|
|
109
|
+
labelKey: 'com.labre.wardley.tag.nature.activity',
|
|
106
110
|
description: 'Something that is DONE — a step, a service, a process.',
|
|
111
|
+
descriptionKey: 'com.labre.wardley.tag.nature.activity.description',
|
|
107
112
|
},
|
|
108
113
|
{
|
|
109
114
|
id: WARDLEY_NATURE.data,
|
|
110
115
|
label: 'Data',
|
|
116
|
+
labelKey: 'com.labre.wardley.tag.nature.data',
|
|
111
117
|
description: 'Something that is RECORDED — a dataset, a register.',
|
|
118
|
+
descriptionKey: 'com.labre.wardley.tag.nature.data.description',
|
|
112
119
|
},
|
|
113
120
|
{
|
|
114
121
|
id: WARDLEY_NATURE.practice,
|
|
115
122
|
label: 'Practice',
|
|
123
|
+
labelKey: 'com.labre.wardley.tag.nature.practice',
|
|
116
124
|
description: 'A way of doing — a method, a convention, an operating model.',
|
|
125
|
+
descriptionKey: 'com.labre.wardley.tag.nature.practice.description',
|
|
117
126
|
},
|
|
118
127
|
{
|
|
119
128
|
id: WARDLEY_NATURE.knowledge,
|
|
120
129
|
label: 'Knowledge',
|
|
130
|
+
labelKey: 'com.labre.wardley.tag.nature.knowledge',
|
|
121
131
|
description: 'Something that is KNOWN — a model, a theory, a rule.',
|
|
132
|
+
descriptionKey: 'com.labre.wardley.tag.nature.knowledge.description',
|
|
122
133
|
},
|
|
123
134
|
],
|
|
124
135
|
},
|
|
125
136
|
{
|
|
126
137
|
id: WARDLEY_COMPETITION_TAG_ID,
|
|
127
138
|
label: 'Competition',
|
|
139
|
+
labelKey: 'com.labre.wardley.tag.competition',
|
|
128
140
|
description: "Which of Porter's forces this glyph marks: relative competition, a struggle for survival, or a struggle to establish.",
|
|
141
|
+
descriptionKey: 'com.labre.wardley.tag.competition.description',
|
|
129
142
|
cardinality: 'single',
|
|
130
143
|
appliesTo: [WARDLEY_ROLE.porter],
|
|
131
144
|
values: [
|
|
@@ -135,17 +148,23 @@ export const WARDLEY_TAG_DEFS = {
|
|
|
135
148
|
// shows: a reader picking a value here is choosing what the circle
|
|
136
149
|
// will say, not filling in a form about it.
|
|
137
150
|
label: 'Relative competition (R)',
|
|
151
|
+
labelKey: 'com.labre.wardley.tag.competition.relative',
|
|
138
152
|
description: 'Established players competing against one another on a market that exists.',
|
|
153
|
+
descriptionKey: 'com.labre.wardley.tag.competition.relative.description',
|
|
139
154
|
},
|
|
140
155
|
{
|
|
141
156
|
id: WARDLEY_COMPETITION.struggle,
|
|
142
157
|
label: 'Struggle for survival (L)',
|
|
158
|
+
labelKey: 'com.labre.wardley.tag.competition.struggle',
|
|
143
159
|
description: 'Pressure on a player to stay in a market it is already in.',
|
|
160
|
+
descriptionKey: 'com.labre.wardley.tag.competition.struggle.description',
|
|
144
161
|
},
|
|
145
162
|
{
|
|
146
163
|
id: WARDLEY_COMPETITION.establish,
|
|
147
164
|
label: 'Struggle to establish (E)',
|
|
165
|
+
labelKey: 'com.labre.wardley.tag.competition.establish',
|
|
148
166
|
description: 'Pressure on a newcomer trying to get into a market.',
|
|
167
|
+
descriptionKey: 'com.labre.wardley.tag.competition.establish.description',
|
|
149
168
|
},
|
|
150
169
|
],
|
|
151
170
|
},
|
package/dist/node/consts.d.ts
CHANGED
|
@@ -57,29 +57,31 @@ export declare const LINK_STROKE_WIDTH = 2;
|
|
|
57
57
|
/**
|
|
58
58
|
* Wardley red ("future"/evolution) — matches the validated arrow icon.
|
|
59
59
|
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
60
|
+
* A STORED colour: every evolution arrow ever drawn carries this exact value,
|
|
61
|
+
* so it stays a literal and must never be derived from `NOTATION_NEUTRALS` or
|
|
62
|
+
* any other scale — a change of value would leave the maps already drawn with
|
|
63
|
+
* it a different red from the ones drawn after. (It is no longer how an arrow
|
|
64
|
+
* is RECOGNISED: the legend and the rules read `wardley:change-arrow`, so a
|
|
65
|
+
* connector somebody restyled red is not one.)
|
|
64
66
|
*/
|
|
65
67
|
export declare const WARDLEY_RED = "#d6455d";
|
|
66
68
|
/**
|
|
67
69
|
* Dependency link grey.
|
|
68
70
|
*
|
|
69
|
-
*
|
|
70
|
-
*
|
|
71
|
-
* be derived from `NOTATION_NEUTRALS
|
|
72
|
-
*
|
|
71
|
+
* A STORED colour, like {@link WARDLEY_RED} above and for its reason: every
|
|
72
|
+
* dependency link ever drawn carries this exact value, so it stays a literal
|
|
73
|
+
* and must never be derived from `NOTATION_NEUTRALS`. What MAKES a connector a
|
|
74
|
+
* dependency is its role, not its grey.
|
|
73
75
|
*/
|
|
74
76
|
export declare const LINK_GREY = "#666666";
|
|
75
77
|
/**
|
|
76
78
|
* Inertia bar color + size.
|
|
77
79
|
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
81
|
-
*
|
|
82
|
-
*
|
|
80
|
+
* A STORED colour, like the two above: every inertia bar ever drawn carries
|
|
81
|
+
* this exact value. It happens to equal the scale's ink today, but it stays a
|
|
82
|
+
* literal and must never be derived from `NOTATION_NEUTRALS` — a change of the
|
|
83
|
+
* shared ink would repaint the bars already on a map. A rect somebody tinted
|
|
84
|
+
* `#1f2328` is NOT an inertia: `wardley:inertia` is.
|
|
83
85
|
*/
|
|
84
86
|
export declare const INERTIA_COLOR = "#1f2328";
|
|
85
87
|
export declare const INERTIA_SIZE: {
|
package/dist/node/consts.js
CHANGED
|
@@ -55,29 +55,31 @@ export const LINK_STROKE_WIDTH = 2;
|
|
|
55
55
|
/**
|
|
56
56
|
* Wardley red ("future"/evolution) — matches the validated arrow icon.
|
|
57
57
|
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
-
*
|
|
61
|
-
*
|
|
58
|
+
* A STORED colour: every evolution arrow ever drawn carries this exact value,
|
|
59
|
+
* so it stays a literal and must never be derived from `NOTATION_NEUTRALS` or
|
|
60
|
+
* any other scale — a change of value would leave the maps already drawn with
|
|
61
|
+
* it a different red from the ones drawn after. (It is no longer how an arrow
|
|
62
|
+
* is RECOGNISED: the legend and the rules read `wardley:change-arrow`, so a
|
|
63
|
+
* connector somebody restyled red is not one.)
|
|
62
64
|
*/
|
|
63
65
|
export const WARDLEY_RED = '#d6455d';
|
|
64
66
|
/**
|
|
65
67
|
* Dependency link grey.
|
|
66
68
|
*
|
|
67
|
-
*
|
|
68
|
-
*
|
|
69
|
-
* be derived from `NOTATION_NEUTRALS
|
|
70
|
-
*
|
|
69
|
+
* A STORED colour, like {@link WARDLEY_RED} above and for its reason: every
|
|
70
|
+
* dependency link ever drawn carries this exact value, so it stays a literal
|
|
71
|
+
* and must never be derived from `NOTATION_NEUTRALS`. What MAKES a connector a
|
|
72
|
+
* dependency is its role, not its grey.
|
|
71
73
|
*/
|
|
72
74
|
export const LINK_GREY = '#666666';
|
|
73
75
|
/**
|
|
74
76
|
* Inertia bar color + size.
|
|
75
77
|
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
80
|
-
*
|
|
78
|
+
* A STORED colour, like the two above: every inertia bar ever drawn carries
|
|
79
|
+
* this exact value. It happens to equal the scale's ink today, but it stays a
|
|
80
|
+
* literal and must never be derived from `NOTATION_NEUTRALS` — a change of the
|
|
81
|
+
* shared ink would repaint the bars already on a map. A rect somebody tinted
|
|
82
|
+
* `#1f2328` is NOT an inertia: `wardley:inertia` is.
|
|
81
83
|
*/
|
|
82
84
|
export const INERTIA_COLOR = '#1f2328';
|
|
83
85
|
export const INERTIA_SIZE = { w: 8, h: 44 };
|
|
@@ -30,13 +30,24 @@ export const wardleyNode = (model, ctx, matrix, renderer, rc, bound) => {
|
|
|
30
30
|
return;
|
|
31
31
|
const strokeWidth = model.strokeWidth || 1;
|
|
32
32
|
const R = Math.min(w, h) / 2 - strokeWidth / 2;
|
|
33
|
+
// A node dragged smaller than its own stroke leaves the glyph no room: every
|
|
34
|
+
// radius below is a fraction of `R`, and `arc` throws `IndexSizeError` on a
|
|
35
|
+
// negative one rather than clamping — which, since the surface render loop
|
|
36
|
+
// wraps no renderer in a `try`, aborts the rest of the frame. The hatch step
|
|
37
|
+
// is a fraction of `R` too, so `R === 0` would also spin its loop forever.
|
|
38
|
+
// The native circle underneath is already painted; the glyph simply stops.
|
|
39
|
+
if (!(R > 0))
|
|
40
|
+
return;
|
|
33
41
|
const color = renderer.getColorValue(model.strokeColor, DefaultTheme.shapeStrokeColor, true);
|
|
34
42
|
ctx.setTransform(glyphMatrix);
|
|
35
43
|
// ── Anchor: person glyph (clipped to the circle) ────────────────────
|
|
36
44
|
if (model.kind === 'anchor') {
|
|
37
45
|
ctx.save();
|
|
38
46
|
ctx.beginPath();
|
|
39
|
-
|
|
47
|
+
// The clip is inset by half a stroke again, which can land under zero on a
|
|
48
|
+
// node barely wider than its border: clipped to nothing, the glyph paints
|
|
49
|
+
// nothing, which is the honest picture at that size.
|
|
50
|
+
ctx.arc(cx, cy, Math.max(0, R - strokeWidth / 2), 0, Math.PI * 2);
|
|
40
51
|
ctx.clip();
|
|
41
52
|
ctx.lineWidth = strokeWidth;
|
|
42
53
|
ctx.strokeStyle = color;
|
package/dist/presets.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { FontWeight, type WardleyNodeKind } from '@formicoidea/labre-core/model';
|
|
1
|
+
import { FontWeight, PointStyle, StrokeStyle, type WardleyNodeKind } from '@formicoidea/labre-core/model';
|
|
2
2
|
/**
|
|
3
3
|
* What a Wardley artefact IS, as props — the ONE description the creation sites
|
|
4
4
|
* and the morph both read.
|
|
@@ -72,6 +72,27 @@ export type WardleyLabelledKind = Exclude<WardleyArtefactKind, 'porter' | 'area'
|
|
|
72
72
|
* still carries, and the only string a morph is ever allowed to rewrite.
|
|
73
73
|
*/
|
|
74
74
|
export declare const WARDLEY_NODE_LABEL: Record<WardleyLabelledKind, string>;
|
|
75
|
+
/**
|
|
76
|
+
* The i18n key {@link WARDLEY_NODE_LABEL} is the English default of —
|
|
77
|
+
* resolved at placement (`createWardleyNode` / `createWardleyPipeline` /
|
|
78
|
+
* `createWardleyMarket`), never afterwards. "Pipeline" is the PO's glossary
|
|
79
|
+
* term for Wardley: the French proposal IS the English word.
|
|
80
|
+
*
|
|
81
|
+
* ## The morph's placeholder detection, translated
|
|
82
|
+
*
|
|
83
|
+
* `wardleyMorphedLabel` (`morph.ts`) decides whether to refresh an artefact's
|
|
84
|
+
* name across a morph, and accepts an OPTIONAL `std`: given one, it
|
|
85
|
+
* recognises the stored text as "untouched" whether it reads the English
|
|
86
|
+
* prompt or the host's own resolved wording, and resolves the TARGET kind's
|
|
87
|
+
* prompt through the host too (the same fix `c4MorphedTypeLine` carries,
|
|
88
|
+
* `gfx/c4/src/type-line.ts`). `MorphSpec.afterMorph`
|
|
89
|
+
* (`packages/affine/blocks/surface`) now hands its callee the morph
|
|
90
|
+
* toolbar's own `std`, so `rewriteLabel` passes it straight through: on a
|
|
91
|
+
* host running a French catalogue, morphing an untouched TRANSLATED node
|
|
92
|
+
* rewrites its name to the target kind's own translated prompt, exactly as a
|
|
93
|
+
* standalone playground already rewrote the English one.
|
|
94
|
+
*/
|
|
95
|
+
export declare const wardleyNodeLabelKey: (kind: WardleyLabelledKind) => string;
|
|
75
96
|
/** A box centred on a point — how every Wardley creation site places a node. */
|
|
76
97
|
export declare function wardleyCenteredBox(cx: number, cy: number, w: number, h: number): string;
|
|
77
98
|
/** The canonical box of one kind, centred where the artefact already stands. */
|
|
@@ -108,6 +129,29 @@ export declare const WARDLEY_LABEL_H: number;
|
|
|
108
129
|
* the words end on a given edge is to start the box a full width before it.
|
|
109
130
|
*/
|
|
110
131
|
export declare const WARDLEY_LABEL_W = 120;
|
|
132
|
+
/**
|
|
133
|
+
* Where a kind carries its NAME, and how the words sit in the box.
|
|
134
|
+
*
|
|
135
|
+
* The one description of a placement the four creation sites and the morph all
|
|
136
|
+
* read. Restated at each site it had already started to drift — and a morph
|
|
137
|
+
* that rewrites the BOX of an artefact without knowing where that kind holds
|
|
138
|
+
* its name leaves the words under the new drawing, which is W3's overlap and a
|
|
139
|
+
* label the pointer can no longer reach.
|
|
140
|
+
*
|
|
141
|
+
* `place` is the coarse half of the answer, and the only one a morph acts on:
|
|
142
|
+
* the three circles and the two arrows all wear their name BESIDE them, so an
|
|
143
|
+
* artefact that grows from one to the next leaves the words exactly where they
|
|
144
|
+
* are; the pipeline is the one kind that holds it ABOVE, and crossing between
|
|
145
|
+
* the two is what has to move something.
|
|
146
|
+
*/
|
|
147
|
+
export type WardleyLabelPlacement = {
|
|
148
|
+
x: number;
|
|
149
|
+
y: number;
|
|
150
|
+
textAlign: 'left' | 'center' | 'right';
|
|
151
|
+
place: 'beside' | 'above';
|
|
152
|
+
};
|
|
153
|
+
/** Where a node of one kind, centred on (cx, cy), carries its label. */
|
|
154
|
+
export declare function wardleyLabelBoxFor(kind: WardleyLabelledKind, cx: number, cy: number): WardleyLabelPlacement;
|
|
111
155
|
/**
|
|
112
156
|
* The NAME beside an artefact, as props — a native free-text element.
|
|
113
157
|
*
|
|
@@ -207,6 +251,32 @@ export declare function wardleyMarketLinkProps(sourceId: string, targetId: strin
|
|
|
207
251
|
};
|
|
208
252
|
/** The pairs the triangle wires, given the three dots in creation order. */
|
|
209
253
|
export declare function wardleyMarketLinkPairs<T>(dots: readonly T[]): [T, T][];
|
|
254
|
+
/**
|
|
255
|
+
* How a Wardley connector LOOKS, per tool: the value-chain link is grey, solid
|
|
256
|
+
* and headless, the evolution arrow red, dashed and tipped with a filled
|
|
257
|
+
* triangle.
|
|
258
|
+
*
|
|
259
|
+
* Here beside the node presets, and for their reason: the style rides on the
|
|
260
|
+
* tool activation (`activateWardleyConnector`) and is pictured by the legend
|
|
261
|
+
* row that tool subscribes, so restating it in either place would be two
|
|
262
|
+
* descriptions of one notation that agreed the day they were written.
|
|
263
|
+
*/
|
|
264
|
+
export declare const WARDLEY_EDGE_STYLE: {
|
|
265
|
+
readonly link: {
|
|
266
|
+
readonly stroke: "#666666";
|
|
267
|
+
readonly strokeStyle: StrokeStyle.Solid;
|
|
268
|
+
readonly strokeWidth: 2;
|
|
269
|
+
readonly frontEndpointStyle: PointStyle.None;
|
|
270
|
+
readonly rearEndpointStyle: PointStyle.None;
|
|
271
|
+
};
|
|
272
|
+
readonly arrow: {
|
|
273
|
+
readonly stroke: "#d6455d";
|
|
274
|
+
readonly strokeStyle: StrokeStyle.Dash;
|
|
275
|
+
readonly strokeWidth: 2;
|
|
276
|
+
readonly frontEndpointStyle: PointStyle.None;
|
|
277
|
+
readonly rearEndpointStyle: PointStyle.Triangle;
|
|
278
|
+
};
|
|
279
|
+
};
|
|
210
280
|
/** One of the four arrows: its own axis-aligned box, and the outline in it. */
|
|
211
281
|
export interface WardleyPorterArrow {
|
|
212
282
|
/** Serialized `xywh` of the box the polygon is drawn inside. */
|
package/dist/presets.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { ConnectorMode, DEFAULT_POLYGON_VERTICES, FontFamily, FontWeight, PointStyle, ShapeStyle, StrokeStyle, TextFitMode, } from '@formicoidea/labre-core/model';
|
|
2
2
|
import { Bound } from '@formicoidea/labre-core/global/gfx';
|
|
3
|
-
import { ACCELERATOR_FILL, ACCELERATOR_LABEL, ACCELERATOR_SIZE, ACCELERATOR_STROKE_WIDTH, ACCELERATOR_VERTICES, AREA_FILL, AREA_POLYGON_SIZE, AREA_RECT_SIZE, AREA_STROKE, AREA_STROKE_WIDTH, DECELERATOR_LABEL, DECELERATOR_VERTICES, ECOSYSTEM_LABEL, ECOSYSTEM_SIZE, HANDLE_SIZE, INERTIA_COLOR, LABEL_DEFAULT, LABEL_FONT_SIZE, MARKET_DOT_RING, MARKET_DOT_SIZE, MARKET_DOT_STROKE_WIDTH, MARKET_LABEL, MARKET_LINK_COLOR, MARKET_LINK_WIDTH, MARKET_SIZE, METHOD_FILL, METHOD_LABEL, METHOD_SIZE, NODE_FILL, NODE_SIZE, NODE_STROKE, NODE_STROKE_WIDTH, PIPELINE_FILL, PIPELINE_HEIGHT, PIPELINE_LABEL, PIPELINE_WIDTH, PORTER_ARROW, PORTER_LETTER_FONT_SIZE, PORTER_SIZE, WARDLEY_RED, } from './node/consts.js';
|
|
3
|
+
import { ACCELERATOR_FILL, ACCELERATOR_LABEL, ACCELERATOR_SIZE, ACCELERATOR_STROKE_WIDTH, ACCELERATOR_VERTICES, AREA_FILL, AREA_POLYGON_SIZE, AREA_RECT_SIZE, AREA_STROKE, AREA_STROKE_WIDTH, DECELERATOR_LABEL, DECELERATOR_VERTICES, ECOSYSTEM_LABEL, ECOSYSTEM_SIZE, HANDLE_SIZE, INERTIA_COLOR, LABEL_DEFAULT, LABEL_FONT_SIZE, LABEL_GAP, LINK_GREY, LINK_STROKE_WIDTH, MARKET_DOT_RING, MARKET_DOT_SIZE, MARKET_DOT_STROKE_WIDTH, MARKET_LABEL, MARKET_LINK_COLOR, MARKET_LINK_WIDTH, MARKET_SIZE, METHOD_FILL, METHOD_LABEL, METHOD_SIZE, NODE_FILL, NODE_SIZE, NODE_STROKE, NODE_STROKE_WIDTH, PIPELINE_FILL, PIPELINE_HEIGHT, PIPELINE_LABEL, PIPELINE_WIDTH, PORTER_ARROW, PORTER_LETTER_FONT_SIZE, PORTER_SIZE, WARDLEY_RED, } from './node/consts.js';
|
|
4
4
|
import { WARDLEY_ROLE } from './roles.js';
|
|
5
5
|
/**
|
|
6
6
|
* The size a kind is BORN at, which for Wardley is the notation rather than a
|
|
@@ -65,6 +65,27 @@ export const WARDLEY_NODE_LABEL = {
|
|
|
65
65
|
accelerator: ACCELERATOR_LABEL,
|
|
66
66
|
decelerator: DECELERATOR_LABEL,
|
|
67
67
|
};
|
|
68
|
+
/**
|
|
69
|
+
* The i18n key {@link WARDLEY_NODE_LABEL} is the English default of —
|
|
70
|
+
* resolved at placement (`createWardleyNode` / `createWardleyPipeline` /
|
|
71
|
+
* `createWardleyMarket`), never afterwards. "Pipeline" is the PO's glossary
|
|
72
|
+
* term for Wardley: the French proposal IS the English word.
|
|
73
|
+
*
|
|
74
|
+
* ## The morph's placeholder detection, translated
|
|
75
|
+
*
|
|
76
|
+
* `wardleyMorphedLabel` (`morph.ts`) decides whether to refresh an artefact's
|
|
77
|
+
* name across a morph, and accepts an OPTIONAL `std`: given one, it
|
|
78
|
+
* recognises the stored text as "untouched" whether it reads the English
|
|
79
|
+
* prompt or the host's own resolved wording, and resolves the TARGET kind's
|
|
80
|
+
* prompt through the host too (the same fix `c4MorphedTypeLine` carries,
|
|
81
|
+
* `gfx/c4/src/type-line.ts`). `MorphSpec.afterMorph`
|
|
82
|
+
* (`packages/affine/blocks/surface`) now hands its callee the morph
|
|
83
|
+
* toolbar's own `std`, so `rewriteLabel` passes it straight through: on a
|
|
84
|
+
* host running a French catalogue, morphing an untouched TRANSLATED node
|
|
85
|
+
* rewrites its name to the target kind's own translated prompt, exactly as a
|
|
86
|
+
* standalone playground already rewrote the English one.
|
|
87
|
+
*/
|
|
88
|
+
export const wardleyNodeLabelKey = (kind) => `com.labre.wardley.seed.${kind}`;
|
|
68
89
|
/**
|
|
69
90
|
* The fill a kind is born with. White for every circle that means "a thing on
|
|
70
91
|
* the value chain"; the method's grey is the one that carries meaning — it
|
|
@@ -177,6 +198,40 @@ export const WARDLEY_LABEL_H = LABEL_FONT_SIZE + 8;
|
|
|
177
198
|
* the words end on a given edge is to start the box a full width before it.
|
|
178
199
|
*/
|
|
179
200
|
export const WARDLEY_LABEL_W = 120;
|
|
201
|
+
/** Where a node of one kind, centred on (cx, cy), carries its label. */
|
|
202
|
+
export function wardleyLabelBoxFor(kind, cx, cy) {
|
|
203
|
+
const { w } = WARDLEY_NODE_SIZE[kind];
|
|
204
|
+
// A pipeline is a 120-wide bar, and a name written to the right of it would
|
|
205
|
+
// stand a bar's length away from the thing it names. So the words go above
|
|
206
|
+
// the handle, centred on the body.
|
|
207
|
+
if (kind === 'pipeline') {
|
|
208
|
+
const top = cy - WARDLEY_NODE_SIZE.pipeline.h / 2;
|
|
209
|
+
return {
|
|
210
|
+
x: cx - WARDLEY_LABEL_W / 2,
|
|
211
|
+
y: top - HANDLE_SIZE / 2 - WARDLEY_LABEL_H - LABEL_GAP,
|
|
212
|
+
textAlign: 'center',
|
|
213
|
+
place: 'above',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
// A decelerator points LEFT, and the reference puts the words on the side the
|
|
217
|
+
// arrow's shaft is on so the reading runs into the arrow rather than across
|
|
218
|
+
// it. A label box is a fixed width whatever it reads, so a right-aligned one
|
|
219
|
+
// has to start a box-width before the edge its words must end on.
|
|
220
|
+
if (kind === 'decelerator') {
|
|
221
|
+
return {
|
|
222
|
+
x: cx - w / 2 - LABEL_GAP - WARDLEY_LABEL_W,
|
|
223
|
+
y: cy - WARDLEY_LABEL_H / 2,
|
|
224
|
+
textAlign: 'right',
|
|
225
|
+
place: 'beside',
|
|
226
|
+
};
|
|
227
|
+
}
|
|
228
|
+
return {
|
|
229
|
+
x: cx + w / 2 + LABEL_GAP,
|
|
230
|
+
y: cy - WARDLEY_LABEL_H / 2,
|
|
231
|
+
textAlign: 'left',
|
|
232
|
+
place: 'beside',
|
|
233
|
+
};
|
|
234
|
+
}
|
|
180
235
|
/**
|
|
181
236
|
* The NAME beside an artefact, as props — a native free-text element.
|
|
182
237
|
*
|
|
@@ -381,6 +436,33 @@ export function wardleyMarketLinkPairs(dots) {
|
|
|
381
436
|
[dots[2], dots[0]],
|
|
382
437
|
];
|
|
383
438
|
}
|
|
439
|
+
/* ── The two typed edges ──────────────────────────────────────────────── */
|
|
440
|
+
/**
|
|
441
|
+
* How a Wardley connector LOOKS, per tool: the value-chain link is grey, solid
|
|
442
|
+
* and headless, the evolution arrow red, dashed and tipped with a filled
|
|
443
|
+
* triangle.
|
|
444
|
+
*
|
|
445
|
+
* Here beside the node presets, and for their reason: the style rides on the
|
|
446
|
+
* tool activation (`activateWardleyConnector`) and is pictured by the legend
|
|
447
|
+
* row that tool subscribes, so restating it in either place would be two
|
|
448
|
+
* descriptions of one notation that agreed the day they were written.
|
|
449
|
+
*/
|
|
450
|
+
export const WARDLEY_EDGE_STYLE = {
|
|
451
|
+
link: {
|
|
452
|
+
stroke: LINK_GREY,
|
|
453
|
+
strokeStyle: StrokeStyle.Solid,
|
|
454
|
+
strokeWidth: LINK_STROKE_WIDTH,
|
|
455
|
+
frontEndpointStyle: PointStyle.None,
|
|
456
|
+
rearEndpointStyle: PointStyle.None,
|
|
457
|
+
},
|
|
458
|
+
arrow: {
|
|
459
|
+
stroke: WARDLEY_RED,
|
|
460
|
+
strokeStyle: StrokeStyle.Dash,
|
|
461
|
+
strokeWidth: LINK_STROKE_WIDTH,
|
|
462
|
+
frontEndpointStyle: PointStyle.None,
|
|
463
|
+
rearEndpointStyle: PointStyle.Triangle,
|
|
464
|
+
},
|
|
465
|
+
};
|
|
384
466
|
/**
|
|
385
467
|
* The four arrow outlines, north first and then clockwise.
|
|
386
468
|
*
|
|
@@ -1,2 +1,12 @@
|
|
|
1
1
|
import { type TemplateCategory } from '@formicoidea/labre-core/gfx/template';
|
|
2
|
+
import type { ChromeWording } from '@formicoidea/labre-core/shared/services';
|
|
3
|
+
/**
|
|
4
|
+
* The three hand-drawn palette swatches' own tile names (`Template.nameKey`):
|
|
5
|
+
* none derives from a command, since `linkTool` / `evolutionArrow` /
|
|
6
|
+
* `addAreaPolygon` activate a tool rather than draw anything (see `tpl` below
|
|
7
|
+
* and its docstring).
|
|
8
|
+
*/
|
|
9
|
+
export declare const WARDLEY_TEMPLATE_NAME_LINK: ChromeWording;
|
|
10
|
+
export declare const WARDLEY_TEMPLATE_NAME_EVOLUTION_ARROW: ChromeWording;
|
|
11
|
+
export declare const WARDLEY_TEMPLATE_NAME_AREA_POLYGON: ChromeWording;
|
|
2
12
|
export declare const wardleyTemplateCategory: TemplateCategory;
|