@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,983 @@
1
+ import { activateUmlEdge, createUmlClassifier, createUmlDiagram, createUmlFragment, createUmlInteractionUse, createUmlNode, createUmlPartition, createUmlRegion, createUmlSubject, exportUmlPlantumlFile, exportUmlXmiFile, importUmlDrawioFile, importUmlPlantumlFile, importUmlXmiFile, umlDiagramsForExport, } from './actions.js';
2
+ import { umlLegendEntry, UML_LEGEND_BOX } from './legend.js';
3
+ import { umlExportPlantumlIcon, umlExportXmiIcon, umlImportDrawioIcon, umlImportPlantumlIcon, umlImportXmiIcon, umlToolbarIcon, UML_TOOLBOX_ICONS, } from './toolbar/icons.js';
4
+ const SPECS = [
5
+ /* ── The cold row, authored in the order it paints ─────────────────── */
6
+ // The first nineteen entries are authored in SUB-MENU order rather than
7
+ // in catalogue order, and since the PO ruling of 2026-09-16 the two
8
+ // readings no longer coincide. One `order` still feeds both surfaces, but
9
+ // the catalogue GROUPS by `category` — a section gathers its rows wherever
10
+ // they sit in this list — while the row does not group at all. So the
11
+ // thirteen a cold start paints come first, in the order they paint, and
12
+ // the six entries that decline the row but belong to one of their three
13
+ // sections are slotted in where that section needs them: `addObject`
14
+ // before the class relations, `addNote` before the general ones, and the
15
+ // four below the last seat.
16
+ //
17
+ // Both readings are pinned in `commands-manifest.unit.spec.ts` — the
18
+ // nominated fourteen, the rendered thirteen, and the nine sections.
19
+ {
20
+ id: 'addDiagram',
21
+ label: 'UML diagram',
22
+ iconKey: 'uml.diagram',
23
+ kind: 'artefact',
24
+ category: 'general',
25
+ element: 'board',
26
+ board: true,
27
+ senior: true,
28
+ // Wrapped, not passed by reference: `run` is called `(std, invocation)` and
29
+ // `createUmlDiagram` takes `(std, kind = 'class')`, so handing it over bare
30
+ // would make the invocation object the diagram's KIND. The frame is always
31
+ // born a class diagram; the picker on its own row is what changes it.
32
+ run: std => createUmlDiagram(std),
33
+ },
34
+ {
35
+ id: 'addClass',
36
+ label: 'Class',
37
+ iconKey: 'uml.class',
38
+ kind: 'artefact',
39
+ category: 'class-diagram',
40
+ element: 'node:class',
41
+ senior: true,
42
+ run: std => createUmlClassifier(std, 'class'),
43
+ },
44
+ {
45
+ id: 'addInterface',
46
+ label: 'Interface',
47
+ iconKey: 'uml.interface',
48
+ kind: 'artefact',
49
+ category: 'class-diagram',
50
+ element: 'node:interface',
51
+ senior: true,
52
+ run: std => createUmlClassifier(std, 'interface'),
53
+ },
54
+ {
55
+ id: 'addEnumeration',
56
+ label: 'Enumeration',
57
+ iconKey: 'uml.enumeration',
58
+ kind: 'artefact',
59
+ category: 'class-diagram',
60
+ element: 'node:enumeration',
61
+ senior: true,
62
+ run: std => createUmlClassifier(std, 'enumeration'),
63
+ },
64
+ {
65
+ id: 'addPackage',
66
+ label: 'Package',
67
+ iconKey: 'uml.package',
68
+ kind: 'artefact',
69
+ category: 'general',
70
+ element: 'node:package',
71
+ senior: true,
72
+ run: std => createUmlNode(std, 'package'),
73
+ },
74
+ {
75
+ id: 'addActor',
76
+ label: 'Actor',
77
+ iconKey: 'uml.actor',
78
+ kind: 'artefact',
79
+ category: 'use-case-diagram',
80
+ element: 'node:actor',
81
+ senior: true,
82
+ run: std => createUmlNode(std, 'actor'),
83
+ },
84
+ {
85
+ id: 'addUseCase',
86
+ label: 'Use case',
87
+ iconKey: 'uml.use-case',
88
+ kind: 'artefact',
89
+ category: 'use-case-diagram',
90
+ element: 'node:use-case',
91
+ senior: true,
92
+ run: std => createUmlNode(std, 'use-case'),
93
+ },
94
+ {
95
+ id: 'addSubject',
96
+ label: 'Subject',
97
+ iconKey: 'uml.subject',
98
+ kind: 'artefact',
99
+ category: 'use-case-diagram',
100
+ element: 'boundary:subject',
101
+ senior: true,
102
+ run: createUmlSubject,
103
+ },
104
+ {
105
+ // An instance diagram is a SECOND reading of a class diagram — you draw one
106
+ // once the classes exist — so it is the first thing to leave the row.
107
+ id: 'addObject',
108
+ label: 'Object',
109
+ iconKey: 'uml.object',
110
+ kind: 'artefact',
111
+ category: 'class-diagram',
112
+ element: 'node:object',
113
+ senior: false,
114
+ run: std => createUmlClassifier(std, 'object'),
115
+ },
116
+ {
117
+ id: 'associationTool',
118
+ label: 'Association',
119
+ iconKey: 'uml.association',
120
+ kind: 'tool',
121
+ category: 'class-diagram',
122
+ element: 'connector:association',
123
+ senior: true,
124
+ run: std => activateUmlEdge(std, 'association'),
125
+ },
126
+ {
127
+ id: 'addNote',
128
+ label: 'Note',
129
+ iconKey: 'uml.note',
130
+ kind: 'artefact',
131
+ category: 'general',
132
+ element: 'node:note',
133
+ senior: false,
134
+ // ponytail: DEMOTED from the nominated fourteen to make room for
135
+ // `uml.importXmi` (tranche G, `docs/adr/0019`). The budget is fourteen plus
136
+ // the single over-nomination the PO authorized on 2026-08-28, which BPMN
137
+ // has spent, so a fifteenth UML nomination would break
138
+ // `registry.unit.spec.ts` — and an import is the one entry a user wants on
139
+ // an EMPTY board, which is exactly when the sub-menu is open (the same
140
+ // ruling that put `bpmn.importXml` in its row). The note is still one click
141
+ // away behind "More artefacts…".
142
+ //
143
+ // Which of the fourteen an import ought to displace is a PO curation point
144
+ // with usage data behind it, not a tranche's to settle: the note was picked
145
+ // because it is the only nominated artefact that annotates a diagram rather
146
+ // than being part of one. Revisit with the phase-2 recette.
147
+ run: std => createUmlNode(std, 'note'),
148
+ },
149
+ {
150
+ id: 'generalizationTool',
151
+ label: 'Generalization',
152
+ iconKey: 'uml.generalization',
153
+ kind: 'tool',
154
+ category: 'general',
155
+ element: 'connector:generalization',
156
+ senior: true,
157
+ run: std => activateUmlEdge(std, 'generalization'),
158
+ },
159
+ {
160
+ id: 'dependencyTool',
161
+ label: 'Dependency',
162
+ iconKey: 'uml.dependency',
163
+ kind: 'tool',
164
+ category: 'general',
165
+ element: 'connector:dependency',
166
+ senior: true,
167
+ run: std => activateUmlEdge(std, 'dependency'),
168
+ },
169
+ {
170
+ id: 'includeTool',
171
+ label: 'Include',
172
+ iconKey: 'uml.include',
173
+ kind: 'tool',
174
+ category: 'use-case-diagram',
175
+ element: 'connector:include',
176
+ senior: true,
177
+ run: std => activateUmlEdge(std, 'include'),
178
+ },
179
+ {
180
+ id: 'extendTool',
181
+ label: 'Extend',
182
+ iconKey: 'uml.extend',
183
+ kind: 'tool',
184
+ category: 'use-case-diagram',
185
+ element: 'connector:extend',
186
+ senior: true,
187
+ run: std => activateUmlEdge(std, 'extend'),
188
+ },
189
+ /* ── Past the thirteenth seat, still in the three sections above ────── */
190
+ {
191
+ id: 'aggregationTool',
192
+ label: 'Aggregation',
193
+ iconKey: 'uml.aggregation',
194
+ kind: 'tool',
195
+ category: 'class-diagram',
196
+ element: 'connector:aggregation',
197
+ senior: false,
198
+ run: std => activateUmlEdge(std, 'aggregation'),
199
+ },
200
+ {
201
+ id: 'compositionTool',
202
+ label: 'Composition',
203
+ iconKey: 'uml.composition',
204
+ kind: 'tool',
205
+ category: 'class-diagram',
206
+ element: 'connector:composition',
207
+ senior: false,
208
+ run: std => activateUmlEdge(std, 'composition'),
209
+ },
210
+ {
211
+ id: 'realizationTool',
212
+ label: 'Realization',
213
+ iconKey: 'uml.realization',
214
+ kind: 'tool',
215
+ category: 'general',
216
+ element: 'connector:realization',
217
+ senior: false,
218
+ run: std => activateUmlEdge(std, 'realization'),
219
+ },
220
+ {
221
+ id: 'anchorTool',
222
+ label: 'Anchor',
223
+ iconKey: 'uml.anchor',
224
+ kind: 'tool',
225
+ category: 'general',
226
+ element: 'connector:anchor',
227
+ senior: false,
228
+ run: std => activateUmlEdge(std, 'anchor'),
229
+ },
230
+ /* ── Component diagram (§11.6.4, §11.3.4, §10.4.4) ──────────────────── */
231
+ // Nothing from here down contests the row, and it is ONE decision rather
232
+ // than fifty: the thirteen a user meets on a cold start are the class and
233
+ // use-case diagrams an architect draws first, and no later tranche gets to
234
+ // re-argue that from inside itself. Which is why the sections below can be
235
+ // authored the way the catalogue reads them — by DIAGRAM KIND, in the order
236
+ // of the frame's own kind picker (`kinds.ts`) — with nothing to interleave.
237
+ {
238
+ id: 'addComponent',
239
+ label: 'Component',
240
+ iconKey: 'uml.component',
241
+ kind: 'artefact',
242
+ category: 'component-diagram',
243
+ element: 'node:component',
244
+ senior: false,
245
+ run: std => createUmlClassifier(std, 'component'),
246
+ },
247
+ {
248
+ id: 'addPort',
249
+ label: 'Port',
250
+ iconKey: 'uml.port',
251
+ kind: 'artefact',
252
+ category: 'component-diagram',
253
+ element: 'node:port',
254
+ senior: false,
255
+ run: std => createUmlNode(std, 'port'),
256
+ },
257
+ {
258
+ id: 'addProvidedInterface',
259
+ label: 'Provided interface',
260
+ iconKey: 'uml.provided-interface',
261
+ kind: 'artefact',
262
+ category: 'component-diagram',
263
+ element: 'node:provided-interface',
264
+ senior: false,
265
+ run: std => createUmlNode(std, 'provided-interface'),
266
+ },
267
+ {
268
+ id: 'addRequiredInterface',
269
+ label: 'Required interface',
270
+ iconKey: 'uml.required-interface',
271
+ kind: 'artefact',
272
+ category: 'component-diagram',
273
+ element: 'node:required-interface',
274
+ senior: false,
275
+ run: std => createUmlNode(std, 'required-interface'),
276
+ },
277
+ /* ── Deployment diagram (§19.2.4, §19.3.4, §19.4.4) ─────────────────── */
278
+ {
279
+ id: 'addArtifact',
280
+ label: 'Artifact',
281
+ iconKey: 'uml.artifact',
282
+ kind: 'artefact',
283
+ category: 'deployment-diagram',
284
+ element: 'node:artifact',
285
+ senior: false,
286
+ run: std => createUmlClassifier(std, 'artifact'),
287
+ },
288
+ {
289
+ id: 'addNode',
290
+ label: 'Node',
291
+ iconKey: 'uml.node',
292
+ kind: 'artefact',
293
+ category: 'deployment-diagram',
294
+ element: 'node:node',
295
+ senior: false,
296
+ run: std => createUmlNode(std, 'node'),
297
+ },
298
+ {
299
+ id: 'addDevice',
300
+ label: 'Device',
301
+ iconKey: 'uml.device',
302
+ kind: 'artefact',
303
+ category: 'deployment-diagram',
304
+ element: 'node:device',
305
+ senior: false,
306
+ run: std => createUmlNode(std, 'device'),
307
+ },
308
+ {
309
+ id: 'addExecutionEnvironment',
310
+ label: 'Execution environment',
311
+ iconKey: 'uml.execution-environment',
312
+ kind: 'artefact',
313
+ category: 'deployment-diagram',
314
+ element: 'node:execution-environment',
315
+ senior: false,
316
+ run: std => createUmlNode(std, 'execution-environment'),
317
+ },
318
+ {
319
+ id: 'deployTool',
320
+ label: 'Deploy',
321
+ iconKey: 'uml.deploy',
322
+ kind: 'tool',
323
+ category: 'deployment-diagram',
324
+ element: 'connector:deploy',
325
+ senior: false,
326
+ run: std => activateUmlEdge(std, 'deploy'),
327
+ },
328
+ {
329
+ id: 'manifestTool',
330
+ label: 'Manifest',
331
+ iconKey: 'uml.manifest',
332
+ kind: 'tool',
333
+ category: 'deployment-diagram',
334
+ element: 'connector:manifest',
335
+ senior: false,
336
+ run: std => activateUmlEdge(std, 'manifest'),
337
+ },
338
+ {
339
+ id: 'communicationPathTool',
340
+ label: 'Communication path',
341
+ iconKey: 'uml.communication-path',
342
+ kind: 'tool',
343
+ category: 'deployment-diagram',
344
+ element: 'connector:communication-path',
345
+ senior: false,
346
+ run: std => activateUmlEdge(std, 'communication-path'),
347
+ },
348
+ /* ── Activity diagram (§15.2.4, §15.3.4, §15.4.4, §16.3.4, §16.10.4) ── */
349
+ {
350
+ id: 'addAction',
351
+ label: 'Action',
352
+ iconKey: 'uml.action',
353
+ kind: 'artefact',
354
+ category: 'activity-diagram',
355
+ element: 'node:action',
356
+ senior: false,
357
+ run: std => createUmlNode(std, 'action'),
358
+ },
359
+ {
360
+ id: 'addInitial',
361
+ label: 'Initial node',
362
+ iconKey: 'uml.initial',
363
+ kind: 'artefact',
364
+ category: 'activity-diagram',
365
+ element: 'node:initial',
366
+ senior: false,
367
+ run: std => createUmlNode(std, 'initial'),
368
+ },
369
+ {
370
+ id: 'addActivityFinal',
371
+ label: 'Activity final',
372
+ iconKey: 'uml.activity-final',
373
+ kind: 'artefact',
374
+ category: 'activity-diagram',
375
+ element: 'node:activity-final',
376
+ senior: false,
377
+ run: std => createUmlNode(std, 'activity-final'),
378
+ },
379
+ {
380
+ id: 'addFlowFinal',
381
+ label: 'Flow final',
382
+ iconKey: 'uml.flow-final',
383
+ kind: 'artefact',
384
+ category: 'activity-diagram',
385
+ element: 'node:flow-final',
386
+ senior: false,
387
+ run: std => createUmlNode(std, 'flow-final'),
388
+ },
389
+ {
390
+ id: 'addDecision',
391
+ label: 'Decision',
392
+ iconKey: 'uml.decision',
393
+ kind: 'artefact',
394
+ category: 'activity-diagram',
395
+ element: 'node:decision',
396
+ senior: false,
397
+ run: std => createUmlNode(std, 'decision'),
398
+ },
399
+ {
400
+ id: 'addFork',
401
+ label: 'Fork',
402
+ iconKey: 'uml.fork',
403
+ kind: 'artefact',
404
+ category: 'activity-diagram',
405
+ element: 'node:fork',
406
+ senior: false,
407
+ run: std => createUmlNode(std, 'fork'),
408
+ },
409
+ {
410
+ id: 'addObjectNode',
411
+ label: 'Object node',
412
+ iconKey: 'uml.object-node',
413
+ kind: 'artefact',
414
+ category: 'activity-diagram',
415
+ element: 'node:object-node',
416
+ senior: false,
417
+ run: std => createUmlNode(std, 'object-node'),
418
+ },
419
+ {
420
+ id: 'addSendSignal',
421
+ label: 'Send signal',
422
+ iconKey: 'uml.send-signal',
423
+ kind: 'artefact',
424
+ category: 'activity-diagram',
425
+ element: 'node:send-signal',
426
+ senior: false,
427
+ run: std => createUmlNode(std, 'send-signal'),
428
+ },
429
+ {
430
+ id: 'addAcceptEvent',
431
+ label: 'Accept event',
432
+ iconKey: 'uml.accept-event',
433
+ kind: 'artefact',
434
+ category: 'activity-diagram',
435
+ element: 'node:accept-event',
436
+ senior: false,
437
+ run: std => createUmlNode(std, 'accept-event'),
438
+ },
439
+ {
440
+ id: 'addTimeEvent',
441
+ label: 'Time event',
442
+ iconKey: 'uml.time-event',
443
+ kind: 'artefact',
444
+ category: 'activity-diagram',
445
+ element: 'node:time-event',
446
+ senior: false,
447
+ run: std => createUmlNode(std, 'time-event'),
448
+ },
449
+ {
450
+ // A swimlane is a BOUNDARY, which is why it sits after the activity's
451
+ // elements and before its two flows: it is neither an element of the model
452
+ // nor the sheet the model is drawn on, it is a band drawn ROUND part of the
453
+ // drawing, and what belongs to it is read back from where things sit
454
+ // (§15.6.4). Its kind is what files it; its type is what places it inside
455
+ // that section.
456
+ id: 'addPartition',
457
+ label: 'Partition',
458
+ iconKey: 'uml.partition',
459
+ kind: 'artefact',
460
+ category: 'activity-diagram',
461
+ element: 'boundary:partition',
462
+ senior: false,
463
+ run: createUmlPartition,
464
+ },
465
+ {
466
+ id: 'controlFlowTool',
467
+ label: 'Control flow',
468
+ iconKey: 'uml.control-flow',
469
+ kind: 'tool',
470
+ category: 'activity-diagram',
471
+ element: 'connector:control-flow',
472
+ senior: false,
473
+ run: std => activateUmlEdge(std, 'control-flow'),
474
+ },
475
+ {
476
+ id: 'objectFlowTool',
477
+ label: 'Object flow',
478
+ iconKey: 'uml.object-flow',
479
+ kind: 'tool',
480
+ category: 'activity-diagram',
481
+ element: 'connector:object-flow',
482
+ senior: false,
483
+ run: std => activateUmlEdge(std, 'object-flow'),
484
+ },
485
+ /* ── State machine diagram (§14.2.4) ────────────────────────────────── */
486
+ {
487
+ id: 'addState',
488
+ label: 'State',
489
+ iconKey: 'uml.state',
490
+ kind: 'artefact',
491
+ category: 'state-machine-diagram',
492
+ element: 'node:state',
493
+ senior: false,
494
+ // The one behaviour artefact that walks the CLASSIFIER path: §14.2.4 draws
495
+ // a state as a divided box, so it arrives with a name compartment and the
496
+ // internal-activities tier the renderer rules off (`actions.ts`).
497
+ run: std => createUmlClassifier(std, 'state'),
498
+ },
499
+ {
500
+ id: 'addFinalState',
501
+ label: 'Final state',
502
+ iconKey: 'uml.final-state',
503
+ kind: 'artefact',
504
+ category: 'state-machine-diagram',
505
+ element: 'node:final-state',
506
+ senior: false,
507
+ run: std => createUmlNode(std, 'final-state'),
508
+ },
509
+ {
510
+ id: 'addChoice',
511
+ label: 'Choice',
512
+ iconKey: 'uml.choice',
513
+ kind: 'artefact',
514
+ category: 'state-machine-diagram',
515
+ element: 'node:choice',
516
+ senior: false,
517
+ run: std => createUmlNode(std, 'choice'),
518
+ },
519
+ {
520
+ id: 'addJunction',
521
+ label: 'Junction',
522
+ iconKey: 'uml.junction',
523
+ kind: 'artefact',
524
+ category: 'state-machine-diagram',
525
+ element: 'node:junction',
526
+ senior: false,
527
+ run: std => createUmlNode(std, 'junction'),
528
+ },
529
+ {
530
+ id: 'addShallowHistory',
531
+ label: 'Shallow history',
532
+ iconKey: 'uml.shallow-history',
533
+ kind: 'artefact',
534
+ category: 'state-machine-diagram',
535
+ element: 'node:shallow-history',
536
+ senior: false,
537
+ run: std => createUmlNode(std, 'shallow-history'),
538
+ },
539
+ {
540
+ id: 'addDeepHistory',
541
+ label: 'Deep history',
542
+ iconKey: 'uml.deep-history',
543
+ kind: 'artefact',
544
+ category: 'state-machine-diagram',
545
+ element: 'node:deep-history',
546
+ senior: false,
547
+ run: std => createUmlNode(std, 'deep-history'),
548
+ },
549
+ {
550
+ id: 'addEntryPoint',
551
+ label: 'Entry point',
552
+ iconKey: 'uml.entry-point',
553
+ kind: 'artefact',
554
+ category: 'state-machine-diagram',
555
+ element: 'node:entry-point',
556
+ senior: false,
557
+ run: std => createUmlNode(std, 'entry-point'),
558
+ },
559
+ {
560
+ id: 'addExitPoint',
561
+ label: 'Exit point',
562
+ iconKey: 'uml.exit-point',
563
+ kind: 'artefact',
564
+ category: 'state-machine-diagram',
565
+ element: 'node:exit-point',
566
+ senior: false,
567
+ run: std => createUmlNode(std, 'exit-point'),
568
+ },
569
+ {
570
+ id: 'addTerminate',
571
+ label: 'Terminate',
572
+ iconKey: 'uml.terminate',
573
+ kind: 'artefact',
574
+ category: 'state-machine-diagram',
575
+ element: 'node:terminate',
576
+ senior: false,
577
+ run: std => createUmlNode(std, 'terminate'),
578
+ },
579
+ {
580
+ // A composite state, as the container it is drawn as (§14.2.4) — and a
581
+ // BOUNDARY for the same reason the partition is one: what is IN a region is
582
+ // read back from geometry, never from a list. So it closes its kind's
583
+ // elements, just before the transition tool.
584
+ id: 'addRegion',
585
+ label: 'Region',
586
+ iconKey: 'uml.region',
587
+ kind: 'artefact',
588
+ category: 'state-machine-diagram',
589
+ element: 'boundary:region',
590
+ senior: false,
591
+ run: createUmlRegion,
592
+ },
593
+ {
594
+ id: 'transitionTool',
595
+ label: 'Transition',
596
+ iconKey: 'uml.transition',
597
+ kind: 'tool',
598
+ category: 'state-machine-diagram',
599
+ element: 'connector:transition',
600
+ senior: false,
601
+ run: std => activateUmlEdge(std, 'transition'),
602
+ },
603
+ /* ── Sequence diagram (§17.2.4, §17.4.4, §17.6.4, §17.7.4) ──────────── */
604
+ {
605
+ id: 'addLifeline',
606
+ label: 'Lifeline',
607
+ iconKey: 'uml.lifeline',
608
+ kind: 'artefact',
609
+ category: 'sequence-diagram',
610
+ element: 'node:lifeline',
611
+ senior: false,
612
+ run: std => createUmlNode(std, 'lifeline'),
613
+ },
614
+ {
615
+ id: 'addExecution',
616
+ label: 'Execution',
617
+ iconKey: 'uml.execution',
618
+ kind: 'artefact',
619
+ category: 'sequence-diagram',
620
+ element: 'node:execution',
621
+ senior: false,
622
+ run: std => createUmlNode(std, 'execution'),
623
+ },
624
+ {
625
+ id: 'addDestruction',
626
+ label: 'Destruction',
627
+ iconKey: 'uml.destruction',
628
+ kind: 'artefact',
629
+ category: 'sequence-diagram',
630
+ element: 'node:destruction',
631
+ senior: false,
632
+ run: std => createUmlNode(std, 'destruction'),
633
+ },
634
+ {
635
+ // A combined fragment is a BOUNDARY for the reason the partition and the
636
+ // region are: it is neither an element of the model nor the sheet the model
637
+ // is drawn on, it is a box drawn ROUND part of the drawing, and what is
638
+ // inside it is read back from where things sit (§17.6.4). So it and the
639
+ // interaction use close the sequence diagram's elements, before its five
640
+ // message tools.
641
+ id: 'addFragment',
642
+ label: 'Combined fragment',
643
+ iconKey: 'uml.fragment',
644
+ kind: 'artefact',
645
+ category: 'sequence-diagram',
646
+ element: 'boundary:fragment',
647
+ senior: false,
648
+ // Wrapped rather than passed by reference, the same call `uml.addDiagram`
649
+ // makes: `run` is called `(std, invocation)` and `createUmlFragment` takes
650
+ // `(std, operator = 'alt')`, so handing it over bare would make the
651
+ // invocation object the fragment's OPERATOR.
652
+ run: std => createUmlFragment(std),
653
+ },
654
+ {
655
+ // The same element with `ref` in its tag (§17.7.4) — a command of its own
656
+ // because it is a different modelling act: pointing at another diagram
657
+ // rather than branching this one. See `createUmlInteractionUse`.
658
+ id: 'addInteractionUse',
659
+ label: 'Interaction use',
660
+ iconKey: 'uml.interaction-use',
661
+ kind: 'artefact',
662
+ category: 'sequence-diagram',
663
+ element: 'boundary:interaction-use',
664
+ senior: false,
665
+ run: createUmlInteractionUse,
666
+ },
667
+ {
668
+ id: 'messageSyncTool',
669
+ label: 'Synchronous message',
670
+ iconKey: 'uml.message-sync',
671
+ kind: 'tool',
672
+ category: 'sequence-diagram',
673
+ element: 'connector:message-sync',
674
+ senior: false,
675
+ run: std => activateUmlEdge(std, 'message-sync'),
676
+ },
677
+ {
678
+ id: 'messageAsyncTool',
679
+ label: 'Asynchronous message',
680
+ iconKey: 'uml.message-async',
681
+ kind: 'tool',
682
+ category: 'sequence-diagram',
683
+ element: 'connector:message-async',
684
+ senior: false,
685
+ run: std => activateUmlEdge(std, 'message-async'),
686
+ },
687
+ {
688
+ id: 'messageReplyTool',
689
+ label: 'Reply message',
690
+ iconKey: 'uml.message-reply',
691
+ kind: 'tool',
692
+ category: 'sequence-diagram',
693
+ element: 'connector:message-reply',
694
+ senior: false,
695
+ run: std => activateUmlEdge(std, 'message-reply'),
696
+ },
697
+ {
698
+ id: 'messageCreateTool',
699
+ label: 'Create message',
700
+ iconKey: 'uml.message-create',
701
+ kind: 'tool',
702
+ category: 'sequence-diagram',
703
+ element: 'connector:message-create',
704
+ senior: false,
705
+ run: std => activateUmlEdge(std, 'message-create'),
706
+ },
707
+ {
708
+ id: 'messageDeleteTool',
709
+ label: 'Delete message',
710
+ iconKey: 'uml.message-delete',
711
+ kind: 'tool',
712
+ category: 'sequence-diagram',
713
+ element: 'connector:message-delete',
714
+ senior: false,
715
+ run: std => activateUmlEdge(std, 'message-delete'),
716
+ },
717
+ ];
718
+ /**
719
+ * The authored slot the first INTERCHANGE command sits in — straight after the
720
+ * last artefact, with no gap anywhere before it.
721
+ *
722
+ * A named constant rather than five literals because six declarations three
723
+ * hundred lines apart have to agree about it: the toolbox's own `order`, and
724
+ * the two exports and three imports that follow it. The header says why the
725
+ * whole block is last.
726
+ */
727
+ const UML_INTERCHANGE_ORDER = SPECS.length;
728
+ /**
729
+ * `{ legend }` when the entry owes a row, `{}` when it does not — the frame and
730
+ * the interaction use are the two that do not. See `legend.ts`.
731
+ */
732
+ function legendOf(spec) {
733
+ const entry = umlLegendEntry(spec.element);
734
+ return entry ? { legend: entry } : {};
735
+ }
736
+ const toolboxCommands = SPECS.map((spec, index) => ({
737
+ id: `uml.${spec.id}`,
738
+ owner: 'uml',
739
+ kind: spec.kind,
740
+ labelKey: `com.labre.commands.uml.${spec.id}`,
741
+ labelFallback: spec.label,
742
+ category: spec.category,
743
+ iconKey: spec.iconKey,
744
+ surfaces: spec.senior
745
+ ? ['senior-menu', 'catalogue', 'palette', 'agent']
746
+ : ['catalogue', 'palette', 'agent'],
747
+ // Authored order, with no gap in it: the artefacts run 0…SPECS.length-1 and
748
+ // the interchange block starts at {@link UML_INTERCHANGE_ORDER}. The gap that
749
+ // used to reserve slot 1 for the XMI import is gone with the reason for it.
750
+ order: index,
751
+ scope: 'edgeless',
752
+ // Keyless by intent, and at sixty-nine commands there is no chord alphabet
753
+ // that would not be arbitrary — still bindable from Settings › Shortcuts,
754
+ // which is what `toShortcutDescriptor` being total buys.
755
+ defaultKeys: { mac: [], other: [] },
756
+ availability: 'always',
757
+ run: spec.run,
758
+ telemetry: { framework: 'uml', element: spec.element, board: spec.board },
759
+ // The legend row this entry's artefact puts on the frame it is drawn on —
760
+ // derived from the very `element` value above, so the sixty-two rows of the
761
+ // old table are sixty-two commands that cannot fall out of step with it. The
762
+ // frame declares the BOX instead of a row: a legend must not list the paper.
763
+ ...(spec.board ? { legendBox: UML_LEGEND_BOX } : legendOf(spec)),
764
+ }));
765
+ /**
766
+ * The two EXPORTS — the commands whose subject is a whole DIAGRAM.
767
+ *
768
+ * ## Two formats, two commands, and neither implies the other
769
+ *
770
+ * ADR 0012 declares interchange per framework × format × direction, so PlantUML
771
+ * and XMI are two rows rather than one row with an option. They are also two
772
+ * different promises: XMI 2.5.1 is the OMG's own interchange format and carries
773
+ * the model (classifiers, their attributes and operations, the typed ends of
774
+ * every relationship); PlantUML carries the same model as a source a human
775
+ * reads and a renderer redraws. Both are `tier: 'semantic'` — neither is a
776
+ * picture — and `interchange.ts` is where that is declared.
777
+ *
778
+ * ## Why the selection is the scope
779
+ *
780
+ * A UML diagram frame is ONE diagram — a class diagram, a use-case diagram —
781
+ * and the point of drawing three side by side is that they are three diagrams.
782
+ * Merging them would produce a document neither format can express (a `@startuml`
783
+ * has one title; an XMI `uml:Model` one root), so the selection decides: one
784
+ * frame, one document; several frames, several documents in one file.
785
+ *
786
+ * ## Surfaces
787
+ *
788
+ * Both decline `'senior-menu'`: the sub-menu is what you reach for to DRAW
789
+ * something, and an export draws nothing — and UML is already past the cap, so
790
+ * a slot spent here is a slot taken from an artefact. They keep `'catalogue'`,
791
+ * which is not a category claim but the registry's own invariant: the catalogue
792
+ * is the TOTAL surface.
793
+ *
794
+ * ## The section they are filed under
795
+ *
796
+ * `interchange`, which is where BPMN and Wardley already file theirs and which
797
+ * is therefore the section a host that translated the header once has already
798
+ * translated. It is also the one section {@link Spec.category} did NOT re-file
799
+ * by diagram kind on 2026-09-16, and could not have: an XMI file is not a kind
800
+ * of diagram, it is what a diagram of any kind is written to.
801
+ *
802
+ * UML shipped them under `diagrams` — the section its FRAME was filed under —
803
+ * and the consequence was the one the PO's recette of 2026-09-14 reported:
804
+ * `groupCommandsByCategory` orders sections by the order their category is
805
+ * first MET, so five interchange commands sat inside the first section of the
806
+ * catalogue, above every artefact the framework draws. Their own section,
807
+ * authored last, puts them where the other two frameworks put theirs.
808
+ *
809
+ * On the frame's own row they sit in the "⋮" rather than as buttons, and in the
810
+ * ALWAYS-ON toolbar module — the entry hides itself when the command is absent
811
+ * from the registry (the `when` guard every `commandAction` carries), so with
812
+ * the UML tooling flag off the row is the resize toggle alone and nothing on it
813
+ * can be clicked into a no-op. See `toolbar/config.ts`.
814
+ */
815
+ const exportCommands = [
816
+ {
817
+ id: 'uml.exportPlantuml',
818
+ owner: 'uml',
819
+ kind: 'action',
820
+ labelKey: 'com.labre.commands.uml.exportPlantuml',
821
+ labelFallback: 'Export as PlantUML',
822
+ descriptionKey: 'com.labre.commands.uml.exportPlantuml.description',
823
+ descriptionFallback: 'Download the selected diagram as PlantUML source, ready to paste into any PlantUML renderer.',
824
+ category: 'interchange',
825
+ iconKey: 'uml.export-plantuml',
826
+ surfaces: ['catalogue', 'contextual-toolbar', 'palette', 'agent'],
827
+ // First of the interchange block, which is first of the EXPORTS: the PO's
828
+ // reading of 2026-09-14 puts what leaves the board before what arrives on
829
+ // an empty one.
830
+ order: UML_INTERCHANGE_ORDER,
831
+ scope: 'edgeless',
832
+ defaultKeys: { mac: [], other: [] },
833
+ availability: 'selection',
834
+ run: exportUmlPlantumlFile,
835
+ telemetry: { framework: 'uml', element: 'board:export-plantuml' },
836
+ // A diagram in the selection, and no more than that: an export READS, so
837
+ // it is offered on a read-only document — which is precisely the diagram
838
+ // somebody wants to take away.
839
+ when: std => umlDiagramsForExport(std).length > 0,
840
+ },
841
+ {
842
+ id: 'uml.exportXmi',
843
+ owner: 'uml',
844
+ kind: 'action',
845
+ labelKey: 'com.labre.commands.uml.exportXmi',
846
+ labelFallback: 'Export as XMI',
847
+ descriptionKey: 'com.labre.commands.uml.exportXmi.description',
848
+ descriptionFallback: 'Download the selected diagram as XMI 2.5.1, the OMG interchange format every UML tool reads.',
849
+ category: 'interchange',
850
+ iconKey: 'uml.export-xmi',
851
+ surfaces: ['catalogue', 'contextual-toolbar', 'palette', 'agent'],
852
+ order: UML_INTERCHANGE_ORDER + 1,
853
+ scope: 'edgeless',
854
+ defaultKeys: { mac: [], other: [] },
855
+ availability: 'selection',
856
+ run: exportUmlXmiFile,
857
+ telemetry: { framework: 'uml', element: 'board:export-xmi' },
858
+ when: std => umlDiagramsForExport(std).length > 0,
859
+ },
860
+ ];
861
+ /**
862
+ * The three IMPORTS — where a UML board comes FROM (`docs/adr/0019`).
863
+ *
864
+ * ## Three rows, because the unit of declaration is the triple
865
+ *
866
+ * ADR 0012 declares interchange per framework × format × direction, so three
867
+ * formats are three commands and never one command with a format picker. They
868
+ * are also three different PROMISES, and the promise is made by each command's
869
+ * own label and description before the picker opens (P2): PlantUML and XMI
870
+ * carry a MODEL and take the whole preservation contract; draw.io carries a
871
+ * DRAWING, and every UML fact read out of it is a guess made from a style
872
+ * string. A picker with three entries behind one button would hide exactly that
873
+ * difference at exactly the moment it matters.
874
+ *
875
+ * ## One nomination, and it cost the note its seat
876
+ *
877
+ * R5 puts an import in the senior sub-menu — the same ruling of 2026-08-28 that
878
+ * nominated `bpmn.importXml`: a board comes from a file, and the sub-menu is
879
+ * the first thing a user opens on an empty canvas. The budget is
880
+ * `SENIOR_MENU_CAP` nominations plus the single over-nomination the PO
881
+ * authorized that day, which BPMN has spent — so UML's fifteenth nomination
882
+ * would break `registry.unit.spec.ts` rather than merely crowd the row.
883
+ *
884
+ * So exactly ONE of the three takes the seat, and it is XMI: it is the OMG's
885
+ * own interchange format, it is the one every modelling tool writes, and it is
886
+ * the one that re-imports what this pack exports. The other two sit one click
887
+ * away in the catalogue behind "More artefacts…". `uml.addNote` is the entry
888
+ * that stood down for it, with the reasoning recorded at its declaration — a
889
+ * PO curation point, flagged there rather than settled here.
890
+ *
891
+ * The seat is a nomination and not a cold-start button: authored last, it is
892
+ * the fourteenth of fourteen nominations and the row renders thirteen, so it
893
+ * appears the first time the user reaches for it (ADR 0014 § R3) and not
894
+ * before. The header states the arbitration that put it there.
895
+ */
896
+ const importCommands = [
897
+ {
898
+ id: 'uml.importXmi',
899
+ owner: 'uml',
900
+ kind: 'action',
901
+ labelKey: 'com.labre.commands.uml.importXmi',
902
+ labelFallback: 'Import XMI',
903
+ descriptionKey: 'com.labre.commands.uml.importXmi.description',
904
+ descriptionFallback: 'Open an XMI 2.5.1 model as a diagram. What Labre cannot draw is kept in the document, and the import says what it was.',
905
+ category: 'interchange',
906
+ iconKey: 'uml.import-xmi',
907
+ // The one nominated import — see the header. Not the contextual toolbar: a
908
+ // contextual toolbar is a statement about a SELECTION, and the moment this
909
+ // is most wanted is on a board with nothing on it.
910
+ surfaces: ['senior-menu', 'catalogue', 'palette', 'agent'],
911
+ // Authored LAST among the nominations, which is what makes it the one of
912
+ // the fourteen an overflowed row's thirteen slots leave out until somebody
913
+ // uses it (ADR 0014 § R3). The alternative — the second slot it held from
914
+ // tranche G until the PO's recette of 2026-09-14 — buys the cold row at the
915
+ // price of the catalogue, because one `order` serves both surfaces. See the
916
+ // header.
917
+ order: UML_INTERCHANGE_ORDER + 2,
918
+ scope: 'edgeless',
919
+ defaultKeys: { mac: [], other: [] },
920
+ // An import WRITES, so a read-only document is one it cannot run on and the
921
+ // declaration says so. `'always'` would light the entry on a read-only
922
+ // board, do nothing when clicked, and put the same untruth into the
923
+ // serializable manifest a host reads. Nothing has to be SELECTED — which is
924
+ // the mirror image of the exports beside it — so there is no `when`.
925
+ availability: 'editable',
926
+ run: importUmlXmiFile,
927
+ // `board:` and not `diagram:`: an export names the frame whose toolbar
928
+ // launched it, and an import is launched with no frame anywhere.
929
+ telemetry: { framework: 'uml', element: 'board:import-xmi' },
930
+ },
931
+ {
932
+ id: 'uml.importPlantuml',
933
+ owner: 'uml',
934
+ kind: 'action',
935
+ labelKey: 'com.labre.commands.uml.importPlantuml',
936
+ labelFallback: 'Import PlantUML',
937
+ descriptionKey: 'com.labre.commands.uml.importPlantuml.description',
938
+ descriptionFallback: 'Open a PlantUML source as a diagram. Lines Labre cannot read are kept in the document, and the import says which.',
939
+ category: 'interchange',
940
+ iconKey: 'uml.import-plantuml',
941
+ surfaces: ['catalogue', 'palette', 'agent'],
942
+ order: UML_INTERCHANGE_ORDER + 3,
943
+ scope: 'edgeless',
944
+ defaultKeys: { mac: [], other: [] },
945
+ availability: 'editable',
946
+ run: importUmlPlantumlFile,
947
+ telemetry: { framework: 'uml', element: 'board:import-plantuml' },
948
+ },
949
+ {
950
+ id: 'uml.importDrawio',
951
+ owner: 'uml',
952
+ kind: 'action',
953
+ labelKey: 'com.labre.commands.uml.importDrawio',
954
+ labelFallback: 'Import draw.io drawing',
955
+ descriptionKey: 'com.labre.commands.uml.importDrawio.description',
956
+ descriptionFallback: 'Recognise a draw.io drawing as UML, best effort: the shapes and arrows it understands become a diagram, and it says what it could not read.',
957
+ category: 'interchange',
958
+ iconKey: 'uml.import-drawio',
959
+ surfaces: ['catalogue', 'palette', 'agent'],
960
+ order: UML_INTERCHANGE_ORDER + 4,
961
+ scope: 'edgeless',
962
+ defaultKeys: { mac: [], other: [] },
963
+ availability: 'editable',
964
+ run: importUmlDrawioFile,
965
+ telemetry: { framework: 'uml', element: 'board:import-drawio' },
966
+ },
967
+ ];
968
+ export const umlCommands = [
969
+ ...toolboxCommands,
970
+ ...exportCommands,
971
+ ...importCommands,
972
+ ];
973
+ export const umlCommandIcons = {
974
+ ...UML_TOOLBOX_ICONS,
975
+ // The senior BUTTON's glyph, registered so `FrameworkDescriptor.iconKey`
976
+ // resolves through `getCommandIcon` (rule R1). No command names it.
977
+ 'uml.toolbar': umlToolbarIcon,
978
+ 'uml.export-plantuml': umlExportPlantumlIcon,
979
+ 'uml.export-xmi': umlExportXmiIcon,
980
+ 'uml.import-plantuml': umlImportPlantumlIcon,
981
+ 'uml.import-xmi': umlImportXmiIcon,
982
+ 'uml.import-drawio': umlImportDrawioIcon,
983
+ };