@formicoidea/labre-framework-c4 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 +2 -1
- package/dist/actions.js +35 -12
- package/dist/commands.d.ts +3 -0
- package/dist/commands.js +143 -1
- package/dist/consts.d.ts +24 -0
- package/dist/consts.js +24 -0
- package/dist/index.d.ts +0 -1
- package/dist/index.js +4 -4
- package/dist/morph.js +2 -2
- package/dist/node/type-line-watcher.js +6 -1
- package/dist/templates.js +2 -0
- package/dist/toolbar/c4-senior-button.d.ts +1 -0
- package/dist/toolbar/c4-senior-button.js +1 -0
- package/dist/toolbar/config.d.ts +18 -29
- package/dist/toolbar/config.js +22 -85
- package/dist/toolbar/icons.d.ts +6 -3
- package/dist/toolbar/icons.js +6 -3
- package/dist/toolbar/palette.d.ts +33 -0
- package/dist/toolbar/palette.js +114 -0
- package/dist/translations.js +51 -2
- package/dist/type-line.d.ts +45 -8
- package/dist/type-line.js +133 -16
- package/dist/view.js +7 -0
- package/package.json +3 -3
- package/dist/legend.d.ts +0 -37
- package/dist/legend.js +0 -123
package/dist/commands.js
CHANGED
|
@@ -1,5 +1,131 @@
|
|
|
1
1
|
import { activateC4Relationship, c4BoardsForExport, createC4Board, createC4Boundary, createC4Node, exportC4MermaidFile, } from './actions.js';
|
|
2
|
-
import {
|
|
2
|
+
import { BOUNDARY_STROKE, NODE_PALETTE, RELATIONSHIP_STROKE } from './consts.js';
|
|
3
|
+
import { C4_ROLE } from './roles.js';
|
|
4
|
+
import { c4ExportMermaidIcon, c4ToolbarIcon, C4_TOOLBOX_ICONS, } from './toolbar/icons.js';
|
|
5
|
+
/**
|
|
6
|
+
* The C4 toolbox as commands — the single source every surface reads: the
|
|
7
|
+
* senior sub-menu, the artefact catalogue, the palette, Settings › Shortcuts
|
|
8
|
+
* and the agent (`docs/adr/0008`).
|
|
9
|
+
*
|
|
10
|
+
* ## Thirteen drawn, fourteen declared — exactly the cap
|
|
11
|
+
*
|
|
12
|
+
* The pack draws nine elements, two boundaries, one board and one connecting
|
|
13
|
+
* object: thirteen entries, all thirteen on a sub-menu that holds fourteen. The
|
|
14
|
+
* mermaid EXPORT is the fourteenth catalogue entry and declines the sub-menu, so
|
|
15
|
+
* the catalogue lands on fourteen against a cap of fourteen — which
|
|
16
|
+
* `selectSeniorMenuCommands` measures on the CATALOGUE, not on the menu. C4 is
|
|
17
|
+
* therefore still the framework that FITS, to the entry: nothing is arbitrated,
|
|
18
|
+
* the ranking never runs, and the sub-menu is exactly this list in exactly this
|
|
19
|
+
* order.
|
|
20
|
+
*
|
|
21
|
+
* The board's automatic LEGEND is deliberately not among them, and is not a
|
|
22
|
+
* command at all: it is reached from the selected board's contextual toolbar and
|
|
23
|
+
* from nowhere else (PO arbitration, 27/08/2026 — the same call the Context Map
|
|
24
|
+
* board makes). See `toolbar/config.ts` for what that costs and why it is the
|
|
25
|
+
* arbitrated exception to the command bottleneck.
|
|
26
|
+
*
|
|
27
|
+
* What the legend IS made of, though, is declared right here: each entry below
|
|
28
|
+
* subscribes the row its artefact puts in the box (`CommandDescriptor.legend`,
|
|
29
|
+
* `docs/adr/0026`), so the row and the gesture cannot drift apart the way a
|
|
30
|
+
* second hand-written table did.
|
|
31
|
+
*
|
|
32
|
+
* That does not make the ORDER free — it makes it a fifteenth entry away from
|
|
33
|
+
* mattering. Add one more of anything, an artefact or a second export, and the
|
|
34
|
+
* catalogue outgrows the cap, the ranking kicks in, and the first
|
|
35
|
+
* `SENIOR_MENU_RANKED_SLOTS` of this list become the COLD START every new user
|
|
36
|
+
* meets. BPMN learned that the hard way in a live recette (#144): its
|
|
37
|
+
* declarations were grouped by family, so the head a first-time user met was six
|
|
38
|
+
* events and a task, with nothing to connect them. The fix was the order, and
|
|
39
|
+
* the lesson is cheaper to apply before the overflow than after it.
|
|
40
|
+
*
|
|
41
|
+
* The head is THIRTEEN slots since #167 (seven recent + six frequent), not the
|
|
42
|
+
* seven it was when BPMN met the problem — which makes the cold start nearly the
|
|
43
|
+
* whole toolbox and the author's order nearly the whole answer. Named through
|
|
44
|
+
* the constant rather than restated as a number, so the next widening does not
|
|
45
|
+
* leave a lie here.
|
|
46
|
+
*
|
|
47
|
+
* ## The house order for a framework that FITS (PO, 28/08/2026)
|
|
48
|
+
*
|
|
49
|
+
* A general convention, decided on this pack and applying to every framework of
|
|
50
|
+
* fourteen commands or fewer — i.e. every one whose sub-menu is never
|
|
51
|
+
* arbitrated, so the author's order is the only order anybody ever sees:
|
|
52
|
+
*
|
|
53
|
+
* 1. the BOARDS first — the sheet has to exist before anything can be put on
|
|
54
|
+
* it, and a first-time user who reaches for a person before a board draws
|
|
55
|
+
* a person on the void;
|
|
56
|
+
* 2. then the BASE components, the ones a diagram of this kind is mostly made
|
|
57
|
+
* of;
|
|
58
|
+
* 3. then the NICHE ones last;
|
|
59
|
+
* 4. and components of the SAME TYPE stay adjacent — the external variant sits
|
|
60
|
+
* against the plain one it varies, not in a ghetto of externals at the end.
|
|
61
|
+
*
|
|
62
|
+
* This SUPERSEDES the previous C4 order, which led with the four levels and put
|
|
63
|
+
* the board sixth: it was built to make the would-be cold start drawable, and
|
|
64
|
+
* the PO's answer is that a cold start which opens with the sheet is drawable
|
|
65
|
+
* sooner. Applied here, it reads: the board, then person / person-ext, system /
|
|
66
|
+
* system-ext (each variant next to its plain form), container, component,
|
|
67
|
+
* database, then the two container flavours that are a container with a picture,
|
|
68
|
+
* then the relationship, then the two boundaries, then the export.
|
|
69
|
+
*/
|
|
70
|
+
/* ── The legend, SUBSCRIBED rather than tabulated (`docs/adr/0026`) ────── */
|
|
71
|
+
/**
|
|
72
|
+
* The legend's own three section titles — this framework's word about this
|
|
73
|
+
* framework's vocabulary, which is why they are keyed per framework and never
|
|
74
|
+
* shared with UML's identical three (`translation-service/README.md`).
|
|
75
|
+
*
|
|
76
|
+
* They are DECLARED rather than derived from the commands' `category` because
|
|
77
|
+
* the catalogue files a boundary under `boundaries` and the legend calls the
|
|
78
|
+
* same thing a "Frame": one is where a gesture sits in a toolbox, the other is
|
|
79
|
+
* what the drawing is. The keys are the ones already shipped to the host
|
|
80
|
+
* catalogue, unchanged.
|
|
81
|
+
*/
|
|
82
|
+
const SECTION_ELEMENTS = [
|
|
83
|
+
'com.labre.c4.legend.section.elements',
|
|
84
|
+
'Elements',
|
|
85
|
+
];
|
|
86
|
+
const SECTION_FRAMES = [
|
|
87
|
+
'com.labre.c4.legend.section.frames',
|
|
88
|
+
'Frames',
|
|
89
|
+
];
|
|
90
|
+
const SECTION_RELATIONS = [
|
|
91
|
+
'com.labre.c4.legend.section.relations',
|
|
92
|
+
'Relations',
|
|
93
|
+
];
|
|
94
|
+
/** Every section wording above, for `translations.ts`'s manifest. */
|
|
95
|
+
export const C4_LEGEND_SECTION_WORDINGS = [
|
|
96
|
+
SECTION_ELEMENTS,
|
|
97
|
+
SECTION_FRAMES,
|
|
98
|
+
SECTION_RELATIONS,
|
|
99
|
+
];
|
|
100
|
+
/**
|
|
101
|
+
* One element row, in the very fill {@link NODE_PALETTE} paints that kind with
|
|
102
|
+
* — never a second literal, so restyling the pack restyles its swatches. The
|
|
103
|
+
* WORDING is nowhere here either: the engine reads the role's own label, so
|
|
104
|
+
* renaming a role renames its row.
|
|
105
|
+
*
|
|
106
|
+
* `exact` is asked for on the container and nowhere else. `c4:database`
|
|
107
|
+
* specialises `c4:container`, so an inclusive row on the parent would put a
|
|
108
|
+
* blue square on a board carrying nothing but cylinders — a row naming a shape
|
|
109
|
+
* that is nowhere on the diagram. The other four element roles are childless,
|
|
110
|
+
* so the flag would change nothing for them and marks only the place where the
|
|
111
|
+
* distinction is real.
|
|
112
|
+
*/
|
|
113
|
+
function elementEntry(role, fill, exact = false) {
|
|
114
|
+
return {
|
|
115
|
+
role,
|
|
116
|
+
row: { swatch: 'square', color: fill },
|
|
117
|
+
...(exact ? { exact } : {}),
|
|
118
|
+
section: SECTION_ELEMENTS,
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* A line row: a boundary and a relationship are both drawn, never filled, and
|
|
123
|
+
* both are dashed on the canvas — a filled chip would picture the one thing
|
|
124
|
+
* this board deliberately does not paint.
|
|
125
|
+
*/
|
|
126
|
+
function lineEntry(role, color, section) {
|
|
127
|
+
return { role, row: { swatch: 'line', color, dashed: true }, section };
|
|
128
|
+
}
|
|
3
129
|
const SPECS = [
|
|
4
130
|
/* ── Boards first: the sheet everything else is drawn on ────────────── */
|
|
5
131
|
{
|
|
@@ -10,6 +136,8 @@ const SPECS = [
|
|
|
10
136
|
category: 'diagrams',
|
|
11
137
|
element: 'board',
|
|
12
138
|
board: true,
|
|
139
|
+
// Wider than the shared 260: the longest C4 row reads "Software system".
|
|
140
|
+
legendBox: { width: 290 },
|
|
13
141
|
run: createC4Board,
|
|
14
142
|
},
|
|
15
143
|
/* ── The base components: the four levels, each plain form immediately
|
|
@@ -21,6 +149,7 @@ const SPECS = [
|
|
|
21
149
|
kind: 'artefact',
|
|
22
150
|
category: 'elements',
|
|
23
151
|
element: 'node:person',
|
|
152
|
+
legend: elementEntry(C4_ROLE.person, NODE_PALETTE.person.fill),
|
|
24
153
|
run: std => createC4Node(std, 'person'),
|
|
25
154
|
},
|
|
26
155
|
{
|
|
@@ -39,6 +168,7 @@ const SPECS = [
|
|
|
39
168
|
kind: 'artefact',
|
|
40
169
|
category: 'elements',
|
|
41
170
|
element: 'node:system',
|
|
171
|
+
legend: elementEntry(C4_ROLE.system, NODE_PALETTE.system.fill),
|
|
42
172
|
run: std => createC4Node(std, 'system'),
|
|
43
173
|
},
|
|
44
174
|
{
|
|
@@ -57,6 +187,7 @@ const SPECS = [
|
|
|
57
187
|
kind: 'artefact',
|
|
58
188
|
category: 'elements',
|
|
59
189
|
element: 'node:container',
|
|
190
|
+
legend: elementEntry(C4_ROLE.container, NODE_PALETTE.container.fill, true),
|
|
60
191
|
run: std => createC4Node(std, 'container'),
|
|
61
192
|
},
|
|
62
193
|
{
|
|
@@ -66,6 +197,7 @@ const SPECS = [
|
|
|
66
197
|
kind: 'artefact',
|
|
67
198
|
category: 'elements',
|
|
68
199
|
element: 'node:component',
|
|
200
|
+
legend: elementEntry(C4_ROLE.component, NODE_PALETTE.component.fill),
|
|
69
201
|
run: std => createC4Node(std, 'component'),
|
|
70
202
|
},
|
|
71
203
|
/* ── The niche components: a container with a picture on it ─────────── */
|
|
@@ -78,6 +210,7 @@ const SPECS = [
|
|
|
78
210
|
kind: 'artefact',
|
|
79
211
|
category: 'elements',
|
|
80
212
|
element: 'node:database',
|
|
213
|
+
legend: elementEntry(C4_ROLE.database, NODE_PALETTE.database.fill),
|
|
81
214
|
run: std => createC4Node(std, 'database'),
|
|
82
215
|
},
|
|
83
216
|
{
|
|
@@ -108,6 +241,7 @@ const SPECS = [
|
|
|
108
241
|
kind: 'tool',
|
|
109
242
|
category: 'relations',
|
|
110
243
|
element: 'connector:relationship',
|
|
244
|
+
legend: lineEntry(C4_ROLE.relationship, RELATIONSHIP_STROKE, SECTION_RELATIONS),
|
|
111
245
|
run: activateC4Relationship,
|
|
112
246
|
},
|
|
113
247
|
{
|
|
@@ -117,6 +251,9 @@ const SPECS = [
|
|
|
117
251
|
kind: 'artefact',
|
|
118
252
|
category: 'boundaries',
|
|
119
253
|
element: 'boundary:system',
|
|
254
|
+
// ONE row for both boundaries, written on the PARENT role: a container
|
|
255
|
+
// boundary IS a boundary, and two rows would name one frame twice.
|
|
256
|
+
legend: lineEntry(C4_ROLE.boundary, BOUNDARY_STROKE, SECTION_FRAMES),
|
|
120
257
|
run: std => createC4Boundary(std, 'system'),
|
|
121
258
|
},
|
|
122
259
|
{
|
|
@@ -148,6 +285,8 @@ const toolboxCommands = SPECS.map((spec, order) => ({
|
|
|
148
285
|
availability: 'always',
|
|
149
286
|
run: spec.run,
|
|
150
287
|
telemetry: { framework: 'c4', element: spec.element, board: spec.board },
|
|
288
|
+
...(spec.legend ? { legend: spec.legend } : {}),
|
|
289
|
+
...(spec.legendBox ? { legendBox: spec.legendBox } : {}),
|
|
151
290
|
}));
|
|
152
291
|
/**
|
|
153
292
|
* The EXPORT — the first C4 command whose subject is a whole BOARD.
|
|
@@ -219,4 +358,7 @@ export const c4Commands = [
|
|
|
219
358
|
export const c4CommandIcons = {
|
|
220
359
|
...C4_TOOLBOX_ICONS,
|
|
221
360
|
'c4.export-mermaid': c4ExportMermaidIcon,
|
|
361
|
+
// The senior button's 56×56 glyph, so `FrameworkDescriptor.iconKey` resolves
|
|
362
|
+
// through `getCommandIcon`.
|
|
363
|
+
'c4.toolbar': c4ToolbarIcon,
|
|
222
364
|
};
|
package/dist/consts.d.ts
CHANGED
|
@@ -246,6 +246,22 @@ export declare const NODE_SIZE: Record<C4NodeKind, {
|
|
|
246
246
|
* nothing at all: the words are the artefact.
|
|
247
247
|
*/
|
|
248
248
|
export declare const NODE_LABEL: Record<C4NodeKind, string>;
|
|
249
|
+
/**
|
|
250
|
+
* The i18n key {@link NODE_LABEL} is the English default of — mirrors BPMN's
|
|
251
|
+
* {@link nodeLabelKey} exactly, one package over: resolved at placement
|
|
252
|
+
* (`createC4Node`, the NAME tier) and never afterwards, since the title is
|
|
253
|
+
* content from the moment it lands and an author is free to retype it.
|
|
254
|
+
*
|
|
255
|
+
* Safe to translate where the type line is not: the exporter reads a
|
|
256
|
+
* component's NAME verbatim (`c4StatedName`) with no placeholder suppression,
|
|
257
|
+
* so nothing downstream compares this text against the English literal.
|
|
258
|
+
*/
|
|
259
|
+
export declare const nodeLabelKey: (kind: C4NodeKind) => string;
|
|
260
|
+
/**
|
|
261
|
+
* The prompt a fresh description tier carries, resolved at placement exactly
|
|
262
|
+
* like {@link DESCRIPTION_PLACEHOLDER} below is the English default of.
|
|
263
|
+
*/
|
|
264
|
+
export declare const DESCRIPTION_PLACEHOLDER_KEY = "com.labre.c4.seed.description-placeholder";
|
|
249
265
|
export declare const FONT_FAMILY = "Inter, sans-serif";
|
|
250
266
|
/**
|
|
251
267
|
* The card — the same white every framework background paints, framed and
|
|
@@ -336,6 +352,14 @@ export declare const BOUNDARY_MARGIN = 12;
|
|
|
336
352
|
* on the canvas keeps its words whatever its variant.
|
|
337
353
|
*/
|
|
338
354
|
export declare const BOUNDARY_LABEL: Record<C4BoundaryVariant, string>;
|
|
355
|
+
/**
|
|
356
|
+
* The i18n key {@link BOUNDARY_LABEL} is the English default of — resolved at
|
|
357
|
+
* placement (`createC4Boundary`) exactly like {@link nodeLabelKey}: the name
|
|
358
|
+
* is exported verbatim (`export.ts`, `boundary.name`) with no comparison
|
|
359
|
+
* against the English literal, so translating it is as safe as a component's
|
|
360
|
+
* title.
|
|
361
|
+
*/
|
|
362
|
+
export declare const boundaryLabelKey: (variant: C4BoundaryVariant) => string;
|
|
339
363
|
/**
|
|
340
364
|
* Relationship connector preset — the dashed arrow C4 draws between elements.
|
|
341
365
|
*
|
package/dist/consts.js
CHANGED
|
@@ -291,6 +291,22 @@ export const NODE_LABEL = {
|
|
|
291
291
|
browser: 'Web app',
|
|
292
292
|
component: 'Component',
|
|
293
293
|
};
|
|
294
|
+
/**
|
|
295
|
+
* The i18n key {@link NODE_LABEL} is the English default of — mirrors BPMN's
|
|
296
|
+
* {@link nodeLabelKey} exactly, one package over: resolved at placement
|
|
297
|
+
* (`createC4Node`, the NAME tier) and never afterwards, since the title is
|
|
298
|
+
* content from the moment it lands and an author is free to retype it.
|
|
299
|
+
*
|
|
300
|
+
* Safe to translate where the type line is not: the exporter reads a
|
|
301
|
+
* component's NAME verbatim (`c4StatedName`) with no placeholder suppression,
|
|
302
|
+
* so nothing downstream compares this text against the English literal.
|
|
303
|
+
*/
|
|
304
|
+
export const nodeLabelKey = (kind) => `com.labre.c4.seed.${kind}`;
|
|
305
|
+
/**
|
|
306
|
+
* The prompt a fresh description tier carries, resolved at placement exactly
|
|
307
|
+
* like {@link DESCRIPTION_PLACEHOLDER} below is the English default of.
|
|
308
|
+
*/
|
|
309
|
+
export const DESCRIPTION_PLACEHOLDER_KEY = 'com.labre.c4.seed.description-placeholder';
|
|
294
310
|
/* ── The board ─────────────────────────────────────────────────────────── */
|
|
295
311
|
export const FONT_FAMILY = 'Inter, sans-serif';
|
|
296
312
|
/**
|
|
@@ -389,6 +405,14 @@ export const BOUNDARY_LABEL = {
|
|
|
389
405
|
system: 'System boundary',
|
|
390
406
|
container: 'Container boundary',
|
|
391
407
|
};
|
|
408
|
+
/**
|
|
409
|
+
* The i18n key {@link BOUNDARY_LABEL} is the English default of — resolved at
|
|
410
|
+
* placement (`createC4Boundary`) exactly like {@link nodeLabelKey}: the name
|
|
411
|
+
* is exported verbatim (`export.ts`, `boundary.name`) with no comparison
|
|
412
|
+
* against the English literal, so translating it is as safe as a component's
|
|
413
|
+
* title.
|
|
414
|
+
*/
|
|
415
|
+
export const boundaryLabelKey = (variant) => `com.labre.c4.seed.boundary-${variant}`;
|
|
392
416
|
/* ── The relationship ──────────────────────────────────────────────────── */
|
|
393
417
|
/**
|
|
394
418
|
* Relationship connector preset — the dashed arrow C4 draws between elements.
|
package/dist/index.d.ts
CHANGED
|
@@ -13,5 +13,4 @@ export { C4_INTERCHANGE, C4_MERMAID_EXPORT, C4_MERMAID_EXTENSION, C4_MERMAID_FOR
|
|
|
13
13
|
export { C4_ELEMENT_MATRIX, C4_RELATIONSHIP_MATRIX, C4_RULES, } from './rules.js';
|
|
14
14
|
export { c4CommandIcons, c4Commands } from './commands.js';
|
|
15
15
|
export { c4TranslationEntries } from './translations.js';
|
|
16
|
-
export { C4_AUTO_LEGEND } from './legend.js';
|
|
17
16
|
export { C4_COMPONENT_READING, C4_CONTAINER_READING, C4_PERSON_READING, C4_READINGS, C4_SYSTEM_READING, } from './reading.js';
|
package/dist/index.js
CHANGED
|
@@ -48,10 +48,10 @@ export { C4_ELEMENT_MATRIX, C4_RELATIONSHIP_MATRIX, C4_RULES, } from './rules.js
|
|
|
48
48
|
// The toolbox, for the host that composes the command registry and the
|
|
49
49
|
// translation-key manifest out of the frameworks it installed (see
|
|
50
50
|
// `packages/affine/all/src/{commands,translations}.ts`).
|
|
51
|
+
// What the board's automatic legend can say is no longer a table of its own:
|
|
52
|
+
// every row is subscribed by the command that draws the artefact
|
|
53
|
+
// (`CommandDescriptor.legend`, `docs/adr/0026`), so `c4Commands` already
|
|
54
|
+
// answers "what does this framework document about itself" without a canvas.
|
|
51
55
|
export { c4CommandIcons, c4Commands } from './commands.js';
|
|
52
56
|
export { c4TranslationEntries } from './translations.js';
|
|
53
|
-
// The legend TABLE — what the board's automatic legend can say. Exported for
|
|
54
|
-
// the same reason the two background declarations are: it answers "what does
|
|
55
|
-
// this framework document about itself" without a canvas.
|
|
56
|
-
export { C4_AUTO_LEGEND } from './legend.js';
|
|
57
57
|
export { C4_COMPONENT_READING, C4_CONTAINER_READING, C4_PERSON_READING, C4_READINGS, C4_SYSTEM_READING, } from './reading.js';
|
package/dist/morph.js
CHANGED
|
@@ -167,8 +167,8 @@ function rewriteTier(model, role, decide) {
|
|
|
167
167
|
* "Customer database" keeps that name whatever it becomes, which is the PO's
|
|
168
168
|
* "the label is intact" for every title that is real content.
|
|
169
169
|
*/
|
|
170
|
-
function rewriteTiers(model, from, to) {
|
|
171
|
-
rewriteTier(model, C4_ROLE['type-line'], text => c4MorphedTypeLine(from, to, text));
|
|
170
|
+
function rewriteTiers(model, from, to, std) {
|
|
171
|
+
rewriteTier(model, C4_ROLE['type-line'], text => c4MorphedTypeLine(from, to, text, std));
|
|
172
172
|
rewriteTier(model, C4_ROLE.title, text => c4MorphedTitle(from, to, text));
|
|
173
173
|
}
|
|
174
174
|
/**
|
|
@@ -103,7 +103,12 @@ export class C4TypeLineWatcher extends InteractivityExtension {
|
|
|
103
103
|
if (!kind)
|
|
104
104
|
return;
|
|
105
105
|
const raw = element.text.toString();
|
|
106
|
-
|
|
106
|
+
// `this.std` resolves the canonical line through the host's catalogue —
|
|
107
|
+
// both halves of the prompt, and the technology round-trip
|
|
108
|
+
// (`technologyOfTypeLine`) recognising the host's own resolved word —
|
|
109
|
+
// so a translated document's type line stays in that language across a
|
|
110
|
+
// commit instead of being silently reverted to English (`type-line.ts`).
|
|
111
|
+
const canonical = normalizeC4TypeLine(kind, raw, this.std);
|
|
107
112
|
if (canonical === raw)
|
|
108
113
|
return;
|
|
109
114
|
this.std.store.captureSync();
|
package/dist/templates.js
CHANGED
|
@@ -46,6 +46,8 @@ const boundaryPreview = (inner = '') => `<svg ${ATTRS} fill="none"><rect x="14"
|
|
|
46
46
|
const CONTAINER = NODE_PALETTE.container;
|
|
47
47
|
export const c4TemplateCategory = {
|
|
48
48
|
name: 'C4',
|
|
49
|
+
// Reuses the senior button's own key — see `TemplateCategory.nameKey`.
|
|
50
|
+
nameKey: 'com.labre.framework.c4',
|
|
49
51
|
templates: [
|
|
50
52
|
templateFromCommand(byId('c4.addBoard'), `<svg ${ATTRS} fill="none"><rect x="10" y="10" width="115" height="60" rx="6" fill="${BOARD_CARD_FILL}" stroke="${BOARD_CARD_BORDER}" stroke-width="1.5"/><path d="M10 26 H125" stroke="${BOARD_CARD_BORDER}" stroke-width="1.5"/><rect x="11" y="11" width="113" height="15" fill="${BOARD_BAND_FILL}"/><rect x="20" y="15" width="40" height="6" rx="3" fill="${BOARD_TITLE_COLOR}" opacity="0.55"/><rect x="22" y="36" width="34" height="20" rx="2" fill="${NODE_PALETTE.system.fill}"/><rect x="79" y="36" width="34" height="20" rx="2" fill="${CONTAINER.fill}"/><path d="M58 46 H75" stroke="${RELATIONSHIP_STROKE}" stroke-width="1.6" stroke-dasharray="4 3"/></svg>`),
|
|
51
53
|
templateFromCommand(byId('c4.addPerson'), personPreview('person')),
|
package/dist/toolbar/config.d.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { type TemplateResult } from 'lit';
|
|
1
|
+
import { type ToolbarContext, type ToolbarModuleConfig } from '@formicoidea/labre-core/shared/services';
|
|
3
2
|
/**
|
|
4
3
|
* The selected C4 board's contextual toolbar: the resize toggle, and the
|
|
5
4
|
* automatic legend of what is actually drawn on the board.
|
|
@@ -13,15 +12,14 @@ import { type TemplateResult } from 'lit';
|
|
|
13
12
|
* The PO's call is that generating one belongs to a board you have SELECTED and
|
|
14
13
|
* to nothing else: it is not an artefact to pick off a palette, and an entry in
|
|
15
14
|
* a catalogue of things C4 draws would offer it to a user with no board in front
|
|
16
|
-
* of them. So there is no `c4.legend` command, this button is the only way
|
|
17
|
-
* reach it
|
|
15
|
+
* of them. So there is no `c4.legend` command, and this button is the only way
|
|
16
|
+
* to reach it.
|
|
18
17
|
*
|
|
19
|
-
* That is the same shape — and the same exception —
|
|
20
|
-
*
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* matching what `reportCommandTelemetry` would have sent.
|
|
18
|
+
* That is the same shape — and the same exception — every other framework with a
|
|
19
|
+
* legend makes. The cost is the one the bottleneck exists to avoid: the
|
|
20
|
+
* telemetry it owes is emitted outside `runCommand`. It is emitted ONCE, by the
|
|
21
|
+
* shared factory (`legend-toolbar.ts`), so the seven copies of those wire values
|
|
22
|
+
* that used to sit in seven files can no longer drift apart.
|
|
25
23
|
*
|
|
26
24
|
* ## Two modules on one element, and why
|
|
27
25
|
*
|
|
@@ -66,35 +64,26 @@ export declare const c4BoardToolbarConfig: {
|
|
|
66
64
|
readonly actions: [{
|
|
67
65
|
readonly id: "a.toggle-resize";
|
|
68
66
|
readonly tooltipWording: import("@formicoidea/labre-core/shared/services").ChromeWording;
|
|
69
|
-
readonly icon: TemplateResult<1>;
|
|
67
|
+
readonly icon: import("lit-html").TemplateResult<1>;
|
|
70
68
|
readonly active: (ctx: ToolbarContext) => boolean;
|
|
71
69
|
readonly run: (ctx: ToolbarContext) => void;
|
|
72
|
-
},
|
|
73
|
-
id: string;
|
|
74
|
-
placement: ActionPlacement;
|
|
75
|
-
when: (ctx: ToolbarContext) => boolean;
|
|
76
|
-
generate: (ctx: ToolbarContext) => {
|
|
77
|
-
icon: TemplateResult;
|
|
78
|
-
label: string;
|
|
79
|
-
run: (runCtx: ToolbarContext) => void;
|
|
80
|
-
};
|
|
81
|
-
}];
|
|
70
|
+
}, import("@formicoidea/labre-core/shared/services").ToolbarActionGenerator];
|
|
82
71
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
83
72
|
};
|
|
84
73
|
export declare const c4BoardToolbarExtension: import("@formicoidea/labre-core/store").ExtensionType;
|
|
85
74
|
/**
|
|
86
75
|
* The legend button — the flag-gated half of the row (see the note above).
|
|
87
76
|
*
|
|
88
|
-
*
|
|
89
|
-
*
|
|
77
|
+
* The SHARED factory, not a C4 copy of it: the button, the placement, the box
|
|
78
|
+
* and the one `FrameworkLegendCreated` emission all live in the surface block
|
|
79
|
+
* (`legend-toolbar.ts`, `docs/adr/0026`), and C4 hands it the board model to
|
|
80
|
+
* look for, whose commands to read the rows off, and the historical wire value.
|
|
81
|
+
*
|
|
82
|
+
* `b.` — the factory's default — sorts it after the resize toggle, so the two
|
|
83
|
+
* modules render as the one row a user sees rather than in registration order.
|
|
90
84
|
*/
|
|
91
85
|
export declare const c4LegendToolbarConfig: {
|
|
92
|
-
readonly actions: [
|
|
93
|
-
readonly id: "b.legend";
|
|
94
|
-
readonly tooltipWording: import("@formicoidea/labre-core/shared/services").ChromeWording;
|
|
95
|
-
readonly icon: TemplateResult<1>;
|
|
96
|
-
readonly run: (ctx: ToolbarContext) => void;
|
|
97
|
-
}];
|
|
86
|
+
readonly actions: [import("@formicoidea/labre-core/shared/services").ToolbarAction];
|
|
98
87
|
readonly when: (ctx: ToolbarContext) => boolean;
|
|
99
88
|
};
|
|
100
89
|
/**
|
package/dist/toolbar/config.js
CHANGED
|
@@ -1,10 +1,8 @@
|
|
|
1
|
-
import { EdgelessCRUDIdentifier, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
2
|
-
import { dddLegendIcon } from '@formicoidea/labre-ddd-shared';
|
|
1
|
+
import { EdgelessCRUDIdentifier, legendToolbarAction, validationToolbarConfig, } from '@formicoidea/labre-core/blocks/surface';
|
|
3
2
|
import { C4BoardElementModel } from '@formicoidea/labre-core/model';
|
|
4
|
-
import {
|
|
5
|
-
import { BlockFlavourIdentifier
|
|
3
|
+
import { BOARD_RESIZE_TOGGLE, commandMoreAction, TelemetryProvider, ToolbarModuleExtension, translateKey, } from '@formicoidea/labre-core/shared/services';
|
|
4
|
+
import { BlockFlavourIdentifier } from '@formicoidea/labre-core/std';
|
|
6
5
|
import { html, nothing } from 'lit';
|
|
7
|
-
import { createC4Legend } from '../actions.js';
|
|
8
6
|
import { C4_BOARD_LEVEL_MENU } from '../levels.js';
|
|
9
7
|
import { c4ExportMermaidIcon } from './icons.js';
|
|
10
8
|
const ResizeIcon = html `<svg
|
|
@@ -56,55 +54,6 @@ const CheckIcon = html `<svg
|
|
|
56
54
|
>
|
|
57
55
|
<path d="M5 12.5l4.5 4.5L19 7.5" />
|
|
58
56
|
</svg>`;
|
|
59
|
-
const findCommand = (ctx, id) => getRegisteredCommands(ctx.std).find(candidate => candidate.id === id);
|
|
60
|
-
/**
|
|
61
|
-
* A "⋮" entry that INVOKES a registered command instead of restating what it
|
|
62
|
-
* does — the shape `docs/adr/0010` M3 introduced, and the reason the export has
|
|
63
|
-
* one behaviour, one availability rule and one telemetry emission whether it is
|
|
64
|
-
* reached from here, from the catalogue, from the palette, from
|
|
65
|
-
* Settings › Shortcuts or from the agent.
|
|
66
|
-
*
|
|
67
|
-
* Two things the widget imposes: a menu line is drawn from `label` (a tooltip on
|
|
68
|
-
* a line that is already words would be a second copy of them), and
|
|
69
|
-
* `placement: ActionPlacement.More` is what partitions it out of the row in the
|
|
70
|
-
* first place — `renderToolbar` splits on exactly that flag, and the entries it
|
|
71
|
-
* collects come from every module contributing to the element, so a framework
|
|
72
|
-
* can add to the "⋮" without owning it.
|
|
73
|
-
*
|
|
74
|
-
* `generate` rather than a static entry because the i18n seam needs `std`:
|
|
75
|
-
* `translateKey` is what reaches the host's catalogue, and a hard-coded English
|
|
76
|
-
* label would be the one wording a host could not override.
|
|
77
|
-
*
|
|
78
|
-
* Lifted from the BPMN pool's row, where `bpmn.exportXml` sits in exactly this
|
|
79
|
-
* position.
|
|
80
|
-
*/
|
|
81
|
-
function commandMoreAction(id, commandId, labelKey, labelFallback, icon) {
|
|
82
|
-
return {
|
|
83
|
-
id,
|
|
84
|
-
placement: ActionPlacement.More,
|
|
85
|
-
when: (ctx) => {
|
|
86
|
-
const command = findCommand(ctx, commandId);
|
|
87
|
-
return command !== undefined && (command.when?.(ctx.std) ?? true);
|
|
88
|
-
},
|
|
89
|
-
generate: (ctx) => ({
|
|
90
|
-
icon,
|
|
91
|
-
label: translateKey(ctx.std, labelKey, labelFallback),
|
|
92
|
-
run: (runCtx) => {
|
|
93
|
-
const command = findCommand(runCtx, commandId);
|
|
94
|
-
if (!command)
|
|
95
|
-
return;
|
|
96
|
-
// The same `source` the row's own entries report: the "⋮" is a
|
|
97
|
-
// degradation of the row, not a surface of its own, and the
|
|
98
|
-
// `ElementCreationSource` union deliberately names places rather than
|
|
99
|
-
// widths (`docs/adr/0008`).
|
|
100
|
-
runCommand(runCtx.std, command, {
|
|
101
|
-
surface: 'contextual-toolbar',
|
|
102
|
-
source: 'toolbar:general',
|
|
103
|
-
});
|
|
104
|
-
},
|
|
105
|
-
}),
|
|
106
|
-
};
|
|
107
|
-
}
|
|
108
57
|
/**
|
|
109
58
|
* The selected C4 board's contextual toolbar: the resize toggle, and the
|
|
110
59
|
* automatic legend of what is actually drawn on the board.
|
|
@@ -118,15 +67,14 @@ function commandMoreAction(id, commandId, labelKey, labelFallback, icon) {
|
|
|
118
67
|
* The PO's call is that generating one belongs to a board you have SELECTED and
|
|
119
68
|
* to nothing else: it is not an artefact to pick off a palette, and an entry in
|
|
120
69
|
* a catalogue of things C4 draws would offer it to a user with no board in front
|
|
121
|
-
* of them. So there is no `c4.legend` command, this button is the only way
|
|
122
|
-
* reach it
|
|
70
|
+
* of them. So there is no `c4.legend` command, and this button is the only way
|
|
71
|
+
* to reach it.
|
|
123
72
|
*
|
|
124
|
-
* That is the same shape — and the same exception —
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
*
|
|
129
|
-
* matching what `reportCommandTelemetry` would have sent.
|
|
73
|
+
* That is the same shape — and the same exception — every other framework with a
|
|
74
|
+
* legend makes. The cost is the one the bottleneck exists to avoid: the
|
|
75
|
+
* telemetry it owes is emitted outside `runCommand`. It is emitted ONCE, by the
|
|
76
|
+
* shared factory (`legend-toolbar.ts`), so the seven copies of those wire values
|
|
77
|
+
* that used to sit in seven files can no longer drift apart.
|
|
130
78
|
*
|
|
131
79
|
* ## Two modules on one element, and why
|
|
132
80
|
*
|
|
@@ -345,32 +293,21 @@ const levelPickerAction = {
|
|
|
345
293
|
/**
|
|
346
294
|
* The legend button — the flag-gated half of the row (see the note above).
|
|
347
295
|
*
|
|
348
|
-
*
|
|
349
|
-
*
|
|
296
|
+
* The SHARED factory, not a C4 copy of it: the button, the placement, the box
|
|
297
|
+
* and the one `FrameworkLegendCreated` emission all live in the surface block
|
|
298
|
+
* (`legend-toolbar.ts`, `docs/adr/0026`), and C4 hands it the board model to
|
|
299
|
+
* look for, whose commands to read the rows off, and the historical wire value.
|
|
300
|
+
*
|
|
301
|
+
* `b.` — the factory's default — sorts it after the resize toggle, so the two
|
|
302
|
+
* modules render as the one row a user sees rather than in registration order.
|
|
350
303
|
*/
|
|
351
304
|
export const c4LegendToolbarConfig = {
|
|
352
305
|
actions: [
|
|
353
|
-
{
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
createC4Legend(ctx.std);
|
|
359
|
-
ctx.std
|
|
360
|
-
.getOptional(TelemetryProvider)
|
|
361
|
-
?.track('FrameworkLegendCreated', {
|
|
362
|
-
// The WIRE values, and they are the ones `reportCommandTelemetry`
|
|
363
|
-
// would have sent for a `kind: 'legend'` command — `framework` from
|
|
364
|
-
// the descriptor's `telemetryKey`, `element: 'legend'` as Wardley
|
|
365
|
-
// and the Context Map both emit, so the three are one metric.
|
|
366
|
-
framework: 'c4',
|
|
367
|
-
element: 'legend',
|
|
368
|
-
page: 'whiteboard editor',
|
|
369
|
-
segment: 'element toolbar',
|
|
370
|
-
module: 'c4 toolbar',
|
|
371
|
-
});
|
|
372
|
-
},
|
|
373
|
-
},
|
|
306
|
+
legendToolbarAction({
|
|
307
|
+
Model: C4BoardElementModel,
|
|
308
|
+
owner: 'c4',
|
|
309
|
+
framework: 'c4',
|
|
310
|
+
}),
|
|
374
311
|
],
|
|
375
312
|
when: (ctx) => ctx.getSurfaceModelsByType(C4BoardElementModel).length > 0,
|
|
376
313
|
};
|
package/dist/toolbar/icons.d.ts
CHANGED
|
@@ -67,11 +67,14 @@ export declare const c4ExportMermaidIcon: import("lit-html").TemplateResult<2>;
|
|
|
67
67
|
*
|
|
68
68
|
* {@link c4ExportMermaidIcon} is deliberately NOT here and is added to
|
|
69
69
|
* `c4CommandIcons` in `commands.ts` instead: this record is the TOOLBOX — the
|
|
70
|
-
* thirteen glyphs of things C4 DRAWS — and an export draws nothing.
|
|
70
|
+
* thirteen glyphs of things C4 DRAWS — and an export draws nothing. The same
|
|
71
|
+
* goes for {@link c4ToolbarIcon}, registered there under `c4.toolbar` for
|
|
72
|
+
* `FrameworkDescriptor.iconKey`; it is the one key that test exempts from the
|
|
73
|
+
* glyph ⇄ command bijection.
|
|
71
74
|
*
|
|
72
75
|
* The legend's glyph is in neither, because the legend is no longer a command at
|
|
73
|
-
* all: it is
|
|
74
|
-
* that is now the only way to reach it (PO arbitration, 27/08/2026).
|
|
76
|
+
* all: it is the surface block's own `legendIcon`, drawn by the shared button
|
|
77
|
+
* factory that is now the only way to reach it (PO arbitration, 27/08/2026).
|
|
75
78
|
*/
|
|
76
79
|
export declare const C4_TOOLBOX_ICONS: {
|
|
77
80
|
readonly 'c4.person': import("lit-html").TemplateResult<2>;
|
package/dist/toolbar/icons.js
CHANGED
|
@@ -134,11 +134,14 @@ export const c4ExportMermaidIcon = svg `<svg width="24" height="24" viewBox="0 0
|
|
|
134
134
|
*
|
|
135
135
|
* {@link c4ExportMermaidIcon} is deliberately NOT here and is added to
|
|
136
136
|
* `c4CommandIcons` in `commands.ts` instead: this record is the TOOLBOX — the
|
|
137
|
-
* thirteen glyphs of things C4 DRAWS — and an export draws nothing.
|
|
137
|
+
* thirteen glyphs of things C4 DRAWS — and an export draws nothing. The same
|
|
138
|
+
* goes for {@link c4ToolbarIcon}, registered there under `c4.toolbar` for
|
|
139
|
+
* `FrameworkDescriptor.iconKey`; it is the one key that test exempts from the
|
|
140
|
+
* glyph ⇄ command bijection.
|
|
138
141
|
*
|
|
139
142
|
* The legend's glyph is in neither, because the legend is no longer a command at
|
|
140
|
-
* all: it is
|
|
141
|
-
* that is now the only way to reach it (PO arbitration, 27/08/2026).
|
|
143
|
+
* all: it is the surface block's own `legendIcon`, drawn by the shared button
|
|
144
|
+
* factory that is now the only way to reach it (PO arbitration, 27/08/2026).
|
|
142
145
|
*/
|
|
143
146
|
export const C4_TOOLBOX_ICONS = {
|
|
144
147
|
'c4.person': c4PersonIcon,
|