@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.
- package/LICENSE +384 -0
- package/dist/actions.d.ts +430 -0
- package/dist/actions.js +724 -0
- package/dist/background.d.ts +153 -0
- package/dist/background.js +557 -0
- package/dist/board-hit.d.ts +137 -0
- package/dist/board-hit.js +191 -0
- package/dist/commands-manifest.d.ts +19 -0
- package/dist/commands-manifest.js +571 -0
- package/dist/commands.d.ts +4 -0
- package/dist/commands.js +983 -0
- package/dist/component.d.ts +521 -0
- package/dist/component.js +708 -0
- package/dist/consts.d.ts +365 -0
- package/dist/consts.js +463 -0
- package/dist/descriptor.d.ts +12 -0
- package/dist/descriptor.js +10 -0
- package/dist/drawio-decode.d.ts +32 -0
- package/dist/drawio-decode.js +96 -0
- package/dist/drawio-import.d.ts +133 -0
- package/dist/drawio-import.js +885 -0
- package/dist/edge-morph.d.ts +154 -0
- package/dist/edge-morph.js +153 -0
- package/dist/edge-styles.d.ts +78 -0
- package/dist/edge-styles.js +210 -0
- package/dist/effects.d.ts +9 -0
- package/dist/effects.js +6 -0
- package/dist/element-renderer.d.ts +64 -0
- package/dist/element-renderer.js +114 -0
- package/dist/element-view.d.ts +234 -0
- package/dist/element-view.js +430 -0
- package/dist/export.d.ts +61 -0
- package/dist/export.js +41 -0
- package/dist/filename.d.ts +17 -0
- package/dist/filename.js +26 -0
- package/dist/frame-tag.d.ts +70 -0
- package/dist/frame-tag.js +78 -0
- package/dist/grammar.d.ts +695 -0
- package/dist/grammar.js +1436 -0
- package/dist/import.d.ts +306 -0
- package/dist/import.js +1270 -0
- package/dist/index.d.ts +47 -0
- package/dist/index.js +67 -0
- package/dist/interchange.d.ts +125 -0
- package/dist/interchange.js +372 -0
- package/dist/keywords.d.ts +204 -0
- package/dist/keywords.js +361 -0
- package/dist/kinds.d.ts +73 -0
- package/dist/kinds.js +91 -0
- package/dist/legend.d.ts +30 -0
- package/dist/legend.js +255 -0
- package/dist/model.d.ts +862 -0
- package/dist/model.js +1316 -0
- package/dist/morph.d.ts +178 -0
- package/dist/morph.js +496 -0
- package/dist/node/act-glyphs.d.ts +30 -0
- package/dist/node/act-glyphs.js +142 -0
- package/dist/node/compartment-watcher.d.ts +107 -0
- package/dist/node/compartment-watcher.js +163 -0
- package/dist/node/fit.d.ts +85 -0
- package/dist/node/fit.js +129 -0
- package/dist/node/node-renderer.d.ts +49 -0
- package/dist/node/node-renderer.js +611 -0
- package/dist/node/node-view.d.ts +51 -0
- package/dist/node/node-view.js +89 -0
- package/dist/node/paint.d.ts +84 -0
- package/dist/node/paint.js +143 -0
- package/dist/node/sd-glyphs.d.ts +36 -0
- package/dist/node/sd-glyphs.js +79 -0
- package/dist/node/stm-glyphs.d.ts +22 -0
- package/dist/node/stm-glyphs.js +111 -0
- package/dist/node/tier-metrics.d.ts +38 -0
- package/dist/node/tier-metrics.js +45 -0
- package/dist/operators.d.ts +45 -0
- package/dist/operators.js +75 -0
- package/dist/plantuml-import.d.ts +113 -0
- package/dist/plantuml-import.js +1723 -0
- package/dist/plantuml.d.ts +94 -0
- package/dist/plantuml.js +985 -0
- package/dist/presets.d.ts +149 -0
- package/dist/presets.js +302 -0
- package/dist/profiles.d.ts +2 -0
- package/dist/profiles.js +358 -0
- package/dist/reading.d.ts +160 -0
- package/dist/reading.js +648 -0
- package/dist/roles.d.ts +257 -0
- package/dist/roles.js +997 -0
- package/dist/rules.d.ts +267 -0
- package/dist/rules.js +3090 -0
- package/dist/templates.d.ts +2 -0
- package/dist/templates.js +197 -0
- package/dist/toolbar/config.d.ts +228 -0
- package/dist/toolbar/config.js +833 -0
- package/dist/toolbar/icons.d.ts +440 -0
- package/dist/toolbar/icons.js +711 -0
- package/dist/toolbar/senior-tool.d.ts +11 -0
- package/dist/toolbar/senior-tool.js +21 -0
- package/dist/toolbar/uml-menu.d.ts +17 -0
- package/dist/toolbar/uml-menu.js +20 -0
- package/dist/toolbar/uml-senior-button.d.ts +18 -0
- package/dist/toolbar/uml-senior-button.js +22 -0
- package/dist/translations.d.ts +27 -0
- package/dist/translations.js +117 -0
- package/dist/view.d.ts +33 -0
- package/dist/view.js +236 -0
- package/dist/xmi-import.d.ts +174 -0
- package/dist/xmi-import.js +2268 -0
- package/dist/xmi.d.ts +61 -0
- package/dist/xmi.js +1860 -0
- package/dist/xml-reader.d.ts +157 -0
- package/dist/xml-reader.js +430 -0
- package/dist/xml.d.ts +72 -0
- package/dist/xml.js +96 -0
- package/package.json +38 -0
package/dist/commands.js
ADDED
|
@@ -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
|
+
};
|