@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,557 @@
1
+ import { UML_CARD, UML_DIAGRAM_BOX, UML_DIAGRAM_MARGIN, UML_DIVIDER, UML_FONT_FAMILY, UML_FRAGMENT_BAND, UML_FRAGMENT_BORDER_WIDTH, UML_FRAGMENT_BOX, UML_FRAGMENT_GUARD_FONT_SIZE, UML_FRAGMENT_GUARD_INSET, UML_FRAGMENT_MARGIN, UML_FRAGMENT_OPERAND_DASH, UML_FRAME_BAND_HEIGHT, UML_FRAME_BORDER_WIDTH, UML_FRAME_HEADING_FONT_SIZE, UML_FRAME_INK, UML_NAME_FONT_SIZE, UML_PARTITION_BAND, UML_PARTITION_BORDER_WIDTH, UML_PARTITION_BOX, UML_PARTITION_MARGIN, UML_REGION_BAND, UML_REGION_BORDER_WIDTH, UML_REGION_BOX, UML_REGION_MARGIN, UML_REGION_RADIUS, UML_SUBJECT_BORDER_WIDTH, UML_SUBJECT_BOX, UML_SUBJECT_MARGIN, } from './consts.js';
2
+ import { UML_ROLE } from './roles.js';
3
+ /**
4
+ * The four UML frames, DECLARED (the `FrameworkBackgroundDef` primitive): the
5
+ * diagram sheet of Annex A, the use case subject of §18.1.4, the activity
6
+ * partition of §15.6.4 (in its two orientations) and the composite state of
7
+ * §14.2.4.
8
+ *
9
+ * There is no UML drawing code for either of them: the primitive paints these
10
+ * declarations, and would paint any other framework's the same way
11
+ * (`docs/adr/0009` on why a framework declares rather than draws). The ONE thing
12
+ * UML asks for that the primitive has no vocabulary for — the cut-corner heading
13
+ * tag of Annex A — is a decorator over the renderer these produce, and it lives
14
+ * in `frame-tag.ts` rather than in a new field of the declaration language.
15
+ */
16
+ /* ── The diagram frame ─────────────────────────────────────────────────── */
17
+ /**
18
+ * The UML diagram frame: a white card with a square border and a heading band
19
+ * across its top.
20
+ *
21
+ * **No axes and no zones**, like the C4 board this is modelled on and for the
22
+ * same reason: a UML diagram is a GRAPH. A class drawn top left says nothing
23
+ * more than one drawn bottom right, and graduating the card would invent a frame
24
+ * of reference UML does not have — and then judge people against it.
25
+ *
26
+ * ## The heading is `heading`, not `name` — and that is the whole difference
27
+ *
28
+ * A C4 board writes its title, which is free text. Annex A writes `<kind>
29
+ * <name>`: a class diagram called Orders reads `class Orders`, and only the
30
+ * second word is the author's. So the label binds the model's DERIVED
31
+ * {@link UmlDiagramElementModel.heading} — the two halves joined — and the
32
+ * rename gesture writes `name` alone (`element-view.ts`). Binding `heading`
33
+ * directly would let an editor write the joined string back into a getter, which
34
+ * would either throw or, worse, re-prefix the kind on every rename.
35
+ *
36
+ * ## Why the band paints NOTHING
37
+ *
38
+ * It declares no fill and no divider, which is not an oversight: Annex A draws a
39
+ * frame as one rectangle with a tag in its corner, and a rule ruled across the
40
+ * sheet under the heading would be a line the notation does not have. The band
41
+ * exists to place the words and to reserve the top margin; what a reader SEES
42
+ * there is the tag, and what a user AIMS at is the tag — which is the affordance
43
+ * the C4 board needed its painted strip for.
44
+ *
45
+ * The model's own carve-out (`UmlDiagramElementModel.includesPoint`) still picks
46
+ * the whole band, so a click anywhere along the top margin selects the sheet
47
+ * rather than falling through to the canvas. That is deliberately WIDER than
48
+ * what is painted, and it is the one place the two disagree: the alternative is
49
+ * a frame whose corner tag is the only selectable part of a 44-unit strip that
50
+ * is plainly part of the sheet.
51
+ */
52
+ export const UML_DIAGRAM_FRAME = {
53
+ type: 'umlDiagram',
54
+ role: UML_ROLE.diagram,
55
+ geometry: {
56
+ // Wide and free: a diagram grows sideways as the model is discovered, so
57
+ // neither dimension is locked to the other and the handles are offered from
58
+ // the start.
59
+ width: UML_DIAGRAM_BOX.w,
60
+ height: UML_DIAGRAM_BOX.h,
61
+ lockAspectRatio: false,
62
+ resizable: true,
63
+ // Only the top margin is deep: it IS the heading band, and the band's height
64
+ // is the margin it covers rather than a second number beside it. Declared in
65
+ // `@labre/affine-model` because the frame's hit test reads it too.
66
+ margin: {
67
+ top: UML_FRAME_BAND_HEIGHT,
68
+ right: UML_DIAGRAM_MARGIN,
69
+ bottom: UML_DIAGRAM_MARGIN,
70
+ left: UML_DIAGRAM_MARGIN,
71
+ },
72
+ },
73
+ chrome: {
74
+ fontFamily: UML_FONT_FAMILY,
75
+ palette: {
76
+ card: UML_CARD,
77
+ frame: UML_FRAME_INK,
78
+ },
79
+ surface: {
80
+ fill: '@card',
81
+ border: {
82
+ color: '@frame',
83
+ width: UML_FRAME_BORDER_WIDTH,
84
+ // Square: Annex A's frame is a plain rectangle, and a rounded one would
85
+ // read as a note or a card rather than as the sheet's own edge.
86
+ radius: 0,
87
+ },
88
+ },
89
+ sideBands: [
90
+ {
91
+ // No `fill` and no `divider` — see the note above. The band is where the
92
+ // heading goes, and the tag drawn round it is what a reader sees.
93
+ side: 'top',
94
+ label: {
95
+ id: 'heading',
96
+ // The DERIVED `<kind> <name>`, so the frame says what it is before it
97
+ // says what it is called. The rename writes `name`; see the view.
98
+ prop: 'heading',
99
+ // `y: 0` is the top of the plot, i.e. the band's INNER edge; a
100
+ // negative `dy` walks back UP into the band, where the words go. A
101
+ // third of the band leaves the baseline low enough for the tag drawn
102
+ // round it to clear the descenders.
103
+ anchor: { x: 0, y: 0, dy: -UML_FRAME_BAND_HEIGHT / 3 },
104
+ style: {
105
+ size: UML_FRAME_HEADING_FONT_SIZE,
106
+ weight: 600,
107
+ color: '@frame',
108
+ },
109
+ },
110
+ },
111
+ ],
112
+ },
113
+ };
114
+ /* ── The subject ───────────────────────────────────────────────────────── */
115
+ /**
116
+ * The use case SUBJECT (§18.1.4): a plain rectangle with its name written inside
117
+ * the top-left corner, drawn round the use cases a system offers.
118
+ *
119
+ * TRANSPARENT, like the C4 boundary and for the same reason: it is drawn OVER a
120
+ * diagram, round elements that are already there, and an opaque card would hide
121
+ * the very thing it is pointing at. So it declares a border and no fill, which
122
+ * the primitive paints as an unfilled frame — and the consequence is the reverse
123
+ * of the usual one: a subject dropped over existing use cases does NOT cover
124
+ * them, and is hit nowhere but on its own frame band and its name.
125
+ *
126
+ * SOLID, unlike that boundary, and this is where the two notations part company:
127
+ * C4's stencil dashes its boundary, §18.1.4 draws one unbroken rectangle. A dash
128
+ * here would say "logical grouping" in a notation where a subject is a
129
+ * SYSTEM — the thing whose behaviour the use cases are, with the actors
130
+ * deliberately outside it.
131
+ *
132
+ * The name is written TOP-left rather than the boundary's bottom-left: that is
133
+ * where the specification's figures put it, and it is also the corner a use case
134
+ * ellipse is least likely to reach into, ellipses having no corners of their own.
135
+ *
136
+ * No variant and no second tier: §18.1.4 gives the subject one rectangle and one
137
+ * name — no dashed flavour, no keyword line — so there is nothing here for a
138
+ * variant to select between.
139
+ */
140
+ export const UML_SUBJECT_FRAME = {
141
+ type: 'umlSubject',
142
+ role: UML_ROLE.subject,
143
+ geometry: {
144
+ width: UML_SUBJECT_BOX.w,
145
+ height: UML_SUBJECT_BOX.h,
146
+ // A subject is stretched round whatever it has been drawn about, which is
147
+ // never the same shape twice.
148
+ lockAspectRatio: false,
149
+ resizable: true,
150
+ margin: {
151
+ top: UML_SUBJECT_MARGIN,
152
+ right: UML_SUBJECT_MARGIN,
153
+ bottom: UML_SUBJECT_MARGIN,
154
+ left: UML_SUBJECT_MARGIN,
155
+ },
156
+ },
157
+ zones: [
158
+ {
159
+ id: 'name',
160
+ rect: { x: 0, y: 0, w: 1, h: 1 },
161
+ label: {
162
+ id: 'name',
163
+ // The author's own words, and only those: a subject is named by whoever
164
+ // draws it, so there is no vocabulary to fall back to.
165
+ prop: 'name',
166
+ // Top-left INSIDE the plot. The anchor is a BASELINE, so `dy` is one
167
+ // line down from the plot's top edge — written at the size the name
168
+ // compartment of a classifier uses, because it is the same kind of word.
169
+ anchor: { x: 0, y: 0, dy: UML_NAME_FONT_SIZE },
170
+ style: {
171
+ size: UML_NAME_FONT_SIZE,
172
+ weight: 600,
173
+ color: '@frame',
174
+ },
175
+ },
176
+ },
177
+ ],
178
+ chrome: {
179
+ fontFamily: UML_FONT_FAMILY,
180
+ palette: {
181
+ frame: UML_FRAME_INK,
182
+ },
183
+ surface: {
184
+ // NO fill — see the note above. This is the transparent one.
185
+ border: {
186
+ color: '@frame',
187
+ width: UML_SUBJECT_BORDER_WIDTH,
188
+ radius: 0,
189
+ },
190
+ },
191
+ },
192
+ };
193
+ /* ── The partition ─────────────────────────────────────────────────────── */
194
+ /**
195
+ * The activity PARTITION (§15.6.4): the swimlane drawn across an activity
196
+ * diagram saying who is responsible for the actions inside it.
197
+ *
198
+ * TRANSPARENT, like the subject and for exactly the same reason: it is drawn
199
+ * over a flow that is already there, and an opaque card would hide the very
200
+ * actions it is attributing. Membership is geometry at read time (R11) — an
201
+ * action belongs to the lane its centre is in.
202
+ *
203
+ * ## Two declarations rather than one with a variant
204
+ *
205
+ * `FrameworkBackgroundDef.variantProp` exists and would look like the answer,
206
+ * but it does not reach the two things an orientation has to change here. A
207
+ * variant selects which WASHES and which ZONES are painted (`BackgroundWashDef
208
+ * .variants`, `BackgroundZoneDef.variants`); a side band declares no variants
209
+ * at all, and the band's THICKNESS is the geometry's margin on that side, which
210
+ * is a single fixed inset per declaration. So a vertical lane and a horizontal
211
+ * one differ in two fields the variant mechanism cannot vary —
212
+ * `sideBands[0].side` and which of `margin.top` / `margin.left` is deep — and
213
+ * the honest way to say that is two declarations, picked in the renderer by the
214
+ * element's own `orientation`.
215
+ *
216
+ * The alternative — teaching `variants` to side bands and margins — is a change
217
+ * to the declaration LANGUAGE for one framework's convenience, and the language
218
+ * grows when a second framework asks (`BackgroundSideBandDef`'s own note on why
219
+ * it offers only `left` and `top`).
220
+ *
221
+ * ## The band is painted, unlike the diagram frame's
222
+ *
223
+ * The sheet's heading band paints nothing because Annex A draws a tag in the
224
+ * corner and no rule across the page. A swimlane's header is the opposite:
225
+ * §15.6.4 draws it as a titled strip ruled off from the lane below it, and that
226
+ * rule is what makes a column of actions read as belonging to somebody. So both
227
+ * declarations below carry a `divider` — the lighter of the two frame inks, so
228
+ * the strip reads as part of the same lane rather than as a second frame drawn
229
+ * inside the first. Still no `fill`: a tint would be a colour code UML does not
230
+ * have (R33), and it would tint whatever the lane is drawn over.
231
+ */
232
+ function partitionFrame(side, margin) {
233
+ return {
234
+ type: 'umlPartition',
235
+ role: UML_ROLE.partition,
236
+ geometry: {
237
+ width: UML_PARTITION_BOX.w,
238
+ height: UML_PARTITION_BOX.h,
239
+ // A lane is stretched round whatever it has been drawn about, in
240
+ // whichever direction the flow runs.
241
+ lockAspectRatio: false,
242
+ resizable: true,
243
+ margin,
244
+ },
245
+ chrome: {
246
+ fontFamily: UML_FONT_FAMILY,
247
+ palette: {
248
+ frame: UML_FRAME_INK,
249
+ divider: UML_DIVIDER,
250
+ },
251
+ surface: {
252
+ // NO fill: see the header. This is a transparent frame.
253
+ border: {
254
+ color: '@frame',
255
+ width: UML_PARTITION_BORDER_WIDTH,
256
+ // Square: §15.6.4 draws a partition as a plain rectangle, and a
257
+ // rounded one would read as a composite state.
258
+ radius: 0,
259
+ },
260
+ },
261
+ sideBands: [
262
+ {
263
+ side,
264
+ divider: { color: '@divider', width: UML_PARTITION_BORDER_WIDTH },
265
+ label: {
266
+ id: 'name',
267
+ // The author's own words: a lane is named after whoever owns it,
268
+ // so there is no vocabulary to fall back to.
269
+ prop: 'name',
270
+ // The plot's top-left corner is the band's INNER corner, so a
271
+ // negative delta walks back into the band, where the words go. A
272
+ // third of the band leaves the baseline clear of the descenders —
273
+ // the diagram frame's own arithmetic, on whichever edge the band
274
+ // is. The horizontal lane also needs a `dy`, because its anchor is
275
+ // a BASELINE and the plot's top is the top of the words.
276
+ anchor: side === 'top'
277
+ ? { x: 0, y: 0, dy: -UML_PARTITION_BAND / 3 }
278
+ : {
279
+ x: 0,
280
+ y: 0,
281
+ dx: -UML_PARTITION_BAND + UML_PARTITION_BAND / 4,
282
+ dy: UML_NAME_FONT_SIZE,
283
+ },
284
+ style: {
285
+ size: UML_NAME_FONT_SIZE,
286
+ weight: 600,
287
+ color: '@frame',
288
+ },
289
+ },
290
+ },
291
+ ],
292
+ },
293
+ };
294
+ }
295
+ /** A VERTICAL partition — a column, its name written across the top. */
296
+ export const UML_PARTITION_FRAME_V = partitionFrame('top', {
297
+ top: UML_PARTITION_BAND,
298
+ right: UML_PARTITION_MARGIN,
299
+ bottom: UML_PARTITION_MARGIN,
300
+ left: UML_PARTITION_MARGIN,
301
+ });
302
+ /** A HORIZONTAL partition — a row, its name written down the left edge. */
303
+ export const UML_PARTITION_FRAME_H = partitionFrame('left', {
304
+ top: UML_PARTITION_MARGIN,
305
+ right: UML_PARTITION_MARGIN,
306
+ bottom: UML_PARTITION_MARGIN,
307
+ left: UML_PARTITION_BAND,
308
+ });
309
+ /**
310
+ * The declaration a partition element is painted and hit-tested by — the one
311
+ * place the `orientation` prop is turned into a picture.
312
+ *
313
+ * A function rather than a lookup repeated at each call site, so the renderer,
314
+ * the view and the band helpers cannot disagree about which of the two a lane
315
+ * is: all three ask this.
316
+ *
317
+ * An orientation this build has never heard of falls back to the VERTICAL
318
+ * declaration rather than painting nothing — the same promise
319
+ * `UmlDiagramElementModel.heading` makes about an unknown kind: a document
320
+ * written by a newer build still draws a lane.
321
+ */
322
+ export function umlPartitionFrame(model) {
323
+ return model.orientation === 'horizontal'
324
+ ? UML_PARTITION_FRAME_H
325
+ : UML_PARTITION_FRAME_V;
326
+ }
327
+ /* ── The composite state ───────────────────────────────────────────────── */
328
+ /**
329
+ * The COMPOSITE STATE (§14.2.4): the round-cornered rectangle drawn round the
330
+ * sub-states of a state that has a machine of its own inside it.
331
+ *
332
+ * Transparent, band on the top, and ROUNDED — which is the whole point of it.
333
+ * §14.2.4 draws every state with rounded corners, simple or composite, and a
334
+ * square frame round a sub-machine would read as a partition or as a subject.
335
+ * `surface.border.radius` is in MODEL UNITS here (the primitive draws the card
336
+ * in the units the element is), unlike the node preset's fractional `radius`,
337
+ * so the number comes from `UML_REGION_RADIUS` rather than from
338
+ * `UML_NODE_RADIUS`.
339
+ *
340
+ * ## One region, and the ceiling stated
341
+ *
342
+ * ORTHOGONAL regions — a composite state cut into concurrent regions by dashed
343
+ * separators (§14.2.4, Figure 14.8) — are not drawn. That is an INSTANCE
344
+ * PARTITION in the declaration language (`instanceZones`, the mechanism the
345
+ * BPMN pool's lanes use), and it arrives the day the notation is asked for
346
+ * rather than as a field added here in a hurry. See `UmlRegionElementModel`.
347
+ */
348
+ export const UML_REGION_FRAME = {
349
+ type: 'umlRegion',
350
+ role: UML_ROLE.region,
351
+ geometry: {
352
+ width: UML_REGION_BOX.w,
353
+ height: UML_REGION_BOX.h,
354
+ lockAspectRatio: false,
355
+ resizable: true,
356
+ margin: {
357
+ top: UML_REGION_BAND,
358
+ right: UML_REGION_MARGIN,
359
+ bottom: UML_REGION_MARGIN,
360
+ left: UML_REGION_MARGIN,
361
+ },
362
+ },
363
+ chrome: {
364
+ fontFamily: UML_FONT_FAMILY,
365
+ palette: {
366
+ frame: UML_FRAME_INK,
367
+ divider: UML_DIVIDER,
368
+ },
369
+ surface: {
370
+ // NO fill: a composite state is drawn over the sub-machine it holds.
371
+ border: {
372
+ color: '@frame',
373
+ width: UML_REGION_BORDER_WIDTH,
374
+ radius: UML_REGION_RADIUS,
375
+ },
376
+ },
377
+ sideBands: [
378
+ {
379
+ side: 'top',
380
+ // The rule under the name is §14.2.4's own: a composite state's name
381
+ // compartment is ruled off from the region below it, exactly as a
382
+ // simple state's name is ruled off from its internal activities.
383
+ divider: { color: '@divider', width: UML_REGION_BORDER_WIDTH },
384
+ label: {
385
+ id: 'name',
386
+ prop: 'name',
387
+ anchor: { x: 0, y: 0, dy: -UML_REGION_BAND / 3 },
388
+ style: {
389
+ size: UML_NAME_FONT_SIZE,
390
+ weight: 600,
391
+ color: '@frame',
392
+ },
393
+ },
394
+ },
395
+ ],
396
+ },
397
+ };
398
+ /* ── The combined fragment ─────────────────────────────────────────────── */
399
+ /**
400
+ * The COMBINED FRAGMENT (§17.6.4): the rectangle drawn round part of a
401
+ * conversation with `alt`, `loop` or `opt` in its corner — and, under the `ref`
402
+ * operator, §17.7.4's InteractionUse.
403
+ *
404
+ * TRANSPARENT, like the subject and the partition and for exactly the same
405
+ * reason: it is drawn over lifelines and messages that are already there, and
406
+ * an opaque card would hide the conversation it is qualifying. SOLID border,
407
+ * because §17.6.4 draws one unbroken rectangle — the only dashed lines in the
408
+ * picture are the ones BETWEEN the operands.
409
+ *
410
+ * ## The band paints nothing, like the diagram frame's
411
+ *
412
+ * And for the same reason: §17.6.4 draws the operator in Annex A's own
413
+ * cut-corner pentagon, and a rule ruled right across the fragment under it
414
+ * would be a line the notation does not have. The band exists to place the word
415
+ * and to reserve the top margin; what a reader sees is the pentagon, drawn by
416
+ * `withUmlFrameTag` over this declaration's own label — which is why the label
417
+ * binds `operator` and why the tag carries no words of its own.
418
+ *
419
+ * ## The guard, and the one contract the operands keep
420
+ *
421
+ * `name` is the guard of the fragment's FIRST operand — `[x > 0]`, `[else]` —
422
+ * or, under `ref`, the name of the interaction referred to. It is written in
423
+ * the plot's top-left corner, which is where §17.6.4 puts an operand's guard,
424
+ * and it is a declared ZONE label so the frame view's rename gesture reaches
425
+ * it exactly as it reaches the subject's name.
426
+ *
427
+ * Once the fragment is SPLIT, the guards live in `operands` and nowhere else:
428
+ * the instance-zone label below writes each operand's own, and the first of
429
+ * them occupies the very corner `name` is written in. The contract the "add
430
+ * operand" action keeps (`actions.ts`) is therefore that splitting a fragment
431
+ * MOVES `name` into `operands[0].name` and clears it — one guard, one place,
432
+ * and no corner with two strings in it.
433
+ */
434
+ export const UML_FRAGMENT_FRAME = {
435
+ type: 'umlFragment',
436
+ role: UML_ROLE.fragment,
437
+ geometry: {
438
+ width: UML_FRAGMENT_BOX.w,
439
+ height: UML_FRAGMENT_BOX.h,
440
+ // Stretched round whatever part of the conversation it has been drawn
441
+ // about, which is never the same shape twice.
442
+ lockAspectRatio: false,
443
+ resizable: true,
444
+ // Only the top margin is deep: it IS the operator band, the diagram
445
+ // frame's own arrangement.
446
+ margin: {
447
+ top: UML_FRAGMENT_BAND,
448
+ right: UML_FRAGMENT_MARGIN,
449
+ bottom: UML_FRAGMENT_MARGIN,
450
+ left: UML_FRAGMENT_MARGIN,
451
+ },
452
+ },
453
+ zones: [
454
+ {
455
+ id: 'guard',
456
+ rect: { x: 0, y: 0, w: 1, h: 1 },
457
+ label: {
458
+ id: 'guard',
459
+ // The author's own words, and only those — there is no vocabulary to
460
+ // fall back to for a condition somebody has not written yet.
461
+ prop: 'name',
462
+ // Top-left INSIDE the plot, one line down: the anchor is a BASELINE,
463
+ // and §17.6.4 writes a guard in the operand's own corner.
464
+ anchor: {
465
+ x: 0,
466
+ y: 0,
467
+ dx: UML_FRAGMENT_GUARD_INSET,
468
+ dy: UML_FRAGMENT_GUARD_FONT_SIZE + UML_FRAGMENT_GUARD_INSET,
469
+ },
470
+ style: {
471
+ size: UML_FRAGMENT_GUARD_FONT_SIZE,
472
+ weight: 600,
473
+ color: '@frame',
474
+ },
475
+ },
476
+ },
477
+ ],
478
+ /**
479
+ * The OPERANDS — the bands §17.6.4 cuts an `alt` or a `par` into, separated
480
+ * by dashed lines.
481
+ *
482
+ * The BPMN pool's lanes, to the letter: an instance partition on the
483
+ * declaration, weights rather than heights so a fragment dragged taller
484
+ * redistributes instead of leaving a gap, and the CORNER placement for the
485
+ * guard because that is where §17.6.4 writes one. A pool's lanes take the
486
+ * band placement instead, and the difference is the notation's: a lane name
487
+ * is a title written down a strip, an operand's guard is a condition written
488
+ * in a corner over the messages it governs.
489
+ */
490
+ instanceZones: {
491
+ prop: 'operands',
492
+ // Horizontal bands, top to bottom in array order: time runs down a
493
+ // sequence diagram, and an operand is a stretch of it.
494
+ stack: 'y',
495
+ // A namespace, so an operand a user guards `else` can never shadow the
496
+ // declared `guard` zone above.
497
+ idPrefix: 'operand',
498
+ // DASHED, and the one dashed line in the fragment: §17.6.4 rules its
499
+ // operands off with a broken line precisely so the rectangle round them
500
+ // stays a single unbroken frame.
501
+ divider: {
502
+ color: '@divider',
503
+ width: UML_FRAGMENT_BORDER_WIDTH,
504
+ dash: [...UML_FRAGMENT_OPERAND_DASH],
505
+ },
506
+ label: {
507
+ // The corner placement — no `band`. See the note above.
508
+ dx: UML_FRAGMENT_GUARD_INSET,
509
+ dy: UML_FRAGMENT_GUARD_FONT_SIZE + UML_FRAGMENT_GUARD_INSET,
510
+ style: {
511
+ size: UML_FRAGMENT_GUARD_FONT_SIZE,
512
+ weight: 600,
513
+ color: '@frame',
514
+ },
515
+ },
516
+ },
517
+ chrome: {
518
+ fontFamily: UML_FONT_FAMILY,
519
+ palette: {
520
+ frame: UML_FRAME_INK,
521
+ divider: UML_DIVIDER,
522
+ },
523
+ surface: {
524
+ // NO fill — this is the transparent one.
525
+ border: {
526
+ color: '@frame',
527
+ width: UML_FRAGMENT_BORDER_WIDTH,
528
+ // Square: §17.6.4 draws a plain rectangle, and a rounded one would
529
+ // read as a composite state.
530
+ radius: 0,
531
+ },
532
+ },
533
+ sideBands: [
534
+ {
535
+ // No `fill` and no `divider`, exactly as the diagram frame's heading
536
+ // band declares none: what a reader sees is the pentagon.
537
+ side: 'top',
538
+ label: {
539
+ id: 'operator',
540
+ // The word itself — `alt`, `loop`, `ref`. NOT a derived heading: a
541
+ // fragment has one word in its tag, and the guard is written in the
542
+ // operand rather than beside the operator.
543
+ prop: 'operator',
544
+ // `y: 0` is the band's INNER edge; a negative `dy` walks back up into
545
+ // it. A third of the band clears the descenders, which is the
546
+ // diagram frame's own arithmetic.
547
+ anchor: { x: 0, y: 0, dy: -UML_FRAGMENT_BAND / 3 },
548
+ style: {
549
+ size: UML_FRAME_HEADING_FONT_SIZE,
550
+ weight: 600,
551
+ color: '@frame',
552
+ },
553
+ },
554
+ },
555
+ ],
556
+ },
557
+ };