@formicoidea/labre-framework-c4 0.33.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/actions.d.ts +179 -0
- package/dist/actions.js +375 -0
- package/dist/background.d.ts +77 -0
- package/dist/background.js +223 -0
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +221 -0
- package/dist/component.d.ts +192 -0
- package/dist/component.js +188 -0
- package/dist/consts.d.ts +331 -0
- package/dist/consts.js +384 -0
- package/dist/descriptor.d.ts +12 -0
- package/dist/descriptor.js +10 -0
- package/dist/effects.d.ts +9 -0
- package/dist/effects.js +6 -0
- package/dist/element-renderer.d.ts +18 -0
- package/dist/element-renderer.js +14 -0
- package/dist/element-view.d.ts +51 -0
- package/dist/element-view.js +146 -0
- package/dist/export.d.ts +184 -0
- package/dist/export.js +454 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.js +52 -0
- package/dist/interchange.d.ts +74 -0
- package/dist/interchange.js +143 -0
- package/dist/legend.d.ts +37 -0
- package/dist/legend.js +123 -0
- package/dist/levels.d.ts +70 -0
- package/dist/levels.js +46 -0
- package/dist/morph.d.ts +89 -0
- package/dist/morph.js +229 -0
- package/dist/node/node-renderer.d.ts +6 -0
- package/dist/node/node-renderer.js +304 -0
- package/dist/node/node-view.d.ts +45 -0
- package/dist/node/node-view.js +80 -0
- package/dist/node/type-line-watcher.d.ts +70 -0
- package/dist/node/type-line-watcher.js +142 -0
- package/dist/presets.d.ts +84 -0
- package/dist/presets.js +149 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +177 -0
- package/dist/roles.d.ts +116 -0
- package/dist/roles.js +303 -0
- package/dist/rules.d.ts +95 -0
- package/dist/rules.js +1261 -0
- package/dist/toolbar/c4-menu.d.ts +11 -0
- package/dist/toolbar/c4-menu.js +14 -0
- package/dist/toolbar/c4-senior-button.d.ts +19 -0
- package/dist/toolbar/c4-senior-button.js +23 -0
- package/dist/toolbar/config.d.ts +150 -0
- package/dist/toolbar/config.js +436 -0
- package/dist/toolbar/icons.d.ts +90 -0
- package/dist/toolbar/icons.js +157 -0
- package/dist/toolbar/senior-tool.d.ts +1 -0
- package/dist/toolbar/senior-tool.js +11 -0
- package/dist/translations.d.ts +18 -0
- package/dist/translations.js +42 -0
- package/dist/type-line.d.ts +175 -0
- package/dist/type-line.js +244 -0
- package/dist/view.d.ts +33 -0
- package/dist/view.js +148 -0
- package/package.json +34 -0
|
@@ -0,0 +1,188 @@
|
|
|
1
|
+
import { DESCRIPTION_FONT_SIZE, DESCRIPTION_LINES, DESCRIPTION_PLACEHOLDER, PERSON_BODY_TOP, TIER_LINE_HEIGHT, TIER_MARGIN, TIER_SIDE_INSET, TITLE_FONT_SIZE, TITLE_LINES, TITLE_TYPE_GAP, TYPE_DESCRIPTION_GAP, TYPE_FONT_SIZE, } from './consts.js';
|
|
2
|
+
import { C4_ROLE } from './roles.js';
|
|
3
|
+
import { technologyOfTypeLine, TYPE_TECHNOLOGY_PLACEHOLDER } from './type-line.js';
|
|
4
|
+
/**
|
|
5
|
+
* The three text boxes of a component, laid out against the node's own box.
|
|
6
|
+
*
|
|
7
|
+
* ## The rhythm
|
|
8
|
+
*
|
|
9
|
+
* A margin, the name over two lines, a small gap, the type line, a wider gap,
|
|
10
|
+
* the description over two, and the same margin again — which is exactly what
|
|
11
|
+
* {@link NODE_BOX} is tall enough for, because the box is derived from this
|
|
12
|
+
* stack rather than the stack fitted into the box. So the default element is
|
|
13
|
+
* neither cramped nor half empty, and the two gaps say what they mean: the name
|
|
14
|
+
* and its type line are one heading, the sentence under them is a second
|
|
15
|
+
* statement.
|
|
16
|
+
*
|
|
17
|
+
* The tiers are stacked by walking DOWN — each one placed under the last plus
|
|
18
|
+
* its gap — rather than by six independent offsets. Six offsets is six chances
|
|
19
|
+
* for two tiers to overlap; a walk cannot produce one.
|
|
20
|
+
*
|
|
21
|
+
* ## The person
|
|
22
|
+
*
|
|
23
|
+
* `bodyTop` is the one asymmetry and it is the stencil's: a person's head stands
|
|
24
|
+
* clear ABOVE a body of the standard height, and its words are laid out in the
|
|
25
|
+
* BODY (`v:textRect` is the body box exactly), so the whole stack starts below
|
|
26
|
+
* the head rather than across it.
|
|
27
|
+
*
|
|
28
|
+
* ## What this is not
|
|
29
|
+
*
|
|
30
|
+
* A creation-time answer and nothing more. The tiers are real elements from the
|
|
31
|
+
* moment they are drawn: an author who moves one has moved it, and nothing here
|
|
32
|
+
* runs again to put it back. It is also proportional in the one direction that
|
|
33
|
+
* matters — a node dragged taller keeps its margins where they were, because
|
|
34
|
+
* they are absolutes; only the person's head, which is a picture, scales.
|
|
35
|
+
*/
|
|
36
|
+
export function c4TierBoxes(kind, x, y, w, h) {
|
|
37
|
+
const bodyTop = kind === 'person' || kind === 'person-ext' ? h * PERSON_BODY_TOP : 0;
|
|
38
|
+
const inset = w * TIER_SIDE_INSET;
|
|
39
|
+
const width = w - inset * 2;
|
|
40
|
+
let top = bodyTop + TIER_MARGIN;
|
|
41
|
+
const tier = (fontSize, lines, gapAfter) => {
|
|
42
|
+
const box = {
|
|
43
|
+
x: x + inset,
|
|
44
|
+
y: y + top,
|
|
45
|
+
w: width,
|
|
46
|
+
h: fontSize * TIER_LINE_HEIGHT * lines,
|
|
47
|
+
};
|
|
48
|
+
top += box.h + gapAfter;
|
|
49
|
+
return box;
|
|
50
|
+
};
|
|
51
|
+
return {
|
|
52
|
+
title: tier(TITLE_FONT_SIZE, TITLE_LINES, TITLE_TYPE_GAP),
|
|
53
|
+
typeLine: tier(TYPE_FONT_SIZE, 1, TYPE_DESCRIPTION_GAP),
|
|
54
|
+
description: tier(DESCRIPTION_FONT_SIZE, DESCRIPTION_LINES, 0),
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* The tiers of the component a node belongs to — `{}` when it belongs to none.
|
|
59
|
+
*
|
|
60
|
+
* ## Group membership, then roles
|
|
61
|
+
*
|
|
62
|
+
* The group answers "which words are THIS node's" — two containers side by side
|
|
63
|
+
* both have a `[Container: …]` under them, and only the grouping says which is
|
|
64
|
+
* which. The role then answers "which of these words is the type line", which
|
|
65
|
+
* position in `children` cannot: a group's child order is an implementation
|
|
66
|
+
* detail that a reorder, a copy or a regroup rewrites, while a role is written
|
|
67
|
+
* on the element and travels with it.
|
|
68
|
+
*
|
|
69
|
+
* ## What a bare node resolves to, and why that is the right answer
|
|
70
|
+
*
|
|
71
|
+
* `{}` — no name, no technology, no description. Which is exactly what happens
|
|
72
|
+
* to a node whose group was released (native "ungroup"), to one whose texts were
|
|
73
|
+
* deleted, and to one drawn before this change. None of those is an error and
|
|
74
|
+
* none of them is guessed at: an element with no words on it states nothing, and
|
|
75
|
+
* the export says so by writing nothing. The picture is still a C4 element — the
|
|
76
|
+
* role is on the shape and survives everything.
|
|
77
|
+
*
|
|
78
|
+
* The NAME is the one tier with somewhere else to look, and only for the last of
|
|
79
|
+
* those three: an element drawn before the title became a child keeps its name
|
|
80
|
+
* in the shape's own inner text. See {@link c4StatedName}.
|
|
81
|
+
*
|
|
82
|
+
* The FIRST group holding the node wins, and the first text of each role in it.
|
|
83
|
+
* Groups nest, so a component grouped again inside a bigger group has two
|
|
84
|
+
* ancestors; document order picks the one written first, which is the innermost
|
|
85
|
+
* the creation site made.
|
|
86
|
+
*/
|
|
87
|
+
export function c4ComponentTiers(nodeId, groups, texts) {
|
|
88
|
+
const siblings = new Set(c4ComponentSiblings(nodeId, groups));
|
|
89
|
+
if (siblings.size === 0)
|
|
90
|
+
return {};
|
|
91
|
+
const tiers = {};
|
|
92
|
+
for (const text of texts) {
|
|
93
|
+
if (!siblings.has(text.id))
|
|
94
|
+
continue;
|
|
95
|
+
if (text.role === C4_ROLE.title)
|
|
96
|
+
tiers.title ??= text;
|
|
97
|
+
else if (text.role === C4_ROLE['type-line'])
|
|
98
|
+
tiers.typeLine ??= text;
|
|
99
|
+
else if (text.role === C4_ROLE.description)
|
|
100
|
+
tiers.description ??= text;
|
|
101
|
+
}
|
|
102
|
+
return tiers;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Everything grouped with this element — `[]` when it is grouped with nothing.
|
|
106
|
+
*
|
|
107
|
+
* The other direction of the same question {@link c4ComponentTiers} asks, and
|
|
108
|
+
* the one the commit hook needs: given a type line somebody has just finished
|
|
109
|
+
* typing into, which shape does it belong to, so its kind can supply the word?
|
|
110
|
+
*
|
|
111
|
+
* The element itself is among the siblings, which is what the id list actually
|
|
112
|
+
* says and what saves the caller from reasoning about whether it was excluded.
|
|
113
|
+
*/
|
|
114
|
+
export function c4ComponentSiblings(elementId, groups) {
|
|
115
|
+
const group = groups.find(candidate => candidate.childIds.includes(elementId));
|
|
116
|
+
return group ? group.childIds : [];
|
|
117
|
+
}
|
|
118
|
+
/* ── What a tier actually states ───────────────────────────────────────── */
|
|
119
|
+
/** Whatever a tier says, as a plain trimmed string. */
|
|
120
|
+
export function c4TierText(tier) {
|
|
121
|
+
const value = tier?.text;
|
|
122
|
+
if (value === null || value === undefined)
|
|
123
|
+
return '';
|
|
124
|
+
return String(value).trim();
|
|
125
|
+
}
|
|
126
|
+
/**
|
|
127
|
+
* The NAME a component states — its `c4:title` tier if it has one, and the
|
|
128
|
+
* shape's own inner text if it has not.
|
|
129
|
+
*
|
|
130
|
+
* ## No placeholder suppression, unlike the other two
|
|
131
|
+
*
|
|
132
|
+
* A fresh element's title reads `Container`, and that goes into the export
|
|
133
|
+
* verbatim. It is not a prompt standing in for a value the way
|
|
134
|
+
* `[Container: technology]` is: an unnamed container IS a container, and
|
|
135
|
+
* `Container(x, "Container")` is a true statement about a box somebody drew and
|
|
136
|
+
* has not named yet. Blanking it would hand the reader `?` instead — less
|
|
137
|
+
* information, not more honesty. The other two tiers suppress their prompts
|
|
138
|
+
* because "built with a technology called technology" is not true of anything.
|
|
139
|
+
*
|
|
140
|
+
* ## The fallback, and who needs it
|
|
141
|
+
*
|
|
142
|
+
* An element drawn before 28/08/2026 keeps its name in the SHAPE's native inner
|
|
143
|
+
* text, which is where that iteration put it, and it has no title child at all.
|
|
144
|
+
* That is the whole of the compatibility story and it costs one `??`: nothing is
|
|
145
|
+
* migrated, nothing is rewritten, and such an element exports exactly as it
|
|
146
|
+
* always did. The test is EXISTENCE of the tier, not whether it is empty — a
|
|
147
|
+
* component whose title the author deliberately cleared has been cleared, and
|
|
148
|
+
* reaching past it to a shape text that is not there either would say nothing
|
|
149
|
+
* different anyway.
|
|
150
|
+
*/
|
|
151
|
+
export function c4StatedName(tiers, shapeText) {
|
|
152
|
+
if (tiers.title)
|
|
153
|
+
return c4TierText(tiers.title);
|
|
154
|
+
return shapeText === null || shapeText === undefined
|
|
155
|
+
? ''
|
|
156
|
+
: String(shapeText).trim();
|
|
157
|
+
}
|
|
158
|
+
/**
|
|
159
|
+
* The technology a component STATES — `''` when it states none.
|
|
160
|
+
*
|
|
161
|
+
* The one place the creation placeholder is read as "nothing yet". Every tier
|
|
162
|
+
* exists from the moment a component is drawn, so an element nobody has typed on
|
|
163
|
+
* carries a literal `[Container: technology]`; exporting that as a technology
|
|
164
|
+
* would put the word "technology" in the technology slot of a file somebody is
|
|
165
|
+
* about to paste into a renderer.
|
|
166
|
+
*
|
|
167
|
+
* Deliberately NOT done in `technologyOfTypeLine`, which the commit hook also
|
|
168
|
+
* calls: normalising the placeholder to "nothing" would let a focus-and-blur
|
|
169
|
+
* silently rewrite `[Container: technology]` to `[Container]` and eat the
|
|
170
|
+
* stencil's own prompt. Reading and rewriting are different questions, and only
|
|
171
|
+
* the reader gets to be opinionated.
|
|
172
|
+
*
|
|
173
|
+
* The comparison is on the WORD rather than on the whole line, so it holds for
|
|
174
|
+
* every kind and for a placeholder an author moved brackets around.
|
|
175
|
+
*/
|
|
176
|
+
export function c4StatedTechnology(tier) {
|
|
177
|
+
const technology = technologyOfTypeLine(c4TierText(tier));
|
|
178
|
+
return technology.toLowerCase() === TYPE_TECHNOLOGY_PLACEHOLDER
|
|
179
|
+
? ''
|
|
180
|
+
: technology;
|
|
181
|
+
}
|
|
182
|
+
/** The description a component STATES — `''` for the untouched placeholder. */
|
|
183
|
+
export function c4StatedDescription(tier) {
|
|
184
|
+
const description = c4TierText(tier);
|
|
185
|
+
return description.toLowerCase() === DESCRIPTION_PLACEHOLDER
|
|
186
|
+
? ''
|
|
187
|
+
: description;
|
|
188
|
+
}
|
package/dist/consts.d.ts
ADDED
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import type { C4BoundaryVariant, C4NodeKind } from '@formicoidea/labre-core/model';
|
|
2
|
+
/**
|
|
3
|
+
* Visual constants for the C4 pack.
|
|
4
|
+
*
|
|
5
|
+
* Unlike BPMN — a black-and-white notation where the SHAPE carries the meaning
|
|
6
|
+
* and colour is decoration — C4's official stencil is a colour code: the four
|
|
7
|
+
* levels are four blues, from the near-navy of a person down to the pale wash of
|
|
8
|
+
* a component, and anything outside the scope of the diagram is grey. That is
|
|
9
|
+
* the one thing a reader uses to tell a container from a component when both are
|
|
10
|
+
* rounded rectangles with words in them, so the palette below IS the notation
|
|
11
|
+
* and is written down as data rather than left to whoever draws next.
|
|
12
|
+
*
|
|
13
|
+
* Every hex, every size and every radius below is read off the PO's own
|
|
14
|
+
* reference model — the Visio-exported `C4Model_default.svg` stencil — rather
|
|
15
|
+
* than approximated, and the file's units are carried through at ×2 so that a
|
|
16
|
+
* default node is a comfortable size on a canvas. Where a number is a fraction
|
|
17
|
+
* of the node box rather than an absolute, it lives in the renderer beside the
|
|
18
|
+
* path it shapes. Every value here is a creation-time DEFAULT; each one is an
|
|
19
|
+
* editable shape property afterwards, exactly as in BPMN.
|
|
20
|
+
*/
|
|
21
|
+
/** The stencil's own unit → model unit factor. Every absolute below is ×2. */
|
|
22
|
+
export declare const STENCIL_SCALE = 2;
|
|
23
|
+
/** One kind's three colours: the body, the line round it, and the words in it. */
|
|
24
|
+
export interface C4NodePaint {
|
|
25
|
+
fill: string;
|
|
26
|
+
/** The border — the fill, taken a step darker. */
|
|
27
|
+
border: string;
|
|
28
|
+
/** The inner text, chosen for contrast against {@link fill}. */
|
|
29
|
+
text: string;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* The palette, per kind. TOTAL over {@link C4NodeKind} by its type, so a kind
|
|
33
|
+
* added to the model cannot land without being given a colour.
|
|
34
|
+
*
|
|
35
|
+
* The four levels run light as they go IN: a person is the darkest thing on the
|
|
36
|
+
* page (#08427b), a software system one step lighter (#1168bd), every container
|
|
37
|
+
* lighter again (#438dd5) and a component palest of all (#85bbf0) — which is
|
|
38
|
+
* also the one that takes black text, because white on that wash is unreadable.
|
|
39
|
+
* `database`, `mobile` and `browser` are CONTAINERS and take the container's
|
|
40
|
+
* colour exactly: what makes them different is the silhouette the renderer
|
|
41
|
+
* draws, never the level, and a fourth blue would say otherwise.
|
|
42
|
+
*
|
|
43
|
+
* Every border is the stencil's own darker shade of its fill, lifted verbatim
|
|
44
|
+
* from the reference model's stylesheet (`.st1`, `.st5`, `.st6`, `.st7`,
|
|
45
|
+
* `.st22`) rather than darkened by eye — which is what makes the two decorated
|
|
46
|
+
* containers work at all: `mobile` and `browser` paint their BEZEL in the border
|
|
47
|
+
* colour and their SCREEN in the fill, so the pair has to be the stencil's pair.
|
|
48
|
+
*/
|
|
49
|
+
export declare const NODE_PALETTE: Record<C4NodeKind, C4NodePaint>;
|
|
50
|
+
/** Border weight of every node — one line (stencil `stroke-width:1`), ×2. */
|
|
51
|
+
export declare const NODE_STROKE_WIDTH = 2;
|
|
52
|
+
/**
|
|
53
|
+
* Corner radius of each kind's OUTER body, in model units.
|
|
54
|
+
*
|
|
55
|
+
* A table rather than one number, because the stencil is not uniform and the
|
|
56
|
+
* difference is legible: `system`, `system-ext`, `container` and `component` are
|
|
57
|
+
* plain `<rect>`s with **no `rx` at all** — square corners — while the two
|
|
58
|
+
* decorated containers are rounded, the phone noticeably (`rx="4.252"`) and the
|
|
59
|
+
* browser window barely (`rx="1.4173"`). The three glyph-bodied kinds carry `0`
|
|
60
|
+
* because their native rect paints nothing: a person's shoulders and a
|
|
61
|
+
* cylinder's lid are curves the glyph draws itself, in proportion to the box.
|
|
62
|
+
*
|
|
63
|
+
* This corrects the pack's first pass, which rounded every kind at 10 — a
|
|
64
|
+
* plausible house style, and one the reference model does not draw.
|
|
65
|
+
*/
|
|
66
|
+
export declare const NODE_RADIUS: Record<C4NodeKind, number>;
|
|
67
|
+
/**
|
|
68
|
+
* The three text tiers, at the stencil's own sizes (10 / 6 / 8) ×2.
|
|
69
|
+
*
|
|
70
|
+
* All three are creation-time DEFAULTS the author can change afterwards, and all
|
|
71
|
+
* three are now CANVAS TEXT ELEMENTS: since the PO's recette of 28/08/2026 a C4
|
|
72
|
+
* component is a group holding the shape and its three lines of words — the
|
|
73
|
+
* name included — so every tier is ordinary text with its own toolbar rather
|
|
74
|
+
* than something a renderer painted and nobody could type on.
|
|
75
|
+
*
|
|
76
|
+
* The name was the shape's native inner text for one iteration and is not any
|
|
77
|
+
* more, which is the second half of that recette: two kinds of text in one
|
|
78
|
+
* component meant two editors, two toolbars and two sets of rules for the same
|
|
79
|
+
* three lines. Now there is one of each.
|
|
80
|
+
*
|
|
81
|
+
* The ladder 20 / 16 / 12 is the notation and not typography — the type line is
|
|
82
|
+
* smaller than the name on every C4 diagram ever drawn — but it is a ladder an
|
|
83
|
+
* author can climb off, which is the price of letting them write on the picture.
|
|
84
|
+
*/
|
|
85
|
+
export declare const TITLE_FONT_SIZE: number;
|
|
86
|
+
export declare const TYPE_FONT_SIZE: number;
|
|
87
|
+
export declare const DESCRIPTION_FONT_SIZE: number;
|
|
88
|
+
/**
|
|
89
|
+
* One line box, as a multiple of its own font size — what the creation site
|
|
90
|
+
* measures a tier's HEIGHT in.
|
|
91
|
+
*
|
|
92
|
+
* The stencil states its tiers as baselines, which is the right unit for a
|
|
93
|
+
* renderer painting into a box and the wrong one for a creation site placing an
|
|
94
|
+
* element: a text element is a rectangle, and where its first baseline lands
|
|
95
|
+
* inside that rectangle is the text renderer's business, not this file's. So the
|
|
96
|
+
* stencil's baseline steps are re-read here as line boxes, which is the same
|
|
97
|
+
* geometry counted from the other end.
|
|
98
|
+
*/
|
|
99
|
+
export declare const TIER_LINE_HEIGHT = 1.2;
|
|
100
|
+
/**
|
|
101
|
+
* How many lines each of the two wrapping tiers opens with.
|
|
102
|
+
*
|
|
103
|
+
* The title gets TWO, and that is what grew the element (see {@link NODE_BOX}):
|
|
104
|
+
* "Internet Banking System" is 23 characters, and 23 characters at 20px do not
|
|
105
|
+
* fit across 187 units of usable width. A one-line title box would have meant
|
|
106
|
+
* every real system name spilling out of its own tier on the day it was typed —
|
|
107
|
+
* which is precisely the cramped stack the PO's recette was about.
|
|
108
|
+
*
|
|
109
|
+
* The description gets two as well, which is what the stencil's own sentences
|
|
110
|
+
* run to. Neither is a limit: a longer text wraps inside the tier's width and
|
|
111
|
+
* grows the box downward, and the group grows with it, so a component keeps
|
|
112
|
+
* containing its own words.
|
|
113
|
+
*/
|
|
114
|
+
export declare const TITLE_LINES = 2;
|
|
115
|
+
export declare const DESCRIPTION_LINES = 2;
|
|
116
|
+
/**
|
|
117
|
+
* The vertical rhythm of the stack, in model units.
|
|
118
|
+
*
|
|
119
|
+
* Absolutes rather than multiples of a font size, and deliberately so: what the
|
|
120
|
+
* eye reads here is the SPACE between three blocks of different sizes, and a gap
|
|
121
|
+
* expressed as 0.7em of whichever tier happens to be below it changes meaning
|
|
122
|
+
* every time somebody resizes one of them. These three numbers are the layout.
|
|
123
|
+
*
|
|
124
|
+
* The two gaps are different on purpose. The name and its type line are ONE
|
|
125
|
+
* heading — `Web Application` / `[Container: Java]` is a single statement over
|
|
126
|
+
* two lines — so they sit close. The description is a different statement, and
|
|
127
|
+
* the wider gap under the type line is the stencil's own blank line: it is what
|
|
128
|
+
* keeps a sentence from reading as a fourth tier of the heading.
|
|
129
|
+
*
|
|
130
|
+
* {@link TIER_MARGIN} is equal top and bottom, which is what makes the stack sit
|
|
131
|
+
* in its box rather than in the top of it.
|
|
132
|
+
*/
|
|
133
|
+
export declare const TIER_MARGIN = 24;
|
|
134
|
+
export declare const TITLE_TYPE_GAP = 8;
|
|
135
|
+
export declare const TYPE_DESCRIPTION_GAP = 16;
|
|
136
|
+
/** Side inset the three text tiers sit within, as a fraction of the node width. */
|
|
137
|
+
export declare const TIER_SIDE_INSET = 0.06;
|
|
138
|
+
/**
|
|
139
|
+
* The height of the three tiers and the two gaps between them — the number the
|
|
140
|
+
* default element size is DERIVED from rather than fitted to.
|
|
141
|
+
*
|
|
142
|
+
* 48 + 8 + 14.4 + 16 + 38.4 = 124.8. Written as the sum it is so that changing
|
|
143
|
+
* a font size or a gap moves the box with it: a rhythm and a footprint that can
|
|
144
|
+
* disagree is a rhythm that will.
|
|
145
|
+
*/
|
|
146
|
+
export declare const TIER_STACK_HEIGHT: number;
|
|
147
|
+
/**
|
|
148
|
+
* The sentence a fresh description prompts the author with.
|
|
149
|
+
*
|
|
150
|
+
* The stencil's own placeholder, and a PROMPT rather than a value: every tier of
|
|
151
|
+
* a C4 component exists from the moment it is drawn (PO arbitration,
|
|
152
|
+
* 28/08/2026), so the author meets three lines of stencil rather than a box and
|
|
153
|
+
* two invisible slots somebody has to tell them about. The exporter compares
|
|
154
|
+
* against it to decide that nothing has been stated yet — see
|
|
155
|
+
* `C4_TYPE_PLACEHOLDER` in `type-line.ts` for the same call on the other tier.
|
|
156
|
+
*/
|
|
157
|
+
export declare const DESCRIPTION_PLACEHOLDER = "description";
|
|
158
|
+
/**
|
|
159
|
+
* How far the person's head stands clear ABOVE its body, in model units.
|
|
160
|
+
*
|
|
161
|
+
* The stencil's own `47.767` at ×2, solved off the silhouette path (`mID 1`):
|
|
162
|
+
* its head arc is drawn with `large-arc-flag=1` about a centre 21.26 units above
|
|
163
|
+
* the body's top edge, with `ry=26.504`, so the head clears the body by the sum
|
|
164
|
+
* of the two. Independent of how tall the BODY is, which is what lets the body
|
|
165
|
+
* grow with the text rhythm below without moving the head.
|
|
166
|
+
*/
|
|
167
|
+
export declare const PERSON_HEAD_CLEARANCE: number;
|
|
168
|
+
/**
|
|
169
|
+
* Default node sizes (model units) per kind.
|
|
170
|
+
*
|
|
171
|
+
* ## One footprint, and one exception the stencil itself draws
|
|
172
|
+
*
|
|
173
|
+
* The reference model gives every element the SAME box — `106.3 × 74.409`, a
|
|
174
|
+
* `v:textRect` repeated verbatim on the system, the container, the component,
|
|
175
|
+
* the database, the phone and the browser window. Seven of the nine kinds take
|
|
176
|
+
* one footprint, and a row of C4 elements lining up without anybody arranging
|
|
177
|
+
* them is not a convenience, it is what makes a level readable.
|
|
178
|
+
*
|
|
179
|
+
* ## Why the box is taller than the stencil's, and by exactly how much
|
|
180
|
+
*
|
|
181
|
+
* The WIDTH is the stencil's, untouched: `106.3 × 2 = 212.6`. Widening it would
|
|
182
|
+
* change every glyph with it — a person's head radius is derived from the width
|
|
183
|
+
* — and the reference proportions are the one thing the recette of 27/08 was
|
|
184
|
+
* about.
|
|
185
|
+
*
|
|
186
|
+
* The HEIGHT is derived from the words instead, which is the PO's call of
|
|
187
|
+
* 28/08/2026: grow the shapes if that is what it takes to have room to write.
|
|
188
|
+
* `74.409 × 2 = 148.8` was the stencil's textRect for a box holding a name it
|
|
189
|
+
* could paint in a single line at whatever size it liked. This one holds three
|
|
190
|
+
* REAL text elements, at fixed sizes, with margins and gaps a reader can see —
|
|
191
|
+
* and the title alone needs two lines, because a system name is routinely longer
|
|
192
|
+
* than 187 units of usable width at 20px. So the height is
|
|
193
|
+
* {@link TIER_STACK_HEIGHT} plus a margin at each end: **212.6 × 172.8**, up
|
|
194
|
+
* from 212.6 × 148.8.
|
|
195
|
+
*
|
|
196
|
+
* Derived rather than chosen, so the box can never disagree with what it holds:
|
|
197
|
+
* change a tier's size or a gap and the footprint follows.
|
|
198
|
+
*
|
|
199
|
+
* ## The person
|
|
200
|
+
*
|
|
201
|
+
* `person` and `person-ext` are the exception, and it is the FILE's exception,
|
|
202
|
+
* not a preference. Their silhouette is one path (`mID 1`) whose head arc is
|
|
203
|
+
* drawn about a centre above the body's top edge, so the head stands
|
|
204
|
+
* {@link PERSON_HEAD_CLEARANCE} clear of a body that is itself the standard box.
|
|
205
|
+
* The stencil's own sheet shows it: the person's group is translated further
|
|
206
|
+
* down the page than the system beside it, precisely to make room. The body
|
|
207
|
+
* grows with everything else, so the person is now **212.6 × 268.3**.
|
|
208
|
+
*
|
|
209
|
+
* Forcing a person into the boxed footprint was considered and rejected: the
|
|
210
|
+
* head is a CIRCLE (`rx 26.362`, `ry 26.504`) and stays one only at the
|
|
211
|
+
* silhouette's own aspect ratio — squeezed into a box far wider than it is tall
|
|
212
|
+
* it becomes a flat ellipse, which is the one thing about a C4 person everybody
|
|
213
|
+
* recognises and the one thing that would then be wrong.
|
|
214
|
+
*/
|
|
215
|
+
export declare const NODE_BOX: {
|
|
216
|
+
readonly w: number;
|
|
217
|
+
readonly h: number;
|
|
218
|
+
};
|
|
219
|
+
/** The person's full silhouette — the standard body, plus the head above it. */
|
|
220
|
+
export declare const PERSON_BOX: {
|
|
221
|
+
readonly w: number;
|
|
222
|
+
readonly h: number;
|
|
223
|
+
};
|
|
224
|
+
/**
|
|
225
|
+
* Where the person's body top edge sits, as a fraction of the whole silhouette.
|
|
226
|
+
*
|
|
227
|
+
* Derived from the two boxes rather than restated as the stencil's own ratio, so
|
|
228
|
+
* that growing the body cannot leave this pointing at the middle of the head.
|
|
229
|
+
* The renderer derives the same edge from the head RADIUS instead
|
|
230
|
+
* (`PERSON.bodyTopPerHead`), which is the form that survives an element dragged
|
|
231
|
+
* to an aspect ratio the head has to be clamped at; the two agree at the default
|
|
232
|
+
* size. This one is what the creation site needs: it has a box and no glyph, and
|
|
233
|
+
* it has to know where the words can go.
|
|
234
|
+
*/
|
|
235
|
+
export declare const PERSON_BODY_TOP: number;
|
|
236
|
+
export declare const NODE_SIZE: Record<C4NodeKind, {
|
|
237
|
+
w: number;
|
|
238
|
+
h: number;
|
|
239
|
+
}>;
|
|
240
|
+
/**
|
|
241
|
+
* Default inner text per kind.
|
|
242
|
+
*
|
|
243
|
+
* Every kind carries one, unlike BPMN — where an event's meaning IS its glyph
|
|
244
|
+
* and the spec puts its name outside the shape. Here the box is the same box at
|
|
245
|
+
* three of the four levels, so a C4 element with nothing written in it says
|
|
246
|
+
* nothing at all: the words are the artefact.
|
|
247
|
+
*/
|
|
248
|
+
export declare const NODE_LABEL: Record<C4NodeKind, string>;
|
|
249
|
+
export declare const FONT_FAMILY = "Inter, sans-serif";
|
|
250
|
+
/** The card — the same white every framework background paints. */
|
|
251
|
+
export declare const BOARD_CARD_FILL = "#ffffff";
|
|
252
|
+
export declare const BOARD_CARD_BORDER = "#d5d9e0";
|
|
253
|
+
export declare const BOARD_BORDER_WIDTH = 1.5;
|
|
254
|
+
export declare const BOARD_CORNER_RADIUS = 12;
|
|
255
|
+
export declare const BOARD_TITLE_FONT_SIZE = 20;
|
|
256
|
+
export declare const BOARD_TITLE_COLOR = "#262626";
|
|
257
|
+
/**
|
|
258
|
+
* The size a fresh board is created at, and the room its furniture takes.
|
|
259
|
+
*
|
|
260
|
+
* The Context Map board's own numbers: a C4 diagram is the same kind of object —
|
|
261
|
+
* a sheet you spread out and add boxes to as the system is discovered — so it
|
|
262
|
+
* starts at the same size and grows the same way. The top margin is deeper than
|
|
263
|
+
* the other three because that is where the title is written.
|
|
264
|
+
*/
|
|
265
|
+
export declare const BOARD_REF_WIDTH = 1400;
|
|
266
|
+
export declare const BOARD_REF_HEIGHT = 900;
|
|
267
|
+
export declare const BOARD_MARGIN = 24;
|
|
268
|
+
export declare const BOARD_TITLE_MARGIN = 56;
|
|
269
|
+
/**
|
|
270
|
+
* The boundary's frame and its name, at the stencil's own values ×2 (`.st20`
|
|
271
|
+
* and `.st8` in the reference model).
|
|
272
|
+
*
|
|
273
|
+
* All four numbers changed with the PO's recette of 27/08/2026, and all four
|
|
274
|
+
* were house style before it: the frame was a mid grey at weight 2 with rounded
|
|
275
|
+
* corners, and the stencil draws `#444444` at weight 1 with SQUARE ones. The
|
|
276
|
+
* name is black rather than grey and a size larger, which is what it takes to
|
|
277
|
+
* read a boundary's name over the diagram it is drawn on top of.
|
|
278
|
+
*/
|
|
279
|
+
export declare const BOUNDARY_STROKE = "#444444";
|
|
280
|
+
export declare const BOUNDARY_WIDTH: number;
|
|
281
|
+
export declare const BOUNDARY_CORNER_RADIUS = 0;
|
|
282
|
+
/** The dash, in model units: the stencil's `stroke-dasharray:7.5,4.5`, ×2. */
|
|
283
|
+
export declare const BOUNDARY_DASH: readonly [number, number];
|
|
284
|
+
export declare const BOUNDARY_NAME_FONT_SIZE: number;
|
|
285
|
+
export declare const BOUNDARY_NAME_COLOR = "#000000";
|
|
286
|
+
/**
|
|
287
|
+
* The bracket line under a boundary's name — `[Software System]`, `[Container]`
|
|
288
|
+
* — at the stencil's own 6px ×2, on the same 1.917em baseline step it uses.
|
|
289
|
+
*
|
|
290
|
+
* Vocabulary, not user text: the words come from the declaration's `labelKey`
|
|
291
|
+
* and are translatable through the host's catalogue, which is also what keeps
|
|
292
|
+
* them out of the in-place editor. A boundary's NAME is the author's; what kind
|
|
293
|
+
* of boundary it is, is the notation's.
|
|
294
|
+
*/
|
|
295
|
+
export declare const BOUNDARY_TYPE_FONT_SIZE: number;
|
|
296
|
+
export declare const BOUNDARY_TYPE_STEP: number;
|
|
297
|
+
/** How far above the bottom edge of the plot the name's baseline sits. */
|
|
298
|
+
export declare const BOUNDARY_NAME_INSET = 8;
|
|
299
|
+
export declare const BOUNDARY_REF_WIDTH = 520;
|
|
300
|
+
export declare const BOUNDARY_REF_HEIGHT = 360;
|
|
301
|
+
/**
|
|
302
|
+
* The boundary's inset. Small and equal on all four sides: unlike a board, a
|
|
303
|
+
* boundary has no furniture to make room for — the name is written INSIDE the
|
|
304
|
+
* bottom-left corner of the plot, over the diagram, exactly as C4 draws it.
|
|
305
|
+
*/
|
|
306
|
+
export declare const BOUNDARY_MARGIN = 12;
|
|
307
|
+
/**
|
|
308
|
+
* The wording a fresh boundary is named with, per variant.
|
|
309
|
+
*
|
|
310
|
+
* The variant changes the DEFAULT NAME and nothing else: both are the same
|
|
311
|
+
* dashed rectangle, and C4 tells them apart by what is written under the corner.
|
|
312
|
+
* Read by the creation site (which writes `name`), never by the renderer — the
|
|
313
|
+
* declaration draws whatever the user's own `name` says, so a boundary renamed
|
|
314
|
+
* on the canvas keeps its words whatever its variant.
|
|
315
|
+
*/
|
|
316
|
+
export declare const BOUNDARY_LABEL: Record<C4BoundaryVariant, string>;
|
|
317
|
+
/**
|
|
318
|
+
* Relationship connector preset — the dashed arrow C4 draws between elements.
|
|
319
|
+
*
|
|
320
|
+
* DASHED and not solid, which is the stencil's own choice and worth keeping:
|
|
321
|
+
* every line on a C4 diagram is a relationship, so the dash is not a
|
|
322
|
+
* distinction between two kinds of line but the house style of the one kind
|
|
323
|
+
* there is. Grey rather than black for the same reason the boundary is: the
|
|
324
|
+
* boxes are the statement, the arrows are the grammar between them.
|
|
325
|
+
*
|
|
326
|
+
* `#444444` at weight 1.5 is the stencil's `.st15` (`stroke:#444444`,
|
|
327
|
+
* `stroke-width:0.75`) at ×2, and it is the SAME grey the boundary frame is
|
|
328
|
+
* drawn in — one neutral for everything that is not an element.
|
|
329
|
+
*/
|
|
330
|
+
export declare const RELATIONSHIP_STROKE = "#444444";
|
|
331
|
+
export declare const RELATIONSHIP_WIDTH: number;
|