@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
package/dist/consts.js ADDED
@@ -0,0 +1,463 @@
1
+ // The lifeline's column height is a MODEL constant (the element's own bound
2
+ // override reads it), and it is also this file's footprint for the kind — so it
3
+ // is imported as well as re-exported below.
4
+ import { UML_LIFELINE_SPINE } from '@formicoidea/labre-core/model';
5
+ import { NOTATION_NEUTRALS } from '@formicoidea/labre-core/shared/consts';
6
+ /**
7
+ * Visual constants for the UML pack.
8
+ *
9
+ * UML is the opposite of C4 on the one question that matters here: C4's stencil
10
+ * IS a colour code — four blues for four levels — whereas UML 2.5.1 defines no
11
+ * palette at all. Every figure in the specification is black lines on white
12
+ * paper, and what distinguishes a class from an interface from an enumeration
13
+ * is the KEYWORD written above the name, not a hue. So this file states no
14
+ * colours of its own: it names four neutrals off the shared notation scale
15
+ * (R33) and leaves the meaning where the spec puts it, in the shape and the
16
+ * words.
17
+ *
18
+ * Every value here is a creation-time DEFAULT. A UML node is a native shape, so
19
+ * each of these becomes an ordinary editable property the moment the element is
20
+ * placed — exactly as in BPMN and C4.
21
+ */
22
+ /* ── The four neutrals ──────────────────────────────────────────────────── */
23
+ /**
24
+ * Artefact ink: every node outline, every compartment separator, every glyph
25
+ * (the package tab, the note's fold, the actor's stick figure) and the text
26
+ * written inside an artefact.
27
+ *
28
+ * One ink for all of it, because UML draws one: the notation's contrast is
29
+ * between the LINES and the paper, and a second artefact grey would read as a
30
+ * distinction the spec never makes.
31
+ */
32
+ export const UML_INK = NOTATION_NEUTRALS.ink;
33
+ /**
34
+ * Background structure: the diagram frame's border, its heading tag and the
35
+ * subject's outline and name — the furniture the diagram is drawn ON, as
36
+ * opposed to the diagram itself.
37
+ */
38
+ export const UML_FRAME_INK = NOTATION_NEUTRALS.frameInk;
39
+ /** The paper: the diagram frame's card fill, and the fill of a boxed node. */
40
+ export const UML_CARD = NOTATION_NEUTRALS.cardFill;
41
+ /**
42
+ * Secondary strokes — the divider under the heading band. Lighter than
43
+ * {@link UML_FRAME_INK} so that the band reads as a strip of the same sheet
44
+ * rather than as a second frame drawn inside the first.
45
+ */
46
+ export const UML_DIVIDER = NOTATION_NEUTRALS.divider;
47
+ /* ── Weights ───────────────────────────────────────────────────────────── */
48
+ /**
49
+ * Every relationship is drawn at the same weight — the notation distinguishes
50
+ * them by their ENDS (hollow triangle, hollow or filled diamond, open arrow)
51
+ * and by solid versus dashed, never by thickness. See the edge style table in
52
+ * `actions.ts`.
53
+ */
54
+ export const UML_EDGE_WIDTH = 2;
55
+ /** Every node outline, at the same weight as every line: UML draws one pen. */
56
+ export const UML_NODE_STROKE_WIDTH = 2;
57
+ /* ── Type ──────────────────────────────────────────────────────────────── */
58
+ /** The same family every other framework in the library writes in. */
59
+ export const UML_FONT_FAMILY = 'Inter, sans-serif';
60
+ /**
61
+ * The name compartment, and the two tiers under it.
62
+ *
63
+ * A two-step ladder rather than C4's three, because a classifier has two kinds
64
+ * of words in it: its NAME (with its keyword line, §11.4.4) and its FEATURES —
65
+ * attributes and operations, which are the same sort of statement at the same
66
+ * size. Both are creation-time defaults an author can climb off.
67
+ */
68
+ export const UML_NAME_FONT_SIZE = 16;
69
+ export const UML_BODY_FONT_SIZE = 13;
70
+ /* ── The diagram frame ─────────────────────────────────────────────────── */
71
+ /**
72
+ * The bite taken out of the heading tag's top-right corner, in model units.
73
+ *
74
+ * UML 2.5.1 Annex A draws the frame heading in a rectangle with one corner cut
75
+ * off — the pentagon that tells a reader, at a glance and before reading a
76
+ * word, that they are looking at a diagram frame and not at a box in the
77
+ * drawing. 12 units against a 44-unit band is the proportion the Annex's own
78
+ * figures show.
79
+ */
80
+ export const UML_FRAME_TAG_CUT = 12;
81
+ /**
82
+ * How much shorter the tag is than the band it is drawn in — the air under it,
83
+ * which is what makes it read as a label PINNED to the frame rather than as a
84
+ * second rectangle sharing its top edge.
85
+ */
86
+ export const UML_FRAME_TAG_FOOT = 8;
87
+ /** The heading's own size, and the frame's line. */
88
+ export const UML_FRAME_HEADING_FONT_SIZE = 20;
89
+ export const UML_FRAME_BORDER_WIDTH = 1.5;
90
+ /**
91
+ * The frame's other three margins — the plot's inset from its own border.
92
+ *
93
+ * Also the tag's horizontal PADDING: the heading is written at the plot's left
94
+ * edge, so a tag padded by the same number is a tag with its words centred in
95
+ * it, and the two cannot drift because there is one number.
96
+ */
97
+ export const UML_DIAGRAM_MARGIN = 24;
98
+ /**
99
+ * The band the heading tag is written in — the frame's TOP MARGIN, owned by
100
+ * `@labre/affine-model` because the frame's hit test reads it too (a diagram is
101
+ * clickable by its heading band as well as by its border).
102
+ *
103
+ * Re-exported here so this file stays the one place a reader looks for the
104
+ * pack's metrics, exactly as `consts.ts` in the C4 pack re-exports
105
+ * `C4_BOARD_TITLE_BAND_HEIGHT`.
106
+ */
107
+ export { UML_FRAME_BAND_HEIGHT } from '@formicoidea/labre-core/model';
108
+ /** The size a fresh diagram frame is created at — a Context Map sheet's own. */
109
+ export const UML_DIAGRAM_BOX = { w: 1400, h: 900 };
110
+ /**
111
+ * The size a fresh subject is created at (§18.1.4) — a C4 boundary's own
112
+ * footprint, because it is the same kind of object: a rectangle drawn round
113
+ * part of a diagram that is already there.
114
+ */
115
+ export const UML_SUBJECT_BOX = { w: 520, h: 360 };
116
+ /**
117
+ * The subject's inset, equal on all four sides: unlike a diagram frame it has
118
+ * no furniture to make room for — §18.1.4 writes its name INSIDE the top-left
119
+ * corner, over the drawing, exactly as a C4 boundary writes its own.
120
+ */
121
+ export const UML_SUBJECT_MARGIN = 12;
122
+ /** The subject's outline: solid and thin — §18.1.4 draws no dash and no tint. */
123
+ export const UML_SUBJECT_BORDER_WIDTH = 1.5;
124
+ /* ── Node footprints ───────────────────────────────────────────────────── */
125
+ /**
126
+ * Default node sizes (model units) per kind. TOTAL over {@link UmlNodeKind} by
127
+ * its type, so a kind added to the model cannot land without a footprint.
128
+ *
129
+ * The four compartmented kinds share ONE box — a class, an interface, an
130
+ * enumeration and an object are the same rectangle with different words in it,
131
+ * and a row of them lining up without anybody arranging them is what makes a
132
+ * class diagram readable. 200 × 120 fits a name line plus three feature lines
133
+ * at the sizes above with the margins `component.ts` lays out.
134
+ *
135
+ * The other four are the shapes the notation draws rather than boxes:
136
+ *
137
+ * - `package` is taller by the height of its tab (§12.2.4);
138
+ * - `note` is smaller and squarer — it holds a sentence, not a signature
139
+ * list (Annex A);
140
+ * - `actor` is the stick figure's own aspect ratio, tall and narrow: squeezed
141
+ * into a wide box the head stops being a circle, which is the one thing
142
+ * about a UML actor everybody recognises (§18.1.4);
143
+ * - `use-case` is the ellipse, wide and shallow so a verb phrase fits across
144
+ * its widest chord rather than in its corners, which it has none of.
145
+ *
146
+ * ## The phase-2 footprints
147
+ *
148
+ * A `component` is the class box's own 200 × 120: §11.6.4 draws it as the same
149
+ * rectangle with an icon in the corner, and a component diagram lines its boxes
150
+ * up beside a class diagram's without anybody resizing anything.
151
+ *
152
+ * The rest are their pictures' own proportions:
153
+ *
154
+ * - `port` is a SMALL SQUARE (§11.3.4) — 16 × 16, the one footprint in the pack
155
+ * that is not big enough to write in, which is why its label is placed beside
156
+ * it rather than inside it (`component.ts`);
157
+ * - the two interface glyphs are 60 × 40, tall enough for a ball or a socket
158
+ * over the stub that carries it (§10.4.4) and no wider than the glyph needs —
159
+ * their names are written beside them too;
160
+ * - `artifact` is the note's 180 × 100: a file name over a line or two of what
161
+ * it holds, which is the same amount of writing (§19.3.4);
162
+ * - the three cubes share 220 × 140 (§19.4.4). Wider and taller than a class
163
+ * box because the 3-D depth eats into both: the FRONT FACE — the only part a
164
+ * name can be written in — is what has to end up the size of a box.
165
+ */
166
+ export const UML_NODE_BOX = {
167
+ class: { w: 200, h: 120 },
168
+ interface: { w: 200, h: 120 },
169
+ enumeration: { w: 200, h: 120 },
170
+ object: { w: 200, h: 120 },
171
+ package: { w: 200, h: 130 },
172
+ note: { w: 180, h: 100 },
173
+ actor: { w: 80, h: 120 },
174
+ 'use-case': { w: 200, h: 90 },
175
+ component: { w: 200, h: 120 },
176
+ port: { w: 16, h: 16 },
177
+ 'provided-interface': { w: 60, h: 40 },
178
+ 'required-interface': { w: 60, h: 40 },
179
+ artifact: { w: 180, h: 100 },
180
+ node: { w: 220, h: 140 },
181
+ device: { w: 220, h: 140 },
182
+ 'execution-environment': { w: 220, h: 140 },
183
+ // ── The activity footprints (phase 2) ──────────────────────────────────
184
+ // An `action` is WIDER AND SHALLOWER than a class box: §15.3.4 writes a verb
185
+ // phrase in it — "Vérifier le stock", one line — where a classifier holds a
186
+ // signature list. 160 × 60 is the box that phrase fits across rather than
187
+ // down, and it is the module a whole activity diagram is measured in.
188
+ action: { w: 160, h: 60 },
189
+ // The control nodes are DOTS AND BARS, not boxes: every one of them is sized
190
+ // to its own picture, because none of them has words inside it.
191
+ //
192
+ // - `initial` is the filled disc, 24 across — small enough to read as a
193
+ // marker rather than as a node, big enough to aim at;
194
+ // - `activity-final` and `flow-final` are 28, a touch larger because both
195
+ // are a circle with something INSIDE it (a second disc, a cross) and a
196
+ // 24-unit ring would close up;
197
+ // - `decision` is the diamond, square by construction so it stays a diamond;
198
+ // - `fork` is the bar — 120 long and 10 thick, the one footprint in the pack
199
+ // that is deliberately degenerate in one dimension (§15.3.4 draws a line
200
+ // segment, and a bar as tall as it is wide would read as a box).
201
+ initial: { w: 24, h: 24 },
202
+ 'activity-final': { w: 28, h: 28 },
203
+ 'flow-final': { w: 28, h: 28 },
204
+ decision: { w: 48, h: 48 },
205
+ fork: { w: 120, h: 10 },
206
+ // §15.4.4: a plain rectangle carrying a value. Narrower than an action,
207
+ // because it holds a NOUN — `Commande [validée]` — and taller than nothing,
208
+ // because the optional `[state]` goes on a second line.
209
+ 'object-node': { w: 140, h: 60 },
210
+ // §16.3.4: the two pentagons. The action's own 160 × 60, so a signal sent in
211
+ // the middle of a flow lines up with the actions either side of it without
212
+ // anybody resizing anything — the point of the shape is the silhouette, not
213
+ // the size.
214
+ 'send-signal': { w: 160, h: 60 },
215
+ 'accept-event': { w: 160, h: 60 },
216
+ // §16.10.4: the hourglass. TALL and narrow, which is what an hourglass is —
217
+ // and small, because its words are written beside it rather than in it
218
+ // (`component.ts`), there being no inside to write in.
219
+ 'time-event': { w: 40, h: 56 },
220
+ // ── The state machine footprints (phase 2) ─────────────────────────────
221
+ // §14.2.4: a state is the biggest box of the two behaviour families, because
222
+ // it is the only one with a COMPARTMENT — a name over the internal activities
223
+ // (`entry /`, `do /`, `exit /`), which is three lines under a heading.
224
+ state: { w: 180, h: 90 },
225
+ // The bullseye, drawn exactly like `activity-final` and therefore exactly its
226
+ // size: two shapes a reader is meant to recognise as the same picture must
227
+ // not arrive at different sizes.
228
+ 'final-state': { w: 28, h: 28 },
229
+ // The pseudostates, every one of them a small mark:
230
+ //
231
+ // - `choice` is the diamond again, at 40 rather than the decision's 48 —
232
+ // §14.2.4 draws it smaller, and the difference is what keeps an activity's
233
+ // decision and a machine's choice from being mistaken for one another;
234
+ // - `junction` is the smallest mark in the pack at 16, a filled dot that
235
+ // merely joins lines;
236
+ // - the two histories are 28, the circle that has to hold an `H` or an `H*`;
237
+ // - `entry-point` and `exit-point` are 20 — a hollow circle and a crossed
238
+ // one, drawn ON the border of a composite state, so they are sized like
239
+ // the port they behave like (§11.3.4's 16 plus room for the cross);
240
+ // - `terminate` is the bare X, at the finals' own 28 so a machine's three
241
+ // end marks are one size.
242
+ choice: { w: 40, h: 40 },
243
+ junction: { w: 16, h: 16 },
244
+ 'shallow-history': { w: 28, h: 28 },
245
+ 'deep-history': { w: 28, h: 28 },
246
+ 'entry-point': { w: 20, h: 20 },
247
+ 'exit-point': { w: 20, h: 20 },
248
+ terminate: { w: 28, h: 28 },
249
+ // ── The interaction footprints (phase 3) ───────────────────────────────
250
+ // §17.3.4: a lifeline is a NARROW TALL COLUMN — the element IS the spine, and
251
+ // the head is painted over its top by the glyph layer (see
252
+ // {@link UML_LIFELINE_HEAD}). 16 wide so a message's native perimeter anchors
253
+ // land 8 units either side of the dashed line rather than 80, which is the
254
+ // whole reason the element is not head-shaped; 600 tall because that is a
255
+ // sheet's worth of conversation at the 40-unit rhythm messages are drawn on.
256
+ //
257
+ // The one footprint in the pack that is SMALLER than its own picture, and the
258
+ // model says so in as many words: `UmlNodeElementModel.elementBound` and
259
+ // `.includesPoint` both widen to the head.
260
+ lifeline: { w: 16, h: UML_LIFELINE_SPINE },
261
+ // §17.2.4: the ExecutionSpecification, a thin bar sat ON the spine. 12 wide —
262
+ // a touch narrower than the 16-unit column, so a bar dropped on a lifeline
263
+ // reads as sitting on the line rather than as replacing it. 80 tall is two
264
+ // messages' worth of "busy", which is what an author drags longer or shorter.
265
+ execution: { w: 12, h: 80 },
266
+ // §17.2.4: the destruction X. A SQUARE, like every other bare cross in the
267
+ // pack (`terminate`, `flow-final`), at the size a mark drawn over a 16-unit
268
+ // column has to be to be seen against it.
269
+ destruction: { w: 24, h: 24 },
270
+ };
271
+ /**
272
+ * How far a PORT's centre may sit from its component's outline before
273
+ * `uml.port-on-border` speaks — the tolerance of the `border-proximity` rule
274
+ * (ADR 0024).
275
+ *
276
+ * The glyph's own side, and derived from it rather than restated: §11.3.4 draws
277
+ * a port as a small square ON the boundary, and what makes that reading legible
278
+ * is the square's own footprint against the line, not the size of the component
279
+ * behind it. Three drawings, and the constant is chosen so each gets the answer
280
+ * the eye gives:
281
+ *
282
+ * - HALF IN, HALF OUT — the clause's own preferred picture. The centre is on
283
+ * the line, distance 0, and nothing is said.
284
+ * - TANGENT INSIDE, the square just clear of the edge. The centre is 8 units
285
+ * in, half the glyph, well under the tolerance: also silence, because that is
286
+ * a port an author drew on the border and a tool arguing with it is a tool
287
+ * switched off.
288
+ * - PUSHED IN BY ITS OWN SIZE, the square a full 16 units clear of the edge.
289
+ * The centre is 24 units in, past the tolerance, and the finding falls. That
290
+ * is the drawing the PO made on the recette of 2026-09-16: a port dragged
291
+ * into the middle of its component, which §11.3.4 gives no meaning at all.
292
+ *
293
+ * Deliberately NOT `UML_ATTACH_TOLERANCE` (`model.ts`), which is 24 and measures
294
+ * something else: an edge-to-edge GAP, used to decide which box a glyph belongs
295
+ * to when the exporters need an owner. This one measures a centre against a
296
+ * line, and the two numbers answering two questions is better than one number
297
+ * quietly meaning both.
298
+ */
299
+ export const UML_PORT_BORDER_TOLERANCE = UML_NODE_BOX.port.w;
300
+ /* ── The two behaviour frames ──────────────────────────────────────────── */
301
+ /**
302
+ * The size a fresh PARTITION is created at (§15.6.4), in its default VERTICAL
303
+ * orientation: a column, as tall as the diagram frame it is dropped on and wide
304
+ * enough for an action box (160) with air either side of it.
305
+ *
306
+ * A horizontal partition is created by transposing this, which the creation
307
+ * site does rather than a second constant: a row is a column turned, and two
308
+ * pairs of numbers would be two things to keep in step.
309
+ */
310
+ export const UML_PARTITION_BOX = { w: 360, h: 900 };
311
+ /**
312
+ * The size a fresh composite state is created at (§14.2.4) — the subject's own
313
+ * footprint, because it is the same kind of object: a rectangle drawn round a
314
+ * handful of elements that are already there.
315
+ */
316
+ export const UML_REGION_BOX = { w: 520, h: 320 };
317
+ /**
318
+ * The partition's and the region's inset from their own border — the room the
319
+ * drawing gives up on the three sides that carry no band.
320
+ *
321
+ * The subject's 12, and for the same reason: both are transparent frames drawn
322
+ * OVER work that is already there, so every unit of margin is a unit of that
323
+ * work the frame's own furniture sits on top of.
324
+ */
325
+ export const UML_PARTITION_MARGIN = 12;
326
+ export const UML_REGION_MARGIN = 12;
327
+ /** Both frames' outline: solid and thin, exactly as §15.6.4 and §14.2.4 draw them. */
328
+ export const UML_PARTITION_BORDER_WIDTH = 1.5;
329
+ export const UML_REGION_BORDER_WIDTH = 1.5;
330
+ /**
331
+ * The corner radius of a composite state, in MODEL UNITS.
332
+ *
333
+ * §14.2.4 draws a state — simple or composite — as a ROUND-CORNERED rectangle,
334
+ * and a composite state that came back square would read as a subject or a
335
+ * partition rather than as the state it is. An absolute rather than a fraction,
336
+ * unlike {@link UML_NODE_RADIUS}, because the framework-background declaration's
337
+ * `border.radius` is in model units: the primitive draws the card, and the card
338
+ * is measured in the units the element is.
339
+ */
340
+ export const UML_REGION_RADIUS = 16;
341
+ /**
342
+ * The name band of a partition and of a composite state — the strip each writes
343
+ * its name in, owned by `@labre/affine-model` because both hit tests read it.
344
+ *
345
+ * Re-exported here so this file stays the one place a reader looks for the
346
+ * pack's metrics, exactly as {@link UML_FRAME_BAND_HEIGHT} is.
347
+ */
348
+ export { UML_PARTITION_BAND, UML_REGION_BAND } from '@formicoidea/labre-core/model';
349
+ /* ── The interaction artefacts (phase 3) ───────────────────────────────── */
350
+ /**
351
+ * The lifeline's head box and the height of its column, owned by
352
+ * `@labre/affine-model` because the ELEMENT's own `elementBound` and
353
+ * `includesPoint` read them: §17.3.4's head is 160 wide over a 16-wide column,
354
+ * so the picture overflows the box the platform knows about and the model has
355
+ * to say so.
356
+ *
357
+ * Re-exported here so this file stays the one place a reader looks for the
358
+ * pack's metrics, exactly as {@link UML_FRAME_BAND_HEIGHT} is.
359
+ */
360
+ export { UML_LIFELINE_HEAD, UML_LIFELINE_SPINE } from '@formicoidea/labre-core/model';
361
+ /**
362
+ * The dash pattern of a lifeline's SPINE, in model units (§17.3.4).
363
+ *
364
+ * 6 on, 6 off: even, because the spine is a TIMELINE rather than a relationship
365
+ * — nothing about it points one way — and long enough to read as a dashed line
366
+ * rather than as a dotted one at the zoom a whole sequence diagram is viewed
367
+ * at.
368
+ */
369
+ export const UML_LIFELINE_DASH = [6, 6];
370
+ /** The spine's own weight: lighter than the head, which is a box and not a line. */
371
+ export const UML_LIFELINE_SPINE_WIDTH = 1.5;
372
+ /**
373
+ * The size a fresh combined fragment is created at (§17.6.4).
374
+ *
375
+ * Wide enough to cover three lifelines at the 200-unit spacing a sequence
376
+ * diagram is laid out on, and tall enough for the three or four exchanges an
377
+ * `alt` is actually drawn round. The same pair the model seeds its `xywh` with,
378
+ * and the declaration reads it from here.
379
+ */
380
+ export const UML_FRAGMENT_BOX = { w: 600, h: 260 };
381
+ /**
382
+ * The fragment's inset from its own border on the three sides that carry no
383
+ * tag — the subject's and the partition's 12, and for the same reason: a
384
+ * transparent frame drawn over work that is already there spends every unit of
385
+ * margin on somebody else's drawing.
386
+ */
387
+ export const UML_FRAGMENT_MARGIN = 12;
388
+ /** Its outline: solid and thin, exactly as §17.6.4 draws it. */
389
+ export const UML_FRAGMENT_BORDER_WIDTH = 1.5;
390
+ /**
391
+ * The dashed line between two operands (§17.6.4).
392
+ *
393
+ * A LONGER dash than the lifeline's, on purpose: the two are drawn on the same
394
+ * sheet, crossing each other, and a reader has to tell "this participant exists
395
+ * but is idle" from "the alternative below is a different case".
396
+ */
397
+ export const UML_FRAGMENT_OPERAND_DASH = [10, 6];
398
+ /**
399
+ * The fragment's operator band — the strip its pentagon is drawn in, owned by
400
+ * `@labre/affine-model` because the fragment's hit test reads it.
401
+ *
402
+ * Re-exported for the reason every other band is.
403
+ */
404
+ export { UML_FRAGMENT_BAND } from '@formicoidea/labre-core/model';
405
+ /**
406
+ * The guard written in an operand's own corner, and its inset from the
407
+ * operand's top-left.
408
+ *
409
+ * Smaller than a name: §17.6.4 writes `[x > 0]` as a condition, not as a
410
+ * heading, and a guard set at the frame heading's 20px would compete with the
411
+ * operator word two lines above it.
412
+ */
413
+ export const UML_FRAGMENT_GUARD_FONT_SIZE = UML_BODY_FONT_SIZE;
414
+ export const UML_FRAGMENT_GUARD_INSET = 8;
415
+ /**
416
+ * How close to an operand separator the pointer counts as being ON it, in model
417
+ * units either side.
418
+ *
419
+ * The BPMN pool's own grab half-width, and deliberately: the two gestures are
420
+ * the same gesture on the same kind of line, and a user who has learnt one
421
+ * should not find the other fussier. 12 is wide enough to aim at with a mouse
422
+ * and narrow enough that it does not swallow the messages drawn either side of
423
+ * it.
424
+ */
425
+ export const UML_OPERAND_GRAB = 12;
426
+ /**
427
+ * The GUARD's rename target: the corner box a double-click on an operand's
428
+ * `[condition]` has to land in.
429
+ *
430
+ * Deliberately a CORNER and not the whole band (see `board-hit.ts`): an
431
+ * operand's guard is written over the messages it governs, so a zone covering
432
+ * the operand would take every double-click meant for them. Wide enough for a
433
+ * realistic condition at the guard's own size, and one line tall plus the inset
434
+ * it is anchored by.
435
+ */
436
+ export const UML_OPERAND_GUARD_WIDTH = 180;
437
+ export const UML_OPERAND_GUARD_HEIGHT = UML_FRAGMENT_GUARD_FONT_SIZE + UML_FRAGMENT_GUARD_INSET * 2;
438
+ /**
439
+ * The smallest an operand may be dragged, in model units at the fragment's own
440
+ * reference height — converted to a weight against that fragment's total, so a
441
+ * fragment stretched taller keeps the same VISIBLE floor.
442
+ *
443
+ * 48 is the guard line plus room for one message under it: an operand smaller
444
+ * than that holds nothing, and the separator that made it is no longer a
445
+ * division of anything.
446
+ */
447
+ export const UML_OPERAND_MIN_HEIGHT = 48;
448
+ /* ── Rounded artefacts ─────────────────────────────────────────────────── */
449
+ /**
450
+ * The corner radius of an `action` and of a `state`, as the native shape
451
+ * renderer reads it: a value below 1 is a FRACTION of the box's shorter side.
452
+ *
453
+ * The two round-cornered rectangles of the notation (§15.3.4, §14.2.4), and the
454
+ * only two places this pack departs from `radius: 0`. That zero is not a house
455
+ * style to be defended here — it is what §11.4.4 draws for a classifier — and
456
+ * these two are what §15.3.4 and §14.2.4 draw for their own boxes. A square
457
+ * action is an action that looks like a class.
458
+ *
459
+ * 0.12 of the shorter side: 7 units on a 160 × 60 action, 11 on a 180 × 90
460
+ * state. Enough to be seen at a glance across a sheet, little enough that the
461
+ * box is still a box.
462
+ */
463
+ export const UML_NODE_RADIUS = 0.12;
@@ -0,0 +1,12 @@
1
+ import { UmlRenderViewExtension, UmlViewExtension } from './view.js';
2
+ /** Host wiring for the uml framework. */
3
+ export declare const umlFramework: {
4
+ readonly flag: "uml";
5
+ readonly telemetryKey: "uml";
6
+ readonly extensions: readonly [{
7
+ readonly viewExtension: typeof UmlRenderViewExtension;
8
+ }, {
9
+ readonly flag: "uml";
10
+ readonly viewExtension: typeof UmlViewExtension;
11
+ }];
12
+ };
@@ -0,0 +1,10 @@
1
+ import { UmlRenderViewExtension, UmlViewExtension } from './view.js';
2
+ /** Host wiring for the uml framework. */
3
+ export const umlFramework = {
4
+ flag: 'uml',
5
+ telemetryKey: 'uml',
6
+ extensions: [
7
+ { viewExtension: UmlRenderViewExtension },
8
+ { flag: 'uml', viewExtension: UmlViewExtension },
9
+ ],
10
+ };
@@ -0,0 +1,32 @@
1
+ /**
2
+ * The sentence a host sees when the runtime cannot inflate.
3
+ *
4
+ * Exported so the command's spec can assert the wording rather than a substring
5
+ * of it, and so a host that wants to offer "open it in draw.io and save it
6
+ * uncompressed" has something to match on.
7
+ */
8
+ export declare const DRAWIO_NO_DECOMPRESSION = "This runtime cannot read a compressed .drawio file (DecompressionStream is unavailable). Re-save it from draw.io with File \u203A Properties \u203A Compressed turned off.";
9
+ export declare const DRAWIO_EMPTY_DIAGRAM = "This .drawio file holds no <diagram> to read.";
10
+ export declare const DRAWIO_BAD_PAYLOAD = "This .drawio file\u2019s <diagram> is neither XML nor a payload this reader could inflate.";
11
+ /**
12
+ * An `.drawio` file's text as the `<mxGraphModel>` XML inside it.
13
+ *
14
+ * Identity when the text already holds an `<mxGraphModel>` — that covers a
15
+ * `.drawio.xml`, an uncompressed `.drawio`, and the decoded corpus a spec
16
+ * feeds in. Otherwise: take the first `<diagram>`'s text, base64-decode it,
17
+ * inflate it as a raw deflate stream, and undo the `encodeURIComponent`
18
+ * draw.io applied before compressing.
19
+ *
20
+ * Only the FIRST page is read, and it is a limitation rather than an oversight:
21
+ * a multi-page `.drawio` is several diagrams, this import draws one sheet, and
22
+ * a reader that silently concatenated two pages would produce a drawing neither
23
+ * page ever was. The remaining pages are what a second import of the same file
24
+ * is for.
25
+ *
26
+ * Throws, with one of the three sentences above, when it cannot. That is the
27
+ * contract the import pipeline already has for a file it cannot read
28
+ * (`importInterchangeFile` shows the thrown sentence as the failure
29
+ * notification), and it is the right one here: an empty board with a `warning`
30
+ * would claim the file was read and found empty.
31
+ */
32
+ export declare function decodeDrawio(text: string): Promise<string>;
@@ -0,0 +1,96 @@
1
+ import { isCompressedDrawio } from './drawio-import.js';
2
+ /**
3
+ * The one impure step of the draw.io import: inflating an `<mxfile>`'s payload
4
+ * (`docs/adr/0019`, the compressed-payload split).
5
+ *
6
+ * ## Why this is a module of its own, and not part of the reader
7
+ *
8
+ * `docs/adr/0012` P3 makes every interchange reader a PURE function of text: no
9
+ * `std`, no DOM, no clock, no platform. That is what lets labre-mcp call the
10
+ * very function the editor command calls, and what lets a unit suite prove a
11
+ * format with six string literals. Inflating a raw-deflate stream breaks all
12
+ * three halves of it — it needs `DecompressionStream`, it is asynchronous, and
13
+ * what it hands back is bytes — so it does not belong in `drawio-import.ts` and
14
+ * it is not in the capability's `run` either.
15
+ *
16
+ * The split that came out of that:
17
+ *
18
+ * - `importDrawio` (pure, synchronous) reads DECODED `<mxGraphModel>` XML;
19
+ * - `UML_DRAWIO_IMPORT.run` does the same, and answers a payload that is still
20
+ * compressed with one `warning` note rather than pretending it read nothing;
21
+ * - this function, called by the COMMAND before either of them, is where the
22
+ * platform API lives.
23
+ *
24
+ * A caller that has plain XML pays nothing: an `<mxGraphModel>` anywhere in the
25
+ * text is returned unchanged, which is also what draw.io itself writes with
26
+ * File › Properties › Compressed turned off.
27
+ *
28
+ * ## No dependency, and no vendored inflater
29
+ *
30
+ * `DecompressionStream('deflate-raw')` is the platform's own zlib, shipped in
31
+ * every browser this library supports and in Node 18 and up. Adding `pako` or
32
+ * `fflate` to read one file format would put forty kilobytes into every bundle
33
+ * that imports this package for the sake of a function the runtime already has.
34
+ * Where the API is genuinely absent the failure is NAMED — see below — rather
35
+ * than left as a `TypeError` about an undefined constructor.
36
+ */
37
+ /** `<diagram …>PAYLOAD</diagram>` — the first page of an `mxfile`. */
38
+ const DIAGRAM = /<diagram\b[^>]*>([\s\S]*?)<\/diagram>/i;
39
+ /**
40
+ * The sentence a host sees when the runtime cannot inflate.
41
+ *
42
+ * Exported so the command's spec can assert the wording rather than a substring
43
+ * of it, and so a host that wants to offer "open it in draw.io and save it
44
+ * uncompressed" has something to match on.
45
+ */
46
+ export const DRAWIO_NO_DECOMPRESSION = 'This runtime cannot read a compressed .drawio file (DecompressionStream is unavailable). Re-save it from draw.io with File › Properties › Compressed turned off.';
47
+ export const DRAWIO_EMPTY_DIAGRAM = 'This .drawio file holds no <diagram> to read.';
48
+ export const DRAWIO_BAD_PAYLOAD = 'This .drawio file’s <diagram> is neither XML nor a payload this reader could inflate.';
49
+ /**
50
+ * An `.drawio` file's text as the `<mxGraphModel>` XML inside it.
51
+ *
52
+ * Identity when the text already holds an `<mxGraphModel>` — that covers a
53
+ * `.drawio.xml`, an uncompressed `.drawio`, and the decoded corpus a spec
54
+ * feeds in. Otherwise: take the first `<diagram>`'s text, base64-decode it,
55
+ * inflate it as a raw deflate stream, and undo the `encodeURIComponent`
56
+ * draw.io applied before compressing.
57
+ *
58
+ * Only the FIRST page is read, and it is a limitation rather than an oversight:
59
+ * a multi-page `.drawio` is several diagrams, this import draws one sheet, and
60
+ * a reader that silently concatenated two pages would produce a drawing neither
61
+ * page ever was. The remaining pages are what a second import of the same file
62
+ * is for.
63
+ *
64
+ * Throws, with one of the three sentences above, when it cannot. That is the
65
+ * contract the import pipeline already has for a file it cannot read
66
+ * (`importInterchangeFile` shows the thrown sentence as the failure
67
+ * notification), and it is the right one here: an empty board with a `warning`
68
+ * would claim the file was read and found empty.
69
+ */
70
+ export async function decodeDrawio(text) {
71
+ if (!isCompressedDrawio(text))
72
+ return text;
73
+ const payload = DIAGRAM.exec(text)?.[1]?.trim();
74
+ if (!payload)
75
+ throw new Error(DRAWIO_EMPTY_DIAGRAM);
76
+ const Decompression = globalThis.DecompressionStream;
77
+ if (!Decompression)
78
+ throw new Error(DRAWIO_NO_DECOMPRESSION);
79
+ let inflated;
80
+ try {
81
+ const binary = atob(payload);
82
+ const bytes = Uint8Array.from(binary, character => character.charCodeAt(0));
83
+ const stream = new Blob([bytes])
84
+ .stream()
85
+ .pipeThrough(new Decompression('deflate-raw'));
86
+ // draw.io deflates the URI-ENCODED xml, so this is percent-escaped text and
87
+ // not the document yet.
88
+ inflated = decodeURIComponent(await new Response(stream).text());
89
+ }
90
+ catch {
91
+ throw new Error(DRAWIO_BAD_PAYLOAD);
92
+ }
93
+ if (!inflated.includes('<mxGraphModel'))
94
+ throw new Error(DRAWIO_BAD_PAYLOAD);
95
+ return inflated;
96
+ }