@formicoidea/labre-framework-uml 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.
Files changed (114) hide show
  1. package/LICENSE +384 -0
  2. package/dist/actions.d.ts +430 -0
  3. package/dist/actions.js +724 -0
  4. package/dist/background.d.ts +153 -0
  5. package/dist/background.js +557 -0
  6. package/dist/board-hit.d.ts +137 -0
  7. package/dist/board-hit.js +191 -0
  8. package/dist/commands-manifest.d.ts +19 -0
  9. package/dist/commands-manifest.js +571 -0
  10. package/dist/commands.d.ts +4 -0
  11. package/dist/commands.js +983 -0
  12. package/dist/component.d.ts +521 -0
  13. package/dist/component.js +708 -0
  14. package/dist/consts.d.ts +365 -0
  15. package/dist/consts.js +463 -0
  16. package/dist/descriptor.d.ts +12 -0
  17. package/dist/descriptor.js +10 -0
  18. package/dist/drawio-decode.d.ts +32 -0
  19. package/dist/drawio-decode.js +96 -0
  20. package/dist/drawio-import.d.ts +133 -0
  21. package/dist/drawio-import.js +885 -0
  22. package/dist/edge-morph.d.ts +154 -0
  23. package/dist/edge-morph.js +153 -0
  24. package/dist/edge-styles.d.ts +78 -0
  25. package/dist/edge-styles.js +210 -0
  26. package/dist/effects.d.ts +9 -0
  27. package/dist/effects.js +6 -0
  28. package/dist/element-renderer.d.ts +64 -0
  29. package/dist/element-renderer.js +114 -0
  30. package/dist/element-view.d.ts +234 -0
  31. package/dist/element-view.js +430 -0
  32. package/dist/export.d.ts +61 -0
  33. package/dist/export.js +41 -0
  34. package/dist/filename.d.ts +17 -0
  35. package/dist/filename.js +26 -0
  36. package/dist/frame-tag.d.ts +70 -0
  37. package/dist/frame-tag.js +78 -0
  38. package/dist/grammar.d.ts +695 -0
  39. package/dist/grammar.js +1436 -0
  40. package/dist/import.d.ts +306 -0
  41. package/dist/import.js +1270 -0
  42. package/dist/index.d.ts +47 -0
  43. package/dist/index.js +67 -0
  44. package/dist/interchange.d.ts +125 -0
  45. package/dist/interchange.js +372 -0
  46. package/dist/keywords.d.ts +204 -0
  47. package/dist/keywords.js +361 -0
  48. package/dist/kinds.d.ts +73 -0
  49. package/dist/kinds.js +91 -0
  50. package/dist/legend.d.ts +30 -0
  51. package/dist/legend.js +255 -0
  52. package/dist/model.d.ts +862 -0
  53. package/dist/model.js +1316 -0
  54. package/dist/morph.d.ts +178 -0
  55. package/dist/morph.js +496 -0
  56. package/dist/node/act-glyphs.d.ts +30 -0
  57. package/dist/node/act-glyphs.js +142 -0
  58. package/dist/node/compartment-watcher.d.ts +107 -0
  59. package/dist/node/compartment-watcher.js +163 -0
  60. package/dist/node/fit.d.ts +85 -0
  61. package/dist/node/fit.js +129 -0
  62. package/dist/node/node-renderer.d.ts +49 -0
  63. package/dist/node/node-renderer.js +611 -0
  64. package/dist/node/node-view.d.ts +51 -0
  65. package/dist/node/node-view.js +89 -0
  66. package/dist/node/paint.d.ts +84 -0
  67. package/dist/node/paint.js +143 -0
  68. package/dist/node/sd-glyphs.d.ts +36 -0
  69. package/dist/node/sd-glyphs.js +79 -0
  70. package/dist/node/stm-glyphs.d.ts +22 -0
  71. package/dist/node/stm-glyphs.js +111 -0
  72. package/dist/node/tier-metrics.d.ts +38 -0
  73. package/dist/node/tier-metrics.js +45 -0
  74. package/dist/operators.d.ts +45 -0
  75. package/dist/operators.js +75 -0
  76. package/dist/plantuml-import.d.ts +113 -0
  77. package/dist/plantuml-import.js +1723 -0
  78. package/dist/plantuml.d.ts +94 -0
  79. package/dist/plantuml.js +985 -0
  80. package/dist/presets.d.ts +149 -0
  81. package/dist/presets.js +302 -0
  82. package/dist/profiles.d.ts +2 -0
  83. package/dist/profiles.js +358 -0
  84. package/dist/reading.d.ts +160 -0
  85. package/dist/reading.js +648 -0
  86. package/dist/roles.d.ts +257 -0
  87. package/dist/roles.js +997 -0
  88. package/dist/rules.d.ts +267 -0
  89. package/dist/rules.js +3090 -0
  90. package/dist/templates.d.ts +2 -0
  91. package/dist/templates.js +197 -0
  92. package/dist/toolbar/config.d.ts +228 -0
  93. package/dist/toolbar/config.js +833 -0
  94. package/dist/toolbar/icons.d.ts +440 -0
  95. package/dist/toolbar/icons.js +711 -0
  96. package/dist/toolbar/senior-tool.d.ts +11 -0
  97. package/dist/toolbar/senior-tool.js +21 -0
  98. package/dist/toolbar/uml-menu.d.ts +17 -0
  99. package/dist/toolbar/uml-menu.js +20 -0
  100. package/dist/toolbar/uml-senior-button.d.ts +18 -0
  101. package/dist/toolbar/uml-senior-button.js +22 -0
  102. package/dist/translations.d.ts +27 -0
  103. package/dist/translations.js +117 -0
  104. package/dist/view.d.ts +33 -0
  105. package/dist/view.js +236 -0
  106. package/dist/xmi-import.d.ts +174 -0
  107. package/dist/xmi-import.js +2268 -0
  108. package/dist/xmi.d.ts +61 -0
  109. package/dist/xmi.js +1860 -0
  110. package/dist/xml-reader.d.ts +157 -0
  111. package/dist/xml-reader.js +430 -0
  112. package/dist/xml.d.ts +72 -0
  113. package/dist/xml.js +96 -0
  114. package/package.json +38 -0
@@ -0,0 +1,521 @@
1
+ import type { UmlNodeKind } from '@formicoidea/labre-core/model';
2
+ import { type UmlRoleId } from './roles.js';
3
+ /**
4
+ * A UML component — the SHAPE and its own words, grouped.
5
+ *
6
+ * ## Why a UML artefact is several elements
7
+ *
8
+ * UML 2.5.1 draws a classifier as a rectangle divided into COMPARTMENTS
9
+ * (§9.2.4, §11.4.4): the name on top, the attributes under a line, the
10
+ * operations under a second one. Those compartments are not decoration — they
11
+ * are three separate statements about the same classifier, and an author edits
12
+ * them one at a time.
13
+ *
14
+ * The library already knows how to draw that: the same arrangement C4 arrived
15
+ * at through the PO's recette of 28/08/2026 (`gfx/c4/src/component.ts`), which
16
+ * R15/R16 then generalised into a rule. A component is a native `group` holding
17
+ * a `umlNode` shape that carries NO inner text and one canvas `text` element per
18
+ * compartment, each stamped with its own role. One click selects the classifier,
19
+ * a second descends into the compartment under the pointer, and every gesture
20
+ * the editor already has works on it.
21
+ *
22
+ * ## This module
23
+ *
24
+ * The two questions that arrangement raises, both PURE and both free of `std`
25
+ * and of Yjs, so the creation site, the node renderer, the exporter and a test
26
+ * all answer them the same way:
27
+ *
28
+ * - **where do the compartments go**, given a node's box
29
+ * ({@link umlCompartmentBoxes}) — and, as a by-product nobody else can
30
+ * compute, WHERE THE SEPARATORS ARE, which is what the renderer strokes;
31
+ * - **which words belong to which shape**, given a group and a flat element
32
+ * list ({@link umlComponentSiblings}) — resolved by group membership plus the
33
+ * role stamped on each text, never by position in `children`.
34
+ *
35
+ * The role stays on the SHAPE and on each text; the group carries none
36
+ * (`roles.ts`).
37
+ */
38
+ /**
39
+ * The gap between a compartment's words and the line that closes it, and
40
+ * between the words and the outline of the box.
41
+ *
42
+ * One number for the top, the bottom and — through {@link UML_TIER_SIDE_INSET}
43
+ * — the sides, because UML draws one: the specification's figures are typeset
44
+ * with a single uniform padding inside every compartment, and a second value
45
+ * would read as a distinction between compartments that the notation never
46
+ * makes.
47
+ */
48
+ export declare const UML_TIER_MARGIN = 8;
49
+ /**
50
+ * The side padding, as a FRACTION of the box width rather than the absolute
51
+ * {@link UML_TIER_MARGIN}.
52
+ *
53
+ * A UML box is resized far more often horizontally than vertically — an author
54
+ * widens a class to fit `+ findByCustomerId(id : CustomerId) : Order`, they do
55
+ * not make it taller to fit a longer word. A proportional inset keeps the text
56
+ * column looking deliberate at every width; an absolute one would leave a
57
+ * 200-unit box looking padded and a 600-unit box looking unpadded.
58
+ */
59
+ export declare const UML_TIER_SIDE_INSET = 0.08;
60
+ /**
61
+ * Line height, as a multiple of the font size — the one number that turns
62
+ * "three lines of attributes" into a height.
63
+ */
64
+ export declare const UML_TIER_LINE_HEIGHT = 1.4;
65
+ /**
66
+ * The gap between the NAME and the line under it.
67
+ *
68
+ * Smaller than {@link UML_TIER_MARGIN} on purpose: the name compartment of a
69
+ * fresh classifier holds one line and, for an interface or an enumeration, a
70
+ * keyword line above it (§9.5.4 — `«interface»`), and that pair has to read as
71
+ * one heading rather than as a block floating in an over-tall compartment.
72
+ */
73
+ export declare const UML_NAME_GAP = 6;
74
+ /**
75
+ * How many lines the ATTRIBUTES compartment is sized for.
76
+ *
77
+ * Three, because that is what a default 200 × 120 box leaves room for once the
78
+ * name compartment and the margins are taken out, and because a classifier with
79
+ * three attributes and one or two operations is the shape of nearly every class
80
+ * anybody draws first. The operations compartment takes whatever is left, which
81
+ * is the one tier that must never be sized in lines: it is the bottom of the
82
+ * box, and a box an author has dragged taller has dragged it taller FOR the
83
+ * operations.
84
+ *
85
+ * Since the compartment watcher this is the DEFAULT rather than the sizing: a
86
+ * caller that has read the tier passes its real line count
87
+ * ({@link UmlTierLines}), and three is what a caller who has not read it gets —
88
+ * the stencil's own shape, which is every classifier's until somebody types.
89
+ */
90
+ export declare const UML_ATTRIBUTE_LINES = 3;
91
+ /**
92
+ * The share of a `package` box its TAB occupies, top-left (§12.2.4).
93
+ *
94
+ * A fraction rather than an absolute, because the tab is part of the DRAWING:
95
+ * a package dragged to twice the height is the same folder, twice as big, and a
96
+ * tab pinned to 26 units would turn into a sliver. Exported so the node
97
+ * renderer strokes the tab exactly where the name compartment stops, rather
98
+ * than the two agreeing by coincidence.
99
+ */
100
+ export declare const UML_PACKAGE_TAB = 0.2;
101
+ /**
102
+ * The share of an `actor` box the STICK FIGURE occupies, measured from the top
103
+ * (§18.1.4 — the figure, and its name written underneath it).
104
+ *
105
+ * Exported for the same reason as {@link UML_PACKAGE_TAB}: the renderer draws
106
+ * the figure ABOVE this line and {@link umlCompartmentBoxes} puts the name
107
+ * BELOW it, and a head overlapping its own label is what happens when two files
108
+ * each pick their own number.
109
+ */
110
+ export declare const UML_ACTOR_FIGURE = 0.74;
111
+ /**
112
+ * The DEPTH of the 3-D cube a node, a device and an execution environment are
113
+ * drawn as (§19.4.4), as a fraction of the box's shorter side.
114
+ *
115
+ * A fraction of the SHORTER side rather than of the width, because the depth is
116
+ * drawn at 45° in both directions: taking it from the width alone would make a
117
+ * cube dragged wide and flat lose its top face off the top of the element.
118
+ *
119
+ * Exported for the reason {@link UML_PACKAGE_TAB} is: the renderer draws the
120
+ * three faces from this number and {@link umlCompartmentBoxes} writes the name
121
+ * INSIDE the front one, so a name floating over the cube's roof is what happens
122
+ * when two files each pick their own depth. 12% is the proportion §19.4.4's own
123
+ * figures show — enough to read as a solid, little enough to leave the front
124
+ * face the size of a box.
125
+ */
126
+ export declare const UML_CUBE_DEPTH = 0.12;
127
+ /**
128
+ * The gap between a glyph too small to write in and the name written BESIDE it.
129
+ *
130
+ * Three kinds are in that position, and all three by the notation's own doing: a
131
+ * port is a small square on a border (§11.3.4), and the ball and socket of
132
+ * §10.4.4 are a curve on a stub. None of them has an inside, so the label goes
133
+ * next to the picture — which is exactly how the specification's figures write
134
+ * them.
135
+ */
136
+ export declare const UML_BESIDE_LABEL_GAP = 6;
137
+ /**
138
+ * How wide that label is allowed to be before it wraps.
139
+ *
140
+ * An absolute rather than a fraction, unlike every other measurement here,
141
+ * because it is measuring the WORDS and not the glyph: a port dragged twice as
142
+ * big is a bigger square with the same name beside it, and a name column scaled
143
+ * to a 16-unit square would be four characters wide.
144
+ */
145
+ export declare const UML_BESIDE_LABEL_WIDTH = 120;
146
+ /**
147
+ * How deep the POINT of a signal pentagon bites into its box (§16.3.4), as a
148
+ * fraction of the width.
149
+ *
150
+ * Exported for the reason {@link UML_PACKAGE_TAB} is: the renderer draws the
151
+ * pentagon from this number and {@link umlCompartmentBoxes} pulls the label box
152
+ * back out of the point by it, so a signal's name cannot end up running out
153
+ * through the tip. One number, two readers.
154
+ *
155
+ * The same depth for both shapes, because §16.3.4 draws the same pentagon twice
156
+ * and merely turns it: a send signal's point sticks OUT of the right edge, an
157
+ * accept event's bites IN from the left, and a reader tells them apart by which
158
+ * way the arrow of the silhouette runs. Two depths would make the pair look like
159
+ * two unrelated shapes.
160
+ */
161
+ export declare const UML_SIGNAL_POINT = 0.16;
162
+ /** A box in the same units and origin as the node's own. */
163
+ export interface UmlBox {
164
+ x: number;
165
+ y: number;
166
+ w: number;
167
+ h: number;
168
+ }
169
+ /**
170
+ * The written compartments of one node, plus the separators between them.
171
+ *
172
+ * `attributes` and `operations` are optional because most of the pack has
173
+ * neither: a package, a note, an actor and a use case carry ONE label and no
174
+ * compartment at all (§12.2.4, §18.1.4, Annex A), and an object carries a name
175
+ * and its slots but no operations (§9.8.4 — an instance has values, not
176
+ * behaviour).
177
+ *
178
+ * `splits` are offsets DOWN FROM THE TOP of the node's box, in model units, and
179
+ * they are the renderer's whole instruction: stroke a full-width line at each.
180
+ * They are returned from here rather than recomputed there because they are not
181
+ * a second fact — a separator IS the boundary between two compartments, and two
182
+ * files deriving it separately is how a line ends up crossing the words it was
183
+ * supposed to sit under.
184
+ */
185
+ export interface UmlCompartmentBoxes {
186
+ name: UmlBox;
187
+ attributes?: UmlBox;
188
+ operations?: UmlBox;
189
+ splits: number[];
190
+ }
191
+ /**
192
+ * How many LINES each compartment actually holds — the one thing a box cannot
193
+ * tell you.
194
+ *
195
+ * {@link umlCompartmentBoxes} is otherwise a pure function of a rectangle, and
196
+ * that was enough for as long as every classifier was assumed to hold the
197
+ * stencil it was born with: one name line, three attributes, and the operations
198
+ * taking the rest. An author who types five attributes into the second tier has
199
+ * overflowed it — the words run through the separator under them and out the
200
+ * bottom of the box — and nothing in a rectangle says so.
201
+ *
202
+ * So the counts are passed IN, from whoever has read the tiers
203
+ * ({@link umlTierLineCount}), and every field is optional: a caller that knows
204
+ * nothing about the words gets exactly the layout this module has always
205
+ * returned. `UmlCompartmentWatcher` is what supplies them after an edit
206
+ * commits, and the node renderer is what keeps the separators where the tiers
207
+ * then are.
208
+ *
209
+ * `operations` is read by {@link umlStackHeight} and NOT by
210
+ * {@link umlCompartmentBoxes}, and that asymmetry is the notation's: the
211
+ * operations compartment is the bottom of the box and always takes whatever is
212
+ * left (§11.4.4), so its line count says how tall the box must BE, never how
213
+ * tall the compartment is drawn.
214
+ */
215
+ export interface UmlTierLines {
216
+ name?: number;
217
+ attributes?: number;
218
+ operations?: number;
219
+ }
220
+ /**
221
+ * How many lines one LOGICAL line of a tier is painted as — the measurement
222
+ * this module cannot make and {@link umlTierLineCount} therefore takes.
223
+ *
224
+ * A canvas text element is drawn WRAPPED: the renderer breaks each line at the
225
+ * element's own width before it paints it (`gfx/text`'s `wrapText`), so
226
+ * `+ findByCustomerIdAndStatus(id : CustomerId) : List<Order>` in a 168-unit
227
+ * compartment is one line in the document and three on the picture. Answering
228
+ * that needs a font context — a `getFontString` and a canvas measurer — which
229
+ * this module (pure, free of `std` and of the DOM) has none of, so the caller
230
+ * that HAS one passes the answer in.
231
+ */
232
+ export type UmlTierWrap = (line: string) => number;
233
+ /**
234
+ * How many lines a tier's text is PAINTED as.
235
+ *
236
+ * Takes the VALUE rather than the element, for the reason {@link umlTierText}
237
+ * does — a `Y.Text` on a model, a string in a fixture. The author's newlines are
238
+ * counted always; `wrap` adds the ones the renderer makes, and without it the
239
+ * count is the newlines alone.
240
+ *
241
+ * ## Why `wrap` is not optional in practice
242
+ *
243
+ * It was, until the PO's recette of 14/09/2026. A tier is created with
244
+ * `hasMaxWidth`, so the text editor wraps a long signature at the compartment's
245
+ * width and grows the box downward while the author types — and the watcher then
246
+ * SHRANK that box back to one line's worth on commit, painting the wrapped
247
+ * remainder straight through the separator under it. Measured on a 200-unit
248
+ * class: three typed attribute lines, six painted, a 92.7-unit stack put back
249
+ * into a 54.6-unit compartment.
250
+ *
251
+ * So every caller that can measure must: `umlFitComponent` does, through
252
+ * `umlTierWrapper` (`node/tier-metrics.ts`), and the `wrap`-less form is what a
253
+ * fixture and a pure test get.
254
+ *
255
+ * An empty tier is one line, which is `\n`-count + 1 and also what §14.2.4
256
+ * wants: a state's behaviour compartment is seeded EMPTY and is still ruled off.
257
+ */
258
+ export declare function umlTierLineCount(text: unknown, wrap?: UmlTierWrap): number;
259
+ /**
260
+ * The three glyphs with no INSIDE, whose name is therefore written next to them
261
+ * — outside the element's own box, which nothing else in this module does.
262
+ *
263
+ * Exported because that is a fact a caller has to know: a port's label is the
264
+ * one tier that makes the GROUP bigger than the shape it belongs to, and a test
265
+ * or a layout asserting "every tier is inside its node" has to say which three
266
+ * kinds the notation exempts (§11.3.4, §10.4.4).
267
+ */
268
+ export declare const UML_BESIDE_LABEL_KINDS: ReadonlySet<UmlNodeKind>;
269
+ /**
270
+ * The kinds whose one tier is written in a HEAD drawn wider than the element
271
+ * itself — today the lifeline, and it is the second and last way a tier is
272
+ * allowed outside its own box.
273
+ *
274
+ * ## Why a lifeline is shaped like this at all
275
+ *
276
+ * §17.2.4 draws a lifeline as a NAMED BOX with a dashed line falling out of the
277
+ * bottom of it, and the dashed line is the part that matters: a message is
278
+ * attached to it at the height where it happens, and an execution bar sits on
279
+ * it. So the ELEMENT is the spine — a narrow, tall column whose left and right
280
+ * perimeters are where the connector layer puts an anchor — and the head is a
281
+ * rectangle the renderer draws across the top of it, centred, overflowing it by
282
+ * the same amount on each side (`consts.ts`, `UML_LIFELINE_HEAD`).
283
+ *
284
+ * That makes the head the one place the participant's name can go. Writing it
285
+ * inside a 16-unit column would be writing it on the dashes.
286
+ *
287
+ * ## Why it is not {@link UML_BESIDE_LABEL_KINDS}
288
+ *
289
+ * Because it is not beside anything: a port's name sits to the RIGHT of a glyph
290
+ * that has no inside, and a lifeline's name sits IN a head the renderer draws.
291
+ * The two would share nothing but the fact that a bounding box has to allow for
292
+ * them, and folding them together would give a lifeline a label floating 6
293
+ * units off the spine. Two sets, two placements, one invariant each.
294
+ */
295
+ export declare const UML_HEAD_LABEL_KINDS: ReadonlySet<UmlNodeKind>;
296
+ /**
297
+ * WHICH tier role a kind's single word carries, for every kind whose word is a
298
+ * label rather than a name compartment.
299
+ *
300
+ * The companion of {@link glyphLabel}: that answers where the one tier GOES,
301
+ * this answers what it IS, and the two questions have had the same shape since
302
+ * the lifeline arrived — a placement chosen per kind and a role chosen per
303
+ * kind, which is exactly one table each.
304
+ *
305
+ * Today it says one thing: a lifeline's head carries `uml:lifeline-ident` and
306
+ * everything else carries `uml:label`. §17.3.4 prints a BNF for what goes in
307
+ * that head (`parseLifelineIdent` reads it back) and §18.1.4 prints none for an
308
+ * actor's word, so the two tiers answer "does this parse" differently and a
309
+ * single role could only ever have asked them the same question (`roles.ts`).
310
+ *
311
+ * It does NOT answer for the compartmented kinds or for the five glyphs named
312
+ * rather than labelled (`actions.ts`, `NAMED_GLYPHS`): those carry `uml:name`,
313
+ * which is a different statement — a tier a keyword may be written over — and a
314
+ * helper that folded all three answers together would be a second place to
315
+ * decide what the grammar reads.
316
+ */
317
+ export declare function umlLabelRoleOf(kind: UmlNodeKind): UmlRoleId;
318
+ /**
319
+ * The compartments of a node, laid out against its own box.
320
+ *
321
+ * ## The rhythm of a classifier
322
+ *
323
+ * A margin, the name over one line, a small gap, THE LINE, three lines of
324
+ * attributes, THE SECOND LINE, and the operations take the rest down to the
325
+ * bottom margin. Which is exactly what a 200 × 120 box is tall enough for,
326
+ * because the box was derived from this stack (`consts.ts`) rather than the
327
+ * stack fitted into the box — so a fresh class is neither cramped nor half
328
+ * empty.
329
+ *
330
+ * The tiers are stacked by walking DOWN, each placed under the last, rather
331
+ * than by six independent offsets: six offsets is six chances for two
332
+ * compartments to overlap, and a walk cannot produce one.
333
+ *
334
+ * An `object` walks the same path and stops one tier early: §9.8.4 draws an
335
+ * instance specification as a name compartment over a SLOT compartment, and
336
+ * there is no third. So it gets one split, not two, and the slots take the rest
337
+ * of the box — the tier an author of an object diagram actually types in. A
338
+ * `component` (§11.6.4) and an `artifact` (§19.3.4) walk exactly the same path,
339
+ * for exactly the same reason: a name over one body tier.
340
+ *
341
+ * ## The ones that are not boxes
342
+ *
343
+ * A package, a note, an actor, a use case, the three cubes and the three
344
+ * interface glyphs are SHAPES the notation draws, not rectangles it divides, so
345
+ * each returns a single label box positioned against its own picture and no
346
+ * splits at all:
347
+ *
348
+ * - **package** — under the tab, centred in the body (§12.2.4);
349
+ * - **note** — the inner rectangle, top-aligned and read left, because a note
350
+ * holds a sentence rather than a title (Annex A);
351
+ * - **actor** — under the stick figure, which is where §18.1.4 writes it;
352
+ * - **use case** — centred in the ellipse, the only shape with no corner to
353
+ * hang a label in;
354
+ * - **node / device / execution-environment** — inside the cube's FRONT FACE,
355
+ * the only one of its three faces that is not drawn at an angle (§19.4.4);
356
+ * - **port / provided-interface / required-interface / time-event** — BESIDE
357
+ * the glyph, and outside the element's own box: a small square, a ball on a
358
+ * stick and a 40-unit hourglass have no inside to write in (§11.3.4,
359
+ * §10.4.4, §16.10.4). The one place this module returns a tier that is not
360
+ * contained by the node it belongs to, and
361
+ * {@link UML_BESIDE_LABEL_KINDS} is how it says so out loud;
362
+ * - **action / object-node** — centred in the box, which is where §15.3.4 and
363
+ * §15.4.4 write a verb phrase and a value's name;
364
+ * - **send-signal / accept-event** — centred too, but pulled back out of the
365
+ * pentagon's POINT by {@link UML_SIGNAL_POINT}, so the words never run out
366
+ * through the tip (§16.3.4).
367
+ *
368
+ * ## The ones with no words at all
369
+ *
370
+ * The control nodes of §15.3.4 and the pseudostates of §14.2.4 are MARKS — a
371
+ * disc, a bar, a bullseye, a cross — and carry no tier. They still get a box
372
+ * back from here, centred, because a box is always computable from a box and a
373
+ * second return shape would cost every caller a branch. What decides whether a
374
+ * text element is ever created is `UML_UNLABELLED_KINDS` in `keywords.ts`, and
375
+ * that is the one authority on the question.
376
+ *
377
+ * ## What this is not
378
+ *
379
+ * A creation-time answer, a render-time one and a commit-time one, and nothing
380
+ * more. The compartments are real elements from the moment they are drawn: an
381
+ * author who moves one has moved it, and nothing here runs on its own to put it
382
+ * back. The margins are absolutes, so a node dragged taller keeps them where
383
+ * they were and gives the extra height to the tier that takes "the rest".
384
+ *
385
+ * The commit-time caller is `UmlCompartmentWatcher` (`node/`), and it is the
386
+ * one that passes `lines`: when an edit into a tier commits, it re-asks this
387
+ * function where the compartments go now that the second one holds five lines
388
+ * instead of three, grows the node if the answer no longer fits
389
+ * ({@link umlStackHeight}) and writes the tiers to the boxes it gets back. It
390
+ * is still not a layout engine — nothing runs between keystrokes, and nothing
391
+ * reclaims height an author has dragged out.
392
+ */
393
+ export declare function umlCompartmentBoxes(kind: UmlNodeKind, x: number, y: number, w: number, h: number, lines?: UmlTierLines): UmlCompartmentBoxes;
394
+ /**
395
+ * How tall the node has to BE for the words its tiers hold to fit — `null` for
396
+ * a kind that has no stack.
397
+ *
398
+ * The same walk as {@link umlCompartmentBoxes}, read the other way round: that
399
+ * function is given a box and hands back where the compartments go, this one is
400
+ * given the compartments and hands back the box they need. Both are here so
401
+ * they cannot disagree, which is the whole reason the separators were computed
402
+ * in this module in the first place.
403
+ *
404
+ * The last tier is the one difference between the two. `umlCompartmentBoxes`
405
+ * gives the operations compartment (or a one-split kind's body) EVERYTHING left
406
+ * down to the bottom margin, because a box an author has dragged taller has
407
+ * dragged it taller for them; this returns the height at which "everything
408
+ * left" is exactly the lines it holds, which is the smallest box that fits.
409
+ *
410
+ * `null` rather than a number for a package, an actor, a cube or a mark: those
411
+ * are pictures the notation draws, not rectangles it divides, and there is no
412
+ * honest answer to "how tall must this be" for a stick figure. It doubles as
413
+ * the predicate — a caller that gets a number is looking at a divided box.
414
+ */
415
+ export declare function umlStackHeight(kind: UmlNodeKind, lines: UmlTierLines): number | null;
416
+ /**
417
+ * The little a resolution needs to know about an element — an id, a role, and
418
+ * whatever it says.
419
+ *
420
+ * `text` is `unknown` because on a real element it is a `Y.Text` and in a test
421
+ * it is a string, and this module has no business knowing which: it is read
422
+ * through {@link umlTierText}, which stringifies whatever it is given. That is
423
+ * also what keeps this file free of a Yjs import.
424
+ */
425
+ export interface UmlComponentElement {
426
+ id: string;
427
+ role?: string;
428
+ text?: unknown;
429
+ }
430
+ /** The little a resolution needs to know about a group. */
431
+ export interface UmlComponentGroup {
432
+ id: string;
433
+ childIds: readonly string[];
434
+ }
435
+ /**
436
+ * One classifier's shape and its written compartments, resolved.
437
+ *
438
+ * Every field is optional, and that is the honest answer rather than a
439
+ * defensive one: a group whose texts were deleted, one whose shape was deleted,
440
+ * and a group an author made out of two unrelated boxes all exist, and none of
441
+ * them is an error. What a caller does with a missing tier — write nothing,
442
+ * fall back to the shape's own inner text — is the caller's decision, and the
443
+ * exporter and the node view make different ones.
444
+ */
445
+ export interface UmlComponent {
446
+ node?: UmlComponentElement;
447
+ name?: UmlComponentElement;
448
+ attributes?: UmlComponentElement;
449
+ operations?: UmlComponentElement;
450
+ label?: UmlComponentElement;
451
+ /**
452
+ * §17.3.4's lifeline head — a fifth slot rather than a fifth user of
453
+ * {@link UmlComponent.label}, for the reason `roles.ts` splits the tier: what
454
+ * is written here is a production with a grammar, and what is written in a
455
+ * label is prose. A caller that wants "the word this artefact goes by",
456
+ * whatever tier holds it, asks for `name ?? label ?? ident` — three tiers,
457
+ * never both.
458
+ */
459
+ ident?: UmlComponentElement;
460
+ }
461
+ /**
462
+ * Everything one group holds, sorted into the shape and its tiers.
463
+ *
464
+ * ## Group membership, then roles
465
+ *
466
+ * The group answers "which words are THIS classifier's" — two classes side by
467
+ * side both carry a `+ attribute : Type`, and only the grouping says which is
468
+ * which. The role then answers "which of these words is the operations
469
+ * compartment", which position in `children` cannot: a group's child order is
470
+ * an implementation detail that a reorder, a copy or a regroup rewrites, while
471
+ * a role is written on the element and travels with it.
472
+ *
473
+ * ## `name` and `label` are two fields, not one
474
+ *
475
+ * Because they are two roles, and the vocabulary means them differently: a
476
+ * `uml:name` is the name of a CLASSIFIER, parsed by the grammar for keywords in
477
+ * guillemets and for an abstract name in italics (§9.5.4); a `uml:label` is the
478
+ * plain text of an actor or a use case, which is a name and nothing else. An
479
+ * exporter that had to ask "is this the name field or the label field" of one
480
+ * merged slot would be asking about the element's KIND, which the shape already
481
+ * states. Keeping them apart costs one field and removes the question.
482
+ *
483
+ * The FIRST element of each role wins. A group holding two names is a group
484
+ * somebody built by hand out of two components, and document order is the same
485
+ * tie-break the export's geometric attribution uses.
486
+ */
487
+ export declare function umlComponentSiblings(group: UmlComponentGroup, elements: readonly UmlComponentElement[]): UmlComponent;
488
+ /**
489
+ * The group an element belongs to — `undefined` when it belongs to none.
490
+ *
491
+ * The other direction of the same question, and the one the node view needs:
492
+ * given the shape somebody has just double-clicked, which group holds the text
493
+ * to open an editor on? The C4 pack answers it with `c4ComponentSiblings(id,
494
+ * groups)`; this returns the group itself rather than its id list, because the
495
+ * next call is always {@link umlComponentSiblings}, which takes exactly that.
496
+ *
497
+ * The FIRST group holding the element wins. Groups nest, so a component grouped
498
+ * again inside a bigger group has two ancestors; document order picks the one
499
+ * written first, which is the innermost — the one the creation site made.
500
+ */
501
+ export declare function umlGroupOf(elementId: string, groups: readonly UmlComponentGroup[]): UmlComponentGroup | undefined;
502
+ /**
503
+ * The shape of a resolved component — its `umlNode`, or `undefined` for a group
504
+ * that holds none.
505
+ *
506
+ * A one-line accessor with a name, rather than `component.node` at eleven call
507
+ * sites, because the QUESTION is worth spelling: "which box are these words
508
+ * about". The exporter asks it to read a `kind`, the renderer to read a
509
+ * geometry, and a rule to read a role.
510
+ */
511
+ export declare function umlNodeOfComponent(component: UmlComponent): UmlComponentElement | undefined;
512
+ /**
513
+ * Whatever a tier says, as a plain trimmed string.
514
+ *
515
+ * Takes the VALUE rather than the element, because that is what every caller
516
+ * has: a `Y.Text` off a model, a string off a fixture, `undefined` off a tier
517
+ * that was never created. Stringifying here is what lets the grammar, the
518
+ * exporter and the renderer stay free of a Yjs import — a `Y.Text` answers
519
+ * `toString()` with its content, which is the whole of the coupling.
520
+ */
521
+ export declare function umlTierText(text: unknown): string;