@miragon/modeler-shell 0.0.0-stage → 0.1.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 (126) hide show
  1. package/API.md +1902 -0
  2. package/LICENSE +21 -0
  3. package/README.md +435 -2
  4. package/THIRD_PARTY_NOTICES.md +52 -0
  5. package/dist/appearance-D3FUcbu6.d.cts +53 -0
  6. package/dist/appearance-D3FUcbu6.d.ts +53 -0
  7. package/dist/chunk-5JSZ4VGA.js +551 -0
  8. package/dist/chunk-5JSZ4VGA.js.map +1 -0
  9. package/dist/chunk-6LQOFACK.js +1576 -0
  10. package/dist/chunk-6LQOFACK.js.map +1 -0
  11. package/dist/chunk-N4QXFF3A.js +23 -0
  12. package/dist/chunk-N4QXFF3A.js.map +1 -0
  13. package/dist/chunk-RJRFR6N3.js +136 -0
  14. package/dist/chunk-RJRFR6N3.js.map +1 -0
  15. package/dist/core.cjs +619 -0
  16. package/dist/core.cjs.map +1 -0
  17. package/dist/core.d.cts +171 -0
  18. package/dist/core.d.ts +171 -0
  19. package/dist/core.js +91 -0
  20. package/dist/core.js.map +1 -0
  21. package/dist/diagram-js.cjs +1115 -0
  22. package/dist/diagram-js.cjs.map +1 -0
  23. package/dist/diagram-js.d.cts +163 -0
  24. package/dist/diagram-js.d.ts +163 -0
  25. package/dist/diagram-js.js +867 -0
  26. package/dist/diagram-js.js.map +1 -0
  27. package/dist/index.cjs +2175 -0
  28. package/dist/index.cjs.map +1 -0
  29. package/dist/index.d.cts +20 -0
  30. package/dist/index.d.ts +20 -0
  31. package/dist/index.js +103 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/keymap-CObQE6q6.d.cts +151 -0
  34. package/dist/keymap-DORzHnK-.d.ts +151 -0
  35. package/dist/protocol-DhYBakWL.d.cts +15 -0
  36. package/dist/protocol-IeW8bf4f.d.ts +15 -0
  37. package/dist/shell.css +914 -0
  38. package/dist/testing-vscode-stub.cjs +332 -0
  39. package/dist/testing-vscode-stub.cjs.map +1 -0
  40. package/dist/testing-vscode-stub.d.cts +65 -0
  41. package/dist/testing-vscode-stub.d.ts +65 -0
  42. package/dist/testing-vscode-stub.js +154 -0
  43. package/dist/testing-vscode-stub.js.map +1 -0
  44. package/dist/testing.cjs +2364 -0
  45. package/dist/testing.cjs.map +1 -0
  46. package/dist/testing.d.cts +165 -0
  47. package/dist/testing.d.ts +165 -0
  48. package/dist/testing.js +2297 -0
  49. package/dist/testing.js.map +1 -0
  50. package/dist/types-B6NfkiUG.d.cts +66 -0
  51. package/dist/types-B6NfkiUG.d.ts +66 -0
  52. package/dist/types-DnZn9CPw.d.ts +284 -0
  53. package/dist/types-OuFlqZp0.d.cts +284 -0
  54. package/dist/vscode-extension.cjs +215 -0
  55. package/dist/vscode-extension.cjs.map +1 -0
  56. package/dist/vscode-extension.d.cts +103 -0
  57. package/dist/vscode-extension.d.ts +103 -0
  58. package/dist/vscode-extension.js +24 -0
  59. package/dist/vscode-extension.js.map +1 -0
  60. package/dist/vscode-webview.cjs +2198 -0
  61. package/dist/vscode-webview.cjs.map +1 -0
  62. package/dist/vscode-webview.d.cts +82 -0
  63. package/dist/vscode-webview.d.ts +82 -0
  64. package/dist/vscode-webview.js +250 -0
  65. package/dist/vscode-webview.js.map +1 -0
  66. package/package.json +145 -4
  67. package/src/core/appearance.ts +104 -0
  68. package/src/core/fit.ts +205 -0
  69. package/src/core/index.ts +84 -0
  70. package/src/core/keymap.ts +275 -0
  71. package/src/core/layout.ts +69 -0
  72. package/src/core/messages.ts +88 -0
  73. package/src/core/sizes.ts +85 -0
  74. package/src/core/types.ts +84 -0
  75. package/src/core/zoom.ts +74 -0
  76. package/src/core.ts +2 -0
  77. package/src/diagram-js/connect.ts +75 -0
  78. package/src/diagram-js/index.ts +16 -0
  79. package/src/diagram-js/module.ts +20 -0
  80. package/src/diagram-js/palette-skin.ts +267 -0
  81. package/src/diagram-js/palette.css +269 -0
  82. package/src/diagram-js/service.ts +240 -0
  83. package/src/diagram-js/types.ts +67 -0
  84. package/src/diagram-js/viewport.ts +564 -0
  85. package/src/diagram-js.ts +2 -0
  86. package/src/dom/element.ts +51 -0
  87. package/src/dom/hint.ts +51 -0
  88. package/src/dom/icons.ts +309 -0
  89. package/src/dom/index.ts +29 -0
  90. package/src/dom/inspector.ts +35 -0
  91. package/src/dom/keys.ts +59 -0
  92. package/src/dom/ledger.ts +87 -0
  93. package/src/dom/legend.ts +85 -0
  94. package/src/dom/menu.ts +393 -0
  95. package/src/dom/mount.ts +461 -0
  96. package/src/dom/problems.ts +72 -0
  97. package/src/dom/shell-dom.css +966 -0
  98. package/src/dom/status.ts +72 -0
  99. package/src/dom/tooltip.ts +237 -0
  100. package/src/dom/types.ts +277 -0
  101. package/src/dom/zoom-panel.ts +154 -0
  102. package/src/index.ts +3 -0
  103. package/src/shell.css +6 -0
  104. package/src/testing/cases.ts +1012 -0
  105. package/src/testing/checks.ts +832 -0
  106. package/src/testing/facts.ts +587 -0
  107. package/src/testing/geometry.ts +472 -0
  108. package/src/testing/harness.ts +138 -0
  109. package/src/testing/index.ts +15 -0
  110. package/src/testing/page-tools.ts +434 -0
  111. package/src/testing/page.ts +17 -0
  112. package/src/testing/vscode-stub.ts +237 -0
  113. package/src/testing-vscode-stub.ts +2 -0
  114. package/src/testing.ts +2 -0
  115. package/src/vscode/extension/html.ts +107 -0
  116. package/src/vscode/extension/index.ts +8 -0
  117. package/src/vscode/extension/reveal.ts +9 -0
  118. package/src/vscode/extension/theme-bridge.ts +85 -0
  119. package/src/vscode/extension/types.ts +36 -0
  120. package/src/vscode/protocol.ts +31 -0
  121. package/src/vscode/webview/appearance.ts +141 -0
  122. package/src/vscode/webview/index.ts +8 -0
  123. package/src/vscode/webview/mount.ts +76 -0
  124. package/src/vscode/webview/state.ts +116 -0
  125. package/src/vscode-extension.ts +2 -0
  126. package/src/vscode-webview.ts +2 -0
package/dist/index.cjs ADDED
@@ -0,0 +1,2175 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var src_exports = {};
22
+ __export(src_exports, {
23
+ CHROME_ISLANDS: () => CHROME_ISLANDS,
24
+ FIT_ANIMATION_MS: () => FIT_ANIMATION_MS,
25
+ ICONS: () => ICONS,
26
+ INVALID_STATUS_DELAY_MS: () => INVALID_STATUS_DELAY_MS,
27
+ MAX_FIT_ZOOM: () => MAX_FIT_ZOOM,
28
+ PALETTE_CLEARANCE: () => PALETTE_CLEARANCE,
29
+ PALETTE_TOOL_ICON: () => PALETTE_TOOL_ICON,
30
+ REDUCED_MOTION_CLASS: () => REDUCED_MOTION_CLASS,
31
+ SHELL: () => SHELL,
32
+ SHELL_TOKENS: () => SHELL_TOKENS,
33
+ TEXT_FIELD_SELECTOR: () => TEXT_FIELD_SELECTOR,
34
+ TOOLTIP_DELAY_MS: () => TOOLTIP_DELAY_MS,
35
+ VIEW_TOLERANCE: () => VIEW_TOLERANCE,
36
+ ZOOM_MAX: () => ZOOM_MAX,
37
+ ZOOM_MIN: () => ZOOM_MIN,
38
+ appearanceAttributes: () => appearanceAttributes,
39
+ appearanceFromBodyClasses: () => appearanceFromBodyClasses,
40
+ appearanceFromColorThemeKind: () => appearanceFromColorThemeKind,
41
+ ariaKeyShortcuts: () => ariaKeyShortcuts,
42
+ ariaKeyShortcutsFromLabel: () => ariaKeyShortcutsFromLabel,
43
+ canZoomIn: () => canZoomIn,
44
+ canZoomOut: () => canZoomOut,
45
+ decideKey: () => decideKey,
46
+ decidePaletteRow: () => decidePaletteRow,
47
+ defaultMessages: () => defaultMessages,
48
+ detectPlatform: () => detectPlatform,
49
+ easeStandard: () => easeStandard,
50
+ fitInsets: () => fitInsets,
51
+ fitViewbox: () => fitViewbox,
52
+ formatNumber: () => formatNumber,
53
+ formatPercent: () => formatPercent,
54
+ freeRectCenter: () => freeRectCenter,
55
+ iconElement: () => iconElement,
56
+ iconMarkup: () => iconMarkup,
57
+ interpolateViewbox: () => interpolateViewbox,
58
+ isUndoRedoKey: () => isUndoRedoKey,
59
+ legendStartsOpen: () => legendStartsOpen,
60
+ matchChromeKey: () => matchChromeKey,
61
+ mountShell: () => mountShell,
62
+ placeTooltip: () => placeTooltip,
63
+ reducedMotionFromBodyClasses: () => reducedMotionFromBodyClasses,
64
+ requestedAppearance: () => requestedAppearance,
65
+ resolveAppearance: () => resolveAppearance,
66
+ resolveMessages: () => resolveMessages,
67
+ sameView: () => sameView,
68
+ shellSizesFromTokens: () => shellSizesFromTokens,
69
+ shortcutLabel: () => shortcutLabel,
70
+ zoomReadout: () => zoomReadout
71
+ });
72
+ module.exports = __toCommonJS(src_exports);
73
+
74
+ // src/core/types.ts
75
+ var CHROME_ISLANDS = [
76
+ "menu",
77
+ "palette",
78
+ "primary",
79
+ "zoom",
80
+ "problems",
81
+ "legend",
82
+ "inspector",
83
+ "status"
84
+ ];
85
+
86
+ // src/core/sizes.ts
87
+ var SHELL_TOKENS = {
88
+ edge: "--cd-shell-rand",
89
+ gap: "--cd-shell-luecke",
90
+ row: "--cd-shell-zeile",
91
+ target: "--cd-shell-ziel",
92
+ entry: "--cd-shell-eintrag",
93
+ free: "--cd-shell-frei",
94
+ menuMinWidth: "--cd-shell-menue-breite",
95
+ inspectorWidth: "--cd-shell-inspector-breite",
96
+ fitInsetTop: "--cd-shell-einpassen-oben",
97
+ fitInsetTopSecondRow: "--cd-shell-einpassen-oben-schmal",
98
+ fitInsetSide: "--cd-shell-einpassen-seite",
99
+ fitInsetBottom: "--cd-shell-einpassen-unten",
100
+ fitInsetBottomWeb: "--cd-shell-einpassen-unten-web",
101
+ narrow: "--cd-shell-schmal",
102
+ veryNarrow: "--cd-shell-sehr-schmal",
103
+ low: "--cd-shell-niedrig",
104
+ layerIslands: "--cd-shell-ebene-inseln",
105
+ layerInspector: "--cd-shell-ebene-inspector",
106
+ layerTopRow: "--cd-shell-ebene-reihe",
107
+ layerPopups: "--cd-shell-ebene-aufklapper",
108
+ layerDialog: "--cd-shell-ebene-dialog",
109
+ layerToast: "--cd-shell-ebene-toast"
110
+ };
111
+ var SHELL = {
112
+ edge: 12,
113
+ gap: 8,
114
+ row: 40,
115
+ target: 32,
116
+ entry: 36,
117
+ free: 64,
118
+ menuMinWidth: 248,
119
+ inspectorWidth: 288,
120
+ fitInsetTop: 88,
121
+ fitInsetTopSecondRow: 140,
122
+ fitInsetSide: 24,
123
+ fitInsetBottom: 64,
124
+ fitInsetBottomWeb: 92,
125
+ narrow: 760,
126
+ veryNarrow: 440,
127
+ low: 820,
128
+ layerIslands: 20,
129
+ layerInspector: 25,
130
+ layerTopRow: 30,
131
+ layerPopups: 40,
132
+ layerDialog: 50,
133
+ layerToast: 60
134
+ };
135
+ var PIXEL_OR_PLAIN_NUMBER = /^\s*-?(?:\d+\.?\d*|\.\d+)(?:px)?\s*$/;
136
+ function shellSizesFromTokens(shellGroup) {
137
+ const sizes = {};
138
+ for (const name of Object.keys(SHELL_TOKENS)) {
139
+ const token = SHELL_TOKENS[name];
140
+ const raw = Object.hasOwn(shellGroup, token) ? shellGroup[token] : void 0;
141
+ if (raw === void 0) {
142
+ throw new Error(`@miragon/modeler-shell: the shell token ${token} is missing`);
143
+ }
144
+ if (typeof raw !== "string" || !PIXEL_OR_PLAIN_NUMBER.test(raw)) {
145
+ throw new Error(
146
+ `@miragon/modeler-shell: the shell token ${token} is not a number: ${JSON.stringify(raw)}`
147
+ );
148
+ }
149
+ sizes[name] = Number.parseFloat(raw);
150
+ }
151
+ return sizes;
152
+ }
153
+
154
+ // src/core/fit.ts
155
+ var MAX_FIT_ZOOM = 1;
156
+ var FIT_ANIMATION_MS = 250;
157
+ function fitViewbox(bounds, container, insets, maxZoom = MAX_FIT_ZOOM) {
158
+ if (!(container.width > 0 && container.height > 0)) return void 0;
159
+ const hasWidth = bounds.width > 0;
160
+ const hasHeight = bounds.height > 0;
161
+ if (!hasWidth && !hasHeight) return void 0;
162
+ const horizontal = freeExtent(container.width, insets.left, insets.right);
163
+ const vertical = freeExtent(container.height, insets.top, insets.bottom);
164
+ const contentWidth = hasWidth ? bounds.width : 0;
165
+ const contentHeight = hasHeight ? bounds.height : 0;
166
+ const scale = Math.min(
167
+ maxZoom,
168
+ hasWidth ? horizontal.extent / contentWidth : Infinity,
169
+ hasHeight ? vertical.extent / contentHeight : Infinity
170
+ );
171
+ const box = {
172
+ x: bounds.x + contentWidth / 2 - (horizontal.start + horizontal.extent / 2) / scale,
173
+ y: bounds.y + contentHeight / 2 - (vertical.start + vertical.extent / 2) / scale,
174
+ width: container.width / scale,
175
+ height: container.height / scale,
176
+ scale
177
+ };
178
+ return Object.values(box).every(Number.isFinite) && scale > 0 ? box : void 0;
179
+ }
180
+ function freeExtent(total, before, after) {
181
+ const extent = total - before - after;
182
+ return extent > 0 ? { start: before, extent } : { start: 0, extent: total };
183
+ }
184
+ function freeRectCenter(container, insets) {
185
+ if (!(container.width > 0 && container.height > 0)) return void 0;
186
+ const horizontal = freeExtent(container.width, insets.left, insets.right);
187
+ const vertical = freeExtent(container.height, insets.top, insets.bottom);
188
+ return {
189
+ x: horizontal.start + horizontal.extent / 2,
190
+ y: vertical.start + vertical.extent / 2
191
+ };
192
+ }
193
+ function fitInsets(layout) {
194
+ const { container, host, paletteRow, problemsVisible, legendWidth, inspector } = layout;
195
+ const insets = {
196
+ top: paletteRow === "second-row" ? SHELL.fitInsetTopSecondRow : SHELL.fitInsetTop,
197
+ right: SHELL.fitInsetSide,
198
+ bottom: host === "web" && problemsVisible ? SHELL.fitInsetBottomWeb : SHELL.fitInsetBottom,
199
+ left: SHELL.fitInsetSide
200
+ };
201
+ const bottomSheetOpen = inspector !== void 0 && container.width <= SHELL.narrow;
202
+ if (inspector !== void 0 && !bottomSheetOpen) {
203
+ insets.right = SHELL.edge + SHELL.inspectorWidth + SHELL.fitInsetSide;
204
+ }
205
+ if (bottomSheetOpen) {
206
+ insets.bottom = inspector.sheetTop === void 0 ? SHELL.fitInsetBottom : container.height - inspector.sheetTop + SHELL.edge;
207
+ } else if (legendWidth !== void 0 && legendWidth > 0 && container.width > SHELL.veryNarrow) {
208
+ insets.left = SHELL.edge + legendWidth + SHELL.fitInsetSide;
209
+ }
210
+ return insets;
211
+ }
212
+ var VIEW_TOLERANCE = 1e-3;
213
+ function sameView(a, b, tolerance = VIEW_TOLERANCE) {
214
+ const close = (first, second) => Math.abs(first - second) <= tolerance * Math.max(1, Math.abs(first), Math.abs(second));
215
+ return close(a.scale, b.scale) && close(a.x * a.scale, b.x * b.scale) && close(a.y * a.scale, b.y * b.scale);
216
+ }
217
+ function interpolateViewbox(from, to, progress) {
218
+ if (!(progress > 0)) return { ...from };
219
+ if (progress >= 1) return { ...to };
220
+ const linear = (start, end) => start + (end - start) * progress;
221
+ const scale = from.scale > 0 && to.scale > 0 ? Math.exp(linear(Math.log(from.scale), Math.log(to.scale))) : linear(from.scale, to.scale);
222
+ const centreX = linear(from.x + from.width / 2, to.x + to.width / 2);
223
+ const centreY = linear(from.y + from.height / 2, to.y + to.height / 2);
224
+ const width = linear(from.width * from.scale, to.width * to.scale) / scale;
225
+ const height = linear(from.height * from.scale, to.height * to.scale) / scale;
226
+ return { x: centreX - width / 2, y: centreY - height / 2, width, height, scale };
227
+ }
228
+ function easeStandard(progress) {
229
+ return STANDARD_EASING(progress);
230
+ }
231
+ var STANDARD_EASING = cubicBezier(0.4, 0, 0.2, 1);
232
+ function cubicBezier(x1, y1, x2, y2) {
233
+ const polynomial = (first, second) => {
234
+ const c = 3 * first;
235
+ const b = 3 * (second - first) - c;
236
+ const a = 1 - c - b;
237
+ return {
238
+ at: (t) => ((a * t + b) * t + c) * t,
239
+ slope: (t) => (3 * a * t + 2 * b) * t + c
240
+ };
241
+ };
242
+ const curveX = polynomial(x1, x2);
243
+ const curveY = polynomial(y1, y2);
244
+ const precision = 1e-7;
245
+ const parameterFor = (x) => {
246
+ let t = x;
247
+ for (let iteration = 0; iteration < 8; iteration++) {
248
+ const error = curveX.at(t) - x;
249
+ if (Math.abs(error) < precision) return t;
250
+ const slope = curveX.slope(t);
251
+ if (Math.abs(slope) < 1e-6) break;
252
+ t -= error / slope;
253
+ if (t < 0 || t > 1) break;
254
+ }
255
+ let low = 0;
256
+ let high = 1;
257
+ t = x;
258
+ for (let iteration = 0; iteration < 60; iteration++) {
259
+ const value = curveX.at(t);
260
+ if (Math.abs(value - x) < precision) break;
261
+ if (value < x) low = t;
262
+ else high = t;
263
+ t = (low + high) / 2;
264
+ }
265
+ return t;
266
+ };
267
+ return (progress) => {
268
+ if (!(progress > 0)) return 0;
269
+ if (progress >= 1) return 1;
270
+ return curveY.at(parameterFor(progress));
271
+ };
272
+ }
273
+
274
+ // src/core/zoom.ts
275
+ var ZOOM_MIN = 0.2;
276
+ var ZOOM_MAX = 4;
277
+ function canZoomOut(zoom) {
278
+ return zoom > ZOOM_MIN * 1.001;
279
+ }
280
+ function canZoomIn(zoom) {
281
+ return zoom < ZOOM_MAX * 0.999;
282
+ }
283
+ function formatPercent(zoom, lang) {
284
+ return numberFormat(lang, "percent").format(zoom);
285
+ }
286
+ function formatNumber(value, lang) {
287
+ return numberFormat(lang, "decimal").format(value);
288
+ }
289
+ var FALLBACK_LANG = "en";
290
+ var formats = /* @__PURE__ */ new Map();
291
+ function numberFormat(lang, style) {
292
+ const key = `${style}|${lang}`;
293
+ let format = formats.get(key);
294
+ if (!format) {
295
+ const options = style === "percent" ? { style, maximumFractionDigits: 0 } : { style };
296
+ try {
297
+ format = new Intl.NumberFormat(lang, options);
298
+ } catch {
299
+ format = new Intl.NumberFormat(FALLBACK_LANG, options);
300
+ }
301
+ formats.set(key, format);
302
+ }
303
+ return format;
304
+ }
305
+ function zoomReadout(zoom, lang, messages) {
306
+ const text = formatPercent(zoom, lang);
307
+ return {
308
+ text,
309
+ ariaLabel: messages.zoomToActualNow(formatPercent(1, lang), text),
310
+ canZoomOut: canZoomOut(zoom),
311
+ canZoomIn: canZoomIn(zoom)
312
+ };
313
+ }
314
+
315
+ // src/core/keymap.ts
316
+ function matchChromeKey(event) {
317
+ if (event.altKey) return void 0;
318
+ if (isCommand(event)) {
319
+ if (event.key === "+" || event.key === "=" || event.code === "NumpadAdd") return "zoomIn";
320
+ if (event.key === "-" || event.key === "_" || event.code === "NumpadSubtract") return "zoomOut";
321
+ if (event.key === "0" || event.code === "Numpad0") return "zoomToActual";
322
+ return void 0;
323
+ }
324
+ return event.shiftKey && event.code === "Digit1" ? "fit" : void 0;
325
+ }
326
+ function isCommand(event) {
327
+ return event.metaKey || event.ctrlKey;
328
+ }
329
+ function isUndoRedoKey(event) {
330
+ if (event.altKey || !isCommand(event)) return false;
331
+ const letter = undoRedoLetter(event);
332
+ return letter === "z" || letter === "y" && !event.shiftKey;
333
+ }
334
+ function undoRedoLetter(event) {
335
+ if (event.key.length === 1 && event.key.charCodeAt(0) < 128) return event.key.toLowerCase();
336
+ if (event.code === "KeyZ") return "z";
337
+ if (event.code === "KeyY") return "y";
338
+ return void 0;
339
+ }
340
+ var TEXT_FIELD_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
341
+ function decideKey(event, context) {
342
+ const stop = context.host === "ide";
343
+ const composing = event.isComposing === true;
344
+ if (event.key === "Escape" && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) {
345
+ if (context.menuOpen && !composing) {
346
+ return { action: "closeMenu", preventDefault: true, stopPropagation: stop };
347
+ }
348
+ if (context.menuOpen || context.dialogOpen) return { preventDefault: false, stopPropagation: stop };
349
+ }
350
+ const chromeKey = matchChromeKey(event);
351
+ if (chromeKey !== void 0) {
352
+ const blocked = composing || context.inTextField || context.modalOpen;
353
+ if (!blocked) return { action: chromeKey, preventDefault: true, stopPropagation: stop };
354
+ return { preventDefault: false, stopPropagation: chromeKey === "fit" ? false : stop };
355
+ }
356
+ if (context.inTextField && isUndoRedoKey(event)) return { preventDefault: false, stopPropagation: stop };
357
+ if (context.defaultPrevented) return { preventDefault: false, stopPropagation: stop };
358
+ return { preventDefault: false, stopPropagation: false };
359
+ }
360
+ function detectPlatform(navigator) {
361
+ const platform = navigator?.userAgentData?.platform || navigator?.platform || "";
362
+ return /mac/i.test(platform) ? "mac" : "other";
363
+ }
364
+ function shortcutLabel(key, platform, keyNames) {
365
+ if (platform === "mac") return MAC_SHORTCUT_LABELS[key];
366
+ switch (key) {
367
+ case "zoomIn":
368
+ return `${keyNames.ctrl}+${keyNames.plus}`;
369
+ case "zoomOut":
370
+ return `${keyNames.ctrl}+${keyNames.minus}`;
371
+ case "zoomToActual":
372
+ return `${keyNames.ctrl}+0`;
373
+ case "fit":
374
+ return `${keyNames.shift}+1`;
375
+ }
376
+ }
377
+ var MAC_SHORTCUT_LABELS = {
378
+ zoomIn: "\u2318+",
379
+ zoomOut: "\u2318\u2212",
380
+ zoomToActual: "\u23180",
381
+ fit: "\u21E71"
382
+ };
383
+ function ariaKeyShortcuts(key, platform) {
384
+ const command = platform === "mac" ? "Meta" : "Control";
385
+ switch (key) {
386
+ case "zoomIn":
387
+ return `${command}+= ${command}+Plus`;
388
+ case "zoomOut":
389
+ return `${command}+-`;
390
+ case "zoomToActual":
391
+ return `${command}+0`;
392
+ case "fit":
393
+ return "Shift+1";
394
+ }
395
+ }
396
+ var MAC_MODIFIER_SYMBOLS = {
397
+ "\u2318": "Meta",
398
+ "\u21E7": "Shift",
399
+ "\u2325": "Alt",
400
+ "\u2303": "Control"
401
+ };
402
+ var MODIFIER_NAMES = {
403
+ cmd: "Meta",
404
+ command: "Meta",
405
+ meta: "Meta",
406
+ ctrl: "Control",
407
+ control: "Control",
408
+ strg: "Control",
409
+ shift: "Shift",
410
+ umschalt: "Shift",
411
+ alt: "Alt",
412
+ option: "Alt",
413
+ opt: "Alt"
414
+ };
415
+ var NAMED_KEYS = {
416
+ esc: "Escape",
417
+ escape: "Escape",
418
+ del: "Delete",
419
+ delete: "Delete",
420
+ entf: "Delete",
421
+ backspace: "Backspace",
422
+ enter: "Enter",
423
+ return: "Enter",
424
+ tab: "Tab",
425
+ space: "Space",
426
+ home: "Home",
427
+ end: "End",
428
+ pageup: "PageUp",
429
+ pagedown: "PageDown",
430
+ arrowup: "ArrowUp",
431
+ arrowdown: "ArrowDown",
432
+ arrowleft: "ArrowLeft",
433
+ arrowright: "ArrowRight",
434
+ "\u2191": "ArrowUp",
435
+ "\u2193": "ArrowDown",
436
+ "\u2190": "ArrowLeft",
437
+ "\u2192": "ArrowRight",
438
+ plus: "Plus"
439
+ };
440
+ function ariaKeyName(key) {
441
+ const named = NAMED_KEYS[key.toLowerCase()];
442
+ if (named) return named;
443
+ if (/^f\d{1,2}$/i.test(key)) return key.toUpperCase();
444
+ if (key.length !== 1 || MAC_MODIFIER_SYMBOLS[key]) return void 0;
445
+ if (key === "+") return "Plus";
446
+ if (key === " ") return "Space";
447
+ return key.toUpperCase();
448
+ }
449
+ function ariaKeyShortcutsFromLabel(label) {
450
+ let rest = label.trim();
451
+ const modifiers = [];
452
+ for (let symbol = MAC_MODIFIER_SYMBOLS[rest.charAt(0)]; symbol && rest.length > 1; ) {
453
+ modifiers.push(symbol);
454
+ rest = rest.slice(1).trim();
455
+ symbol = MAC_MODIFIER_SYMBOLS[rest.charAt(0)];
456
+ }
457
+ if (!rest || /\s/.test(rest.replace(/\s*\+\s*/g, "+"))) return void 0;
458
+ const parts = rest.split("+").map((part) => part.trim());
459
+ let key = parts.pop() ?? "";
460
+ if (key === "" && parts.at(-1) === "") {
461
+ parts.pop();
462
+ key = "+";
463
+ }
464
+ for (const part of parts) {
465
+ const modifier = MODIFIER_NAMES[part.toLowerCase()];
466
+ if (!modifier) return void 0;
467
+ modifiers.push(modifier);
468
+ }
469
+ const name = ariaKeyName(key);
470
+ return name ? [...new Set(modifiers), name].join("+") : void 0;
471
+ }
472
+
473
+ // src/core/appearance.ts
474
+ function resolveAppearance(requested, themes) {
475
+ const [fallback] = themes;
476
+ if (fallback === void 0) {
477
+ throw new Error(
478
+ "@miragon/modeler-shell: resolveAppearance needs at least one theme the modeler can show"
479
+ );
480
+ }
481
+ const theme = themes.includes(requested.theme) ? requested.theme : fallback;
482
+ return withKontrast(theme, requested.kontrast);
483
+ }
484
+ function withKontrast(theme, kontrast) {
485
+ return kontrast === "hoch" ? { theme, kontrast } : { theme };
486
+ }
487
+ function appearanceFromColorThemeKind(kind) {
488
+ switch (kind) {
489
+ case 2:
490
+ return { theme: "dark" };
491
+ case 3:
492
+ return { theme: "dark", kontrast: "hoch" };
493
+ case 4:
494
+ return { theme: "light", kontrast: "hoch" };
495
+ default:
496
+ return { theme: "light" };
497
+ }
498
+ }
499
+ function requestedAppearance(kind, setting) {
500
+ const fromKind = appearanceFromColorThemeKind(kind);
501
+ const theme = setting === "light" || setting === "dark" ? setting : fromKind.theme;
502
+ return withKontrast(theme, fromKind.kontrast);
503
+ }
504
+ function appearanceFromBodyClasses(classes) {
505
+ const present = new Set(classes);
506
+ if (present.has("vscode-high-contrast-light")) return { theme: "light", kontrast: "hoch" };
507
+ if (present.has("vscode-high-contrast")) return { theme: "dark", kontrast: "hoch" };
508
+ if (present.has("vscode-dark")) return { theme: "dark" };
509
+ if (present.has("vscode-light")) return { theme: "light" };
510
+ return void 0;
511
+ }
512
+ function reducedMotionFromBodyClasses(classes) {
513
+ return new Set(classes).has("vscode-reduce-motion");
514
+ }
515
+ var REDUCED_MOTION_CLASS = "cd-shell-reduce-motion";
516
+ function appearanceAttributes(appearance) {
517
+ return appearance.kontrast === "hoch" ? { "data-cd-theme": appearance.theme, "data-cd-kontrast": "hoch" } : { "data-cd-theme": appearance.theme };
518
+ }
519
+
520
+ // src/core/messages.ts
521
+ var defaultMessages = {
522
+ menu: "Menu",
523
+ tools: "Tools",
524
+ canvas: "Diagram",
525
+ zoom: "Zoom",
526
+ zoomOut: "Zoom out",
527
+ zoomIn: "Zoom in",
528
+ zoomToActual: (percent) => `Zoom to ${percent}`,
529
+ zoomToActualNow: (percent, now) => `Zoom to ${percent} (now ${now})`,
530
+ fit: "Fit to view",
531
+ legend: "Legend",
532
+ closeLegend: "Close legend",
533
+ problems: (count, formattedCount) => `${formattedCount} ${count === 1 ? "problem" : "problems"}`,
534
+ problemsLabel: (count, errors, formattedCount, formattedErrors) => `${formattedCount} ${count === 1 ? "problem" : "problems"}, ${errors === 0 ? "no errors" : `${formattedErrors} ${errors === 1 ? "error" : "errors"}`}`,
535
+ showProblems: "Show in Problems view",
536
+ emptyHint: "Pick a tool from the palette to start.",
537
+ insertExample: "Insert example",
538
+ invalidFile: (diagram) => `This file isn't a valid ${diagram} right now. The diagram shows the last valid version and is read-only until you fix the text.`,
539
+ keyNames: { ctrl: "Ctrl", shift: "Shift", plus: "Plus", minus: "Minus" }
540
+ };
541
+ function resolveMessages(overrides) {
542
+ const { keyNames, ...texts } = overrides ?? {};
543
+ return {
544
+ ...defaultMessages,
545
+ ...definedEntries(texts),
546
+ keyNames: { ...defaultMessages.keyNames, ...definedEntries(keyNames ?? {}) }
547
+ };
548
+ }
549
+ function definedEntries(partial) {
550
+ return Object.fromEntries(Object.entries(partial).filter(([, value]) => value !== void 0));
551
+ }
552
+
553
+ // src/core/layout.ts
554
+ var PALETTE_CLEARANCE = 16;
555
+ var TOOLTIP_DELAY_MS = 300;
556
+ var INVALID_STATUS_DELAY_MS = 800;
557
+ function decidePaletteRow(input) {
558
+ const { containerWidth, paletteWidth, menuRight, primaryLeft } = input;
559
+ if (containerWidth <= SHELL.narrow) return "second-row";
560
+ const paletteLeft = (containerWidth - paletteWidth) / 2;
561
+ const paletteRight = (containerWidth + paletteWidth) / 2;
562
+ if (paletteLeft < menuRight + PALETTE_CLEARANCE) return "second-row";
563
+ if (primaryLeft !== void 0 && paletteRight > primaryLeft - PALETTE_CLEARANCE) return "second-row";
564
+ return "top";
565
+ }
566
+ function legendStartsOpen(containerHeight) {
567
+ return containerHeight > SHELL.low;
568
+ }
569
+ function placeTooltip(input) {
570
+ const { anchor, island, tooltip, container } = input;
571
+ const inUpperHalf = island.y + island.height / 2 < container.height / 2;
572
+ const y = inUpperHalf ? island.y + island.height + SHELL.gap : island.y - SHELL.gap - tooltip.height;
573
+ const centred = anchor.x + anchor.width / 2 - tooltip.width / 2;
574
+ const rightmost = container.width - SHELL.edge - tooltip.width;
575
+ const x = Math.max(SHELL.edge, Math.min(centred, rightmost));
576
+ return { x, y };
577
+ }
578
+
579
+ // src/dom/element.ts
580
+ function createElement(ownerDocument, tag, attributes = {}, ...children) {
581
+ const element = ownerDocument.createElement(tag);
582
+ for (const [name, value] of Object.entries(attributes)) {
583
+ if (value !== void 0) element.setAttribute(name, value);
584
+ }
585
+ for (const child of children) {
586
+ if (child === void 0 || child === null || child === false) continue;
587
+ element.append(child);
588
+ }
589
+ return element;
590
+ }
591
+ function toggleAttribute(element, name, value) {
592
+ if (value === null || value === false) element.removeAttribute(name);
593
+ else element.setAttribute(name, value);
594
+ }
595
+ function boxRelativeTo(element, root) {
596
+ const box = element.getBoundingClientRect();
597
+ const origin = root.getBoundingClientRect();
598
+ return {
599
+ x: box.left - origin.left - root.clientLeft,
600
+ y: box.top - origin.top - root.clientTop,
601
+ width: box.width,
602
+ height: box.height
603
+ };
604
+ }
605
+ function accessibleName(control) {
606
+ return (control.getAttribute("aria-label") ?? control.textContent ?? "").trim();
607
+ }
608
+ function hasVisibleFocus(element) {
609
+ try {
610
+ return element.matches(":focus-visible");
611
+ } catch {
612
+ return true;
613
+ }
614
+ }
615
+
616
+ // src/dom/hint.ts
617
+ function createEmptyHint(ownerDocument, messages, options) {
618
+ const { onInsertExample } = options;
619
+ const action = onInsertExample ? createElement(
620
+ ownerDocument,
621
+ "button",
622
+ { type: "button", class: "cd-shell-hint-action" },
623
+ messages.insertExample
624
+ ) : void 0;
625
+ action?.addEventListener("click", () => onInsertExample?.());
626
+ const element = createElement(
627
+ ownerDocument,
628
+ "div",
629
+ { class: "cd-shell-hint" },
630
+ createElement(ownerDocument, "p", { class: "cd-shell-hint-text" }, messages.emptyHint),
631
+ action
632
+ );
633
+ element.hidden = true;
634
+ return {
635
+ element,
636
+ isShown: () => !element.hidden,
637
+ setShown(shown) {
638
+ element.hidden = !shown;
639
+ },
640
+ place({ top, right, bottom, left }) {
641
+ element.style.inset = `${top}px ${right}px ${bottom}px ${left}px`;
642
+ },
643
+ destroy() {
644
+ element.remove();
645
+ }
646
+ };
647
+ }
648
+
649
+ // src/dom/inspector.ts
650
+ function createInspector(ownerDocument, options, onChange) {
651
+ const element = createElement(ownerDocument, "aside", {
652
+ class: "cd-shell-inspector",
653
+ "data-chrome": "inspector",
654
+ "aria-label": options.label
655
+ });
656
+ element.hidden = true;
657
+ function setOpen(open) {
658
+ if (open === !element.hidden) return;
659
+ element.hidden = !open;
660
+ onChange(open);
661
+ }
662
+ return {
663
+ element,
664
+ isOpen: () => !element.hidden,
665
+ open: () => setOpen(true),
666
+ close: () => setOpen(false),
667
+ destroy() {
668
+ element.remove();
669
+ }
670
+ };
671
+ }
672
+
673
+ // src/dom/keys.ts
674
+ function isTextField(target) {
675
+ if (!target || typeof target.closest !== "function") return false;
676
+ const element = target;
677
+ return element.isContentEditable === true || element.closest(TEXT_FIELD_SELECTOR) !== null;
678
+ }
679
+ function isShown(element) {
680
+ if (element.localName === "dialog" && !element.hasAttribute("open")) return false;
681
+ if (element.closest("[hidden]")) return false;
682
+ return typeof element.checkVisibility !== "function" || element.checkVisibility({ visibilityProperty: true });
683
+ }
684
+ function isModal(element) {
685
+ if (element.getAttribute("aria-modal") === "true") return true;
686
+ try {
687
+ return element.matches(":modal");
688
+ } catch {
689
+ return false;
690
+ }
691
+ }
692
+ function openDialogs(scope) {
693
+ return [...scope.querySelectorAll('[role="dialog"], [role="alertdialog"], dialog')].filter(isShown);
694
+ }
695
+ function createKeyListener(context) {
696
+ return (event) => {
697
+ if (event.key === "Escape") context.hideTooltip();
698
+ const dialogs = openDialogs(context.dialogScope);
699
+ const decision = decideKey(event, {
700
+ host: context.host,
701
+ inTextField: isTextField(event.target),
702
+ menuOpen: context.menuOpen(),
703
+ dialogOpen: dialogs.length > 0,
704
+ modalOpen: dialogs.some(isModal),
705
+ defaultPrevented: event.defaultPrevented
706
+ });
707
+ if (decision.preventDefault) event.preventDefault();
708
+ if (decision.stopPropagation) event.stopPropagation();
709
+ if (decision.action === "closeMenu") context.closeMenu();
710
+ else if (decision.action) context.run(decision.action);
711
+ };
712
+ }
713
+
714
+ // src/dom/ledger.ts
715
+ var Ledger = class {
716
+ undo = [];
717
+ originalAttributes = /* @__PURE__ */ new Map();
718
+ closed = false;
719
+ get isClosed() {
720
+ return this.closed;
721
+ }
722
+ /** Runs `task` on `close()`, in reverse order of registration. */
723
+ defer(task) {
724
+ this.undo.push(task);
725
+ }
726
+ /** Adds `className` unless present; `close()` removes it only if this ledger added it. */
727
+ addClass(element, className) {
728
+ if (element.classList.contains(className)) return;
729
+ const hadClassAttribute = element.hasAttribute("class");
730
+ element.classList.add(className);
731
+ this.defer(() => {
732
+ element.classList.remove(className);
733
+ if (!hadClassAttribute && element.classList.length === 0) element.removeAttribute("class");
734
+ });
735
+ }
736
+ /** Sets (string) or removes (`null`) an attribute; `close()` restores the value before the first change. */
737
+ attribute(element, name, value) {
738
+ let originals = this.originalAttributes.get(element);
739
+ if (!originals) {
740
+ originals = /* @__PURE__ */ new Map();
741
+ this.originalAttributes.set(element, originals);
742
+ }
743
+ if (!originals.has(name)) {
744
+ originals.set(name, element.getAttribute(name));
745
+ this.defer(() => {
746
+ const original = originals.get(name) ?? null;
747
+ if (original === null) element.removeAttribute(name);
748
+ else element.setAttribute(name, original);
749
+ });
750
+ }
751
+ if (value === null) element.removeAttribute(name);
752
+ else element.setAttribute(name, value);
753
+ }
754
+ listen(target, type, listener, options) {
755
+ const handler = listener;
756
+ target.addEventListener(type, handler, options);
757
+ this.defer(() => target.removeEventListener(type, handler, options));
758
+ }
759
+ /** Runs every undo task once; later calls are no-ops. */
760
+ close() {
761
+ if (this.closed) return;
762
+ this.closed = true;
763
+ for (const task of this.undo.reverse()) {
764
+ try {
765
+ task();
766
+ } catch {
767
+ }
768
+ }
769
+ this.undo.length = 0;
770
+ this.originalAttributes.clear();
771
+ }
772
+ };
773
+
774
+ // src/dom/icons.ts
775
+ var ICONS = {
776
+ menu: [
777
+ ["path", { d: "M4 5h16" }],
778
+ ["path", { d: "M4 12h16" }],
779
+ ["path", { d: "M4 19h16" }]
780
+ ],
781
+ minus: [["path", { d: "M5 12h14" }]],
782
+ plus: [
783
+ ["path", { d: "M5 12h14" }],
784
+ ["path", { d: "M12 5v14" }]
785
+ ],
786
+ maximize: [
787
+ ["path", { d: "M8 3H5a2 2 0 0 0-2 2v3" }],
788
+ ["path", { d: "M21 8V5a2 2 0 0 0-2-2h-3" }],
789
+ ["path", { d: "M3 16v3a2 2 0 0 0 2 2h3" }],
790
+ ["path", { d: "M16 21h3a2 2 0 0 0 2-2v-3" }]
791
+ ],
792
+ check: [["path", { d: "M20 6 9 17l-5-5" }]],
793
+ x: [
794
+ ["path", { d: "M18 6 6 18" }],
795
+ ["path", { d: "m6 6 12 12" }]
796
+ ],
797
+ "file-plus": [
798
+ [
799
+ "path",
800
+ {
801
+ d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"
802
+ }
803
+ ],
804
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }],
805
+ ["path", { d: "M9 15h6" }],
806
+ ["path", { d: "M12 18v-6" }]
807
+ ],
808
+ "folder-open": [
809
+ [
810
+ "path",
811
+ {
812
+ d: "m6 14 1.5-2.9A2 2 0 0 1 9.24 10H20a2 2 0 0 1 1.94 2.5l-1.54 6a2 2 0 0 1-1.95 1.5H4a2 2 0 0 1-2-2V5a2 2 0 0 1 2-2h3.9a2 2 0 0 1 1.69.9l.81 1.2a2 2 0 0 0 1.67.9H18a2 2 0 0 1 2 2v2"
813
+ }
814
+ ]
815
+ ],
816
+ import: [
817
+ ["path", { d: "M12 3v12" }],
818
+ ["path", { d: "m8 11 4 4 4-4" }],
819
+ ["path", { d: "M8 5H4a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V7a2 2 0 0 0-2-2h-4" }]
820
+ ],
821
+ "file-image": [
822
+ [
823
+ "path",
824
+ {
825
+ d: "M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z"
826
+ }
827
+ ],
828
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }],
829
+ ["circle", { cx: "10", cy: "12", r: "2" }],
830
+ ["path", { d: "m20 17-1.296-1.296a2.41 2.41 0 0 0-3.408 0L9 22" }]
831
+ ],
832
+ "file-code-corner": [
833
+ [
834
+ "path",
835
+ {
836
+ d: "M4 12.15V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.706.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2h-3.35"
837
+ }
838
+ ],
839
+ ["path", { d: "M14 2v5a1 1 0 0 0 1 1h5" }],
840
+ ["path", { d: "m5 16-3 3 3 3" }],
841
+ ["path", { d: "m9 22 3-3-3-3" }]
842
+ ],
843
+ keyboard: [
844
+ ["path", { d: "M10 8h.01" }],
845
+ ["path", { d: "M12 12h.01" }],
846
+ ["path", { d: "M14 8h.01" }],
847
+ ["path", { d: "M16 12h.01" }],
848
+ ["path", { d: "M18 8h.01" }],
849
+ ["path", { d: "M6 8h.01" }],
850
+ ["path", { d: "M7 16h10" }],
851
+ ["path", { d: "M8 12h.01" }],
852
+ ["rect", { width: "20", height: "16", x: "2", y: "4", rx: "2" }]
853
+ ],
854
+ "share-2": [
855
+ ["circle", { cx: "18", cy: "5", r: "3" }],
856
+ ["circle", { cx: "6", cy: "12", r: "3" }],
857
+ ["circle", { cx: "18", cy: "19", r: "3" }],
858
+ ["line", { x1: "8.59", x2: "15.42", y1: "13.51", y2: "17.49" }],
859
+ ["line", { x1: "15.41", x2: "8.59", y1: "6.51", y2: "10.49" }]
860
+ ],
861
+ "circle-x": [
862
+ ["circle", { cx: "12", cy: "12", r: "10" }],
863
+ ["path", { d: "m15 9-6 6" }],
864
+ ["path", { d: "m9 9 6 6" }]
865
+ ],
866
+ "triangle-alert": [
867
+ ["path", { d: "m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3" }],
868
+ ["path", { d: "M12 9v4" }],
869
+ ["path", { d: "M12 17h.01" }]
870
+ ],
871
+ "square-dashed-mouse-pointer": [
872
+ [
873
+ "path",
874
+ {
875
+ d: "M12.034 12.681a.498.498 0 0 1 .647-.647l9 3.5a.5.5 0 0 1-.033.943l-3.444 1.068a1 1 0 0 0-.66.66l-1.067 3.443a.5.5 0 0 1-.943.033z"
876
+ }
877
+ ],
878
+ ["path", { d: "M5 3a2 2 0 0 0-2 2" }],
879
+ ["path", { d: "M19 3a2 2 0 0 1 2 2" }],
880
+ ["path", { d: "M5 21a2 2 0 0 1-2-2" }],
881
+ ["path", { d: "M9 3h1" }],
882
+ ["path", { d: "M9 21h2" }],
883
+ ["path", { d: "M14 3h1" }],
884
+ ["path", { d: "M3 9v1" }],
885
+ ["path", { d: "M21 9v2" }],
886
+ ["path", { d: "M3 14v1" }]
887
+ ],
888
+ hand: [
889
+ ["path", { d: "M18 11V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2" }],
890
+ ["path", { d: "M14 10V4a2 2 0 0 0-2-2a2 2 0 0 0-2 2v2" }],
891
+ ["path", { d: "M10 10.5V6a2 2 0 0 0-2-2a2 2 0 0 0-2 2v8" }],
892
+ [
893
+ "path",
894
+ {
895
+ d: "M18 8a2 2 0 1 1 4 0v6a8 8 0 0 1-8 8h-2c-2.8 0-4.5-.86-5.99-2.34l-3.6-3.6a2 2 0 0 1 2.83-2.82L7 15"
896
+ }
897
+ ]
898
+ ],
899
+ "line-segment": [
900
+ ["circle", { cx: "19", cy: "5", r: "2" }],
901
+ ["circle", { cx: "5", cy: "19", r: "2" }],
902
+ ["path", { d: "M6.5 17.5 17.5 6.5" }]
903
+ ],
904
+ "unfold-horizontal": [
905
+ ["path", { d: "M16 12h6" }],
906
+ ["path", { d: "M8 12H2" }],
907
+ ["path", { d: "M12 2v2" }],
908
+ ["path", { d: "M12 8v2" }],
909
+ ["path", { d: "M12 14v2" }],
910
+ ["path", { d: "M12 20v2" }],
911
+ ["path", { d: "m19 15 3-3-3-3" }],
912
+ ["path", { d: "m5 9-3 3 3 3" }]
913
+ ],
914
+ workflow: [
915
+ ["rect", { width: "8", height: "8", x: "3", y: "3", rx: "2" }],
916
+ ["path", { d: "M7 11v4a2 2 0 0 0 2 2h4" }],
917
+ ["rect", { width: "8", height: "8", x: "13", y: "13", rx: "2" }]
918
+ ]
919
+ };
920
+ var PALETTE_TOOL_ICON = { size: 20, strokeWidth: 2 };
921
+ var DEFAULT_SIZE = 16;
922
+ var DEFAULT_STROKE_WIDTH = 2.5;
923
+ var SVG_NAMESPACE = "http://www.w3.org/2000/svg";
924
+ var ICON_ELEMENTS = /* @__PURE__ */ new Set([
925
+ "path",
926
+ "circle",
927
+ "ellipse",
928
+ "line",
929
+ "polyline",
930
+ "polygon",
931
+ "rect"
932
+ ]);
933
+ var GEOMETRY_ATTRIBUTES = /* @__PURE__ */ new Set([
934
+ "d",
935
+ "cx",
936
+ "cy",
937
+ "r",
938
+ "rx",
939
+ "ry",
940
+ "x",
941
+ "y",
942
+ "x1",
943
+ "y1",
944
+ "x2",
945
+ "y2",
946
+ "width",
947
+ "height",
948
+ "points"
949
+ ]);
950
+ function positiveOr(value, fallback) {
951
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : fallback;
952
+ }
953
+ function iconNodeOf(icon) {
954
+ if (typeof icon !== "string") return icon;
955
+ return Object.prototype.hasOwnProperty.call(ICONS, icon) ? ICONS[icon] : [];
956
+ }
957
+ function sanitize(icon, options = {}) {
958
+ const size = String(positiveOr(options.size, DEFAULT_SIZE));
959
+ const root = [];
960
+ if (options.className) root.push(["class", options.className]);
961
+ root.push(
962
+ ["width", size],
963
+ ["height", size],
964
+ ["viewBox", "0 0 24 24"],
965
+ ["fill", "none"],
966
+ ["stroke", "currentColor"],
967
+ ["stroke-width", String(positiveOr(options.strokeWidth, DEFAULT_STROKE_WIDTH))],
968
+ ["stroke-linecap", "round"],
969
+ ["stroke-linejoin", "round"],
970
+ ["aria-hidden", "true"],
971
+ ["focusable", "false"]
972
+ );
973
+ const children = [];
974
+ for (const node of iconNodeOf(icon)) {
975
+ const [tag, attributes] = node;
976
+ if (!ICON_ELEMENTS.has(tag)) continue;
977
+ const kept = Object.entries(attributes).filter(([name]) => GEOMETRY_ATTRIBUTES.has(name)).map(([name, value]) => [name, String(value)]);
978
+ children.push([tag, kept]);
979
+ }
980
+ return { root, children };
981
+ }
982
+ function escapeAttribute(value) {
983
+ return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/'/g, "&#39;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
984
+ }
985
+ function attributeList(attributes) {
986
+ return attributes.map(([name, value]) => ` ${name}="${escapeAttribute(value)}"`).join("");
987
+ }
988
+ function iconMarkup(icon, options) {
989
+ const { root, children } = sanitize(icon, options);
990
+ const content = children.map(([tag, attributes]) => `<${tag}${attributeList(attributes)}/>`).join("");
991
+ return `<svg xmlns="${SVG_NAMESPACE}"${attributeList(root)}>${content}</svg>`;
992
+ }
993
+ function iconElement(icon, options) {
994
+ const { root, children } = sanitize(icon, options);
995
+ const svg = document.createElementNS(SVG_NAMESPACE, "svg");
996
+ for (const [name, value] of root) svg.setAttribute(name, value);
997
+ for (const [tag, attributes] of children) {
998
+ const child = document.createElementNS(SVG_NAMESPACE, tag);
999
+ for (const [name, value] of attributes) child.setAttribute(name, value);
1000
+ svg.append(child);
1001
+ }
1002
+ return svg;
1003
+ }
1004
+
1005
+ // src/dom/legend.ts
1006
+ function createLegend(context) {
1007
+ const { ownerDocument, messages, tooltip, titleId, options } = context;
1008
+ const close = createElement(ownerDocument, "button", {
1009
+ type: "button",
1010
+ class: "cd-shell-legend-close",
1011
+ "aria-label": messages.closeLegend
1012
+ });
1013
+ close.append(iconElement("x"));
1014
+ const body = createElement(ownerDocument, "div", { class: "cd-shell-legend-body" });
1015
+ const element = createElement(
1016
+ ownerDocument,
1017
+ "section",
1018
+ { class: "cd-shell-legend cd-shell-island", "data-chrome": "legend", "aria-labelledby": titleId },
1019
+ createElement(
1020
+ ownerDocument,
1021
+ "div",
1022
+ { class: "cd-shell-legend-header" },
1023
+ createElement(ownerDocument, "div", { class: "cd-shell-legend-title", id: titleId }, messages.legend),
1024
+ close
1025
+ ),
1026
+ body
1027
+ );
1028
+ element.hidden = true;
1029
+ options.render(body);
1030
+ let open = false;
1031
+ let started = false;
1032
+ const detachTooltip = tooltip.attach(close, { label: messages.closeLegend });
1033
+ function apply(next) {
1034
+ started = true;
1035
+ if (next === open) return false;
1036
+ open = next;
1037
+ element.hidden = !open;
1038
+ if (!open && element.contains(ownerDocument.activeElement)) context.focusAfterClose();
1039
+ context.onChange(open);
1040
+ return true;
1041
+ }
1042
+ function setOpen(next) {
1043
+ if (apply(next)) options.onToggle?.(next);
1044
+ }
1045
+ close.addEventListener("click", () => setOpen(false));
1046
+ return {
1047
+ element,
1048
+ body,
1049
+ isOpen: () => open,
1050
+ setOpen,
1051
+ toggle: () => setOpen(!open),
1052
+ start(initial) {
1053
+ if (!started) apply(initial);
1054
+ },
1055
+ isStarted: () => started,
1056
+ destroy() {
1057
+ detachTooltip();
1058
+ element.remove();
1059
+ }
1060
+ };
1061
+ }
1062
+
1063
+ // src/dom/menu.ts
1064
+ var GROUP_ORDER = ["model", "export", "help"];
1065
+ function isPrintable(event) {
1066
+ return event.key.length === 1 && /\S/.test(event.key) && !event.ctrlKey && !event.metaKey && !event.altKey;
1067
+ }
1068
+ function createMenu(context) {
1069
+ const { root, layer, id, messages, tooltip } = context;
1070
+ const ownerDocument = root.ownerDocument;
1071
+ let groups = context.groups;
1072
+ let items = [];
1073
+ let destroyed = false;
1074
+ const trigger = createElement(ownerDocument, "button", {
1075
+ type: "button",
1076
+ class: "cd-shell-menu-trigger cd-shell-island",
1077
+ "data-chrome": "menu",
1078
+ "aria-label": messages.menu,
1079
+ "aria-haspopup": "menu",
1080
+ "aria-expanded": "false",
1081
+ "aria-controls": id
1082
+ });
1083
+ trigger.append(iconElement("menu"));
1084
+ const dropdown = createElement(ownerDocument, "div", {
1085
+ class: "cd-shell-menu",
1086
+ id,
1087
+ role: "menu",
1088
+ "aria-label": messages.menu
1089
+ });
1090
+ dropdown.hidden = true;
1091
+ layer.prepend(dropdown);
1092
+ const isOpen = () => !dropdown.hidden;
1093
+ function entriesOf(name) {
1094
+ const own = groups[name] ?? [];
1095
+ return name === "model" ? [...own, ...context.shellModelEntries()] : own;
1096
+ }
1097
+ function hasEntries() {
1098
+ return GROUP_ORDER.some((name) => entriesOf(name).length > 0);
1099
+ }
1100
+ function updateTriggerVisibility() {
1101
+ trigger.hidden = !hasEntries();
1102
+ if (trigger.hidden) close();
1103
+ }
1104
+ function isDisabled(entry, group) {
1105
+ if (group === "model" && context.isReadOnly() && entry.keepEnabledWhenReadOnly !== true) return true;
1106
+ return entry.disabled?.() ?? false;
1107
+ }
1108
+ function itemElement(role, label, options) {
1109
+ const iconSlot = createElement(ownerDocument, "span", { class: "cd-shell-menu-item-icon" });
1110
+ if (options.icon) iconSlot.append(iconElement(options.icon));
1111
+ const element = createElement(
1112
+ ownerDocument,
1113
+ "button",
1114
+ { type: "button", class: "cd-shell-menu-item", role, tabindex: "-1" },
1115
+ iconSlot,
1116
+ createElement(ownerDocument, "span", { class: "cd-shell-menu-item-label" }, label),
1117
+ options.end && createElement(ownerDocument, "span", { class: "cd-shell-menu-item-end" }, options.end)
1118
+ );
1119
+ toggleAttribute(element, "aria-disabled", options.disabled && "true");
1120
+ if (options.checked !== void 0) element.setAttribute("aria-checked", String(options.checked));
1121
+ return element;
1122
+ }
1123
+ function checkMark() {
1124
+ return iconElement("check", { className: "cd-shell-menu-check" });
1125
+ }
1126
+ function renderEntry(entry, group) {
1127
+ const disabled = isDisabled(entry, group);
1128
+ if (entry.kind === "checkbox") {
1129
+ const checked = entry.checked();
1130
+ const element2 = itemElement("menuitemcheckbox", entry.label, {
1131
+ icon: entry.icon,
1132
+ end: checkMark(),
1133
+ disabled,
1134
+ checked
1135
+ });
1136
+ element2.dataset.menuId = entry.id;
1137
+ element2.dataset.menuGroup = group;
1138
+ return {
1139
+ key: entry.id,
1140
+ element: element2,
1141
+ label: entry.label,
1142
+ disabled,
1143
+ activate() {
1144
+ entry.onToggle(!entry.checked());
1145
+ refresh();
1146
+ }
1147
+ };
1148
+ }
1149
+ const shortcut = entry.kind === "action" && entry.shortcut ? createElement(ownerDocument, "span", { class: "cd-shell-menu-shortcut" }, entry.shortcut) : void 0;
1150
+ const element = itemElement("menuitem", entry.label, { icon: entry.icon, end: shortcut, disabled });
1151
+ element.dataset.menuId = entry.id;
1152
+ element.dataset.menuGroup = group;
1153
+ if (entry.kind === "action") {
1154
+ const keyShortcuts = entry.ariaKeyShortcuts ?? (entry.shortcut ? ariaKeyShortcutsFromLabel(entry.shortcut) : void 0);
1155
+ toggleAttribute(element, "aria-keyshortcuts", keyShortcuts || null);
1156
+ }
1157
+ if (entry.kind === "dialog") element.setAttribute("aria-haspopup", "dialog");
1158
+ if (entry.kind === "action" && entry.danger) element.dataset.danger = "";
1159
+ return {
1160
+ key: entry.id,
1161
+ element,
1162
+ label: entry.label,
1163
+ disabled,
1164
+ activate() {
1165
+ close({ restoreFocus: true });
1166
+ entry.onSelect();
1167
+ }
1168
+ };
1169
+ }
1170
+ function renderRadioGroup(entry, group) {
1171
+ const disabled = isDisabled(entry, group);
1172
+ const value = entry.value();
1173
+ const container = createElement(
1174
+ ownerDocument,
1175
+ "div",
1176
+ {
1177
+ class: "cd-shell-menu-group",
1178
+ role: "group",
1179
+ "aria-label": entry.label,
1180
+ "data-menu-id": entry.id,
1181
+ "data-menu-group": group
1182
+ },
1183
+ // The group's aria-label carries the heading; a menu holds no loose text.
1184
+ createElement(
1185
+ ownerDocument,
1186
+ "div",
1187
+ { class: "cd-shell-menu-heading", "aria-hidden": "true" },
1188
+ entry.label
1189
+ )
1190
+ );
1191
+ const radioItems = entry.options.map((option) => {
1192
+ const element = itemElement("menuitemradio", option.label, {
1193
+ end: checkMark(),
1194
+ disabled,
1195
+ checked: option.value === value
1196
+ });
1197
+ element.dataset.menuValue = option.value;
1198
+ container.append(element);
1199
+ return {
1200
+ key: `${entry.id}\0${option.value}`,
1201
+ element,
1202
+ label: option.label,
1203
+ disabled,
1204
+ activate() {
1205
+ if (entry.value() !== option.value) entry.onSelect(option.value);
1206
+ refresh();
1207
+ }
1208
+ };
1209
+ });
1210
+ return { element: container, items: radioItems };
1211
+ }
1212
+ function render() {
1213
+ const focusedKey = items.find((item) => item.element === ownerDocument.activeElement)?.key;
1214
+ const nodes = [];
1215
+ const rendered = [];
1216
+ for (const name of GROUP_ORDER) {
1217
+ const entries = entriesOf(name);
1218
+ if (entries.length === 0) continue;
1219
+ if (nodes.length > 0) {
1220
+ nodes.push(
1221
+ createElement(ownerDocument, "div", { class: "cd-shell-menu-separator", role: "separator" })
1222
+ );
1223
+ }
1224
+ for (const entry of entries) {
1225
+ if (entry.kind === "radio") {
1226
+ const radio = renderRadioGroup(entry, name);
1227
+ nodes.push(radio.element);
1228
+ rendered.push(...radio.items);
1229
+ } else {
1230
+ const item = renderEntry(entry, name);
1231
+ nodes.push(item.element);
1232
+ rendered.push(item);
1233
+ }
1234
+ }
1235
+ }
1236
+ for (const item of rendered) {
1237
+ item.element.addEventListener("click", () => {
1238
+ if (!item.disabled) item.activate();
1239
+ });
1240
+ }
1241
+ dropdown.replaceChildren(...nodes);
1242
+ items = rendered;
1243
+ if (focusedKey !== void 0 && isOpen()) {
1244
+ (items.find((item) => item.key === focusedKey) ?? items[0])?.element.focus();
1245
+ }
1246
+ }
1247
+ function focusAt(index) {
1248
+ if (items.length === 0) return;
1249
+ items[(index + items.length) % items.length]?.element.focus();
1250
+ }
1251
+ function currentIndex() {
1252
+ return items.findIndex((item) => item.element === ownerDocument.activeElement);
1253
+ }
1254
+ function typeAhead(character) {
1255
+ const start = currentIndex();
1256
+ const lower = character.toLowerCase();
1257
+ for (let step = 1; step <= items.length; step++) {
1258
+ const index = (Math.max(start, -1) + step) % items.length;
1259
+ if (items[index]?.label.toLowerCase().startsWith(lower)) {
1260
+ focusAt(index);
1261
+ return;
1262
+ }
1263
+ }
1264
+ }
1265
+ const onOutsidePointerDown = (event) => {
1266
+ const target = event.target;
1267
+ if (target && (dropdown.contains(target) || trigger.contains(target))) return;
1268
+ close();
1269
+ };
1270
+ const onWindowBlur = () => close();
1271
+ function open(focus = "first") {
1272
+ if (destroyed || !hasEntries()) return;
1273
+ tooltip.hide();
1274
+ const wasOpen = isOpen();
1275
+ render();
1276
+ if (!wasOpen) {
1277
+ dropdown.hidden = false;
1278
+ trigger.setAttribute("aria-expanded", "true");
1279
+ ownerDocument.addEventListener("pointerdown", onOutsidePointerDown, true);
1280
+ ownerDocument.defaultView?.addEventListener("blur", onWindowBlur);
1281
+ }
1282
+ focusAt(focus === "last" ? items.length - 1 : 0);
1283
+ }
1284
+ function close(options = {}) {
1285
+ if (!isOpen()) return;
1286
+ dropdown.hidden = true;
1287
+ trigger.setAttribute("aria-expanded", "false");
1288
+ ownerDocument.removeEventListener("pointerdown", onOutsidePointerDown, true);
1289
+ ownerDocument.defaultView?.removeEventListener("blur", onWindowBlur);
1290
+ if (options.restoreFocus && !trigger.hidden) trigger.focus();
1291
+ }
1292
+ function refresh() {
1293
+ if (isOpen()) render();
1294
+ }
1295
+ trigger.addEventListener("click", () => {
1296
+ if (isOpen()) close();
1297
+ else open("first");
1298
+ });
1299
+ function consume(event) {
1300
+ event.preventDefault();
1301
+ event.stopPropagation();
1302
+ }
1303
+ trigger.addEventListener("keydown", (event) => {
1304
+ if (event.key === "Enter" || event.key === " " || event.key === "ArrowDown") {
1305
+ consume(event);
1306
+ open("first");
1307
+ } else if (event.key === "ArrowUp") {
1308
+ consume(event);
1309
+ open("last");
1310
+ }
1311
+ });
1312
+ dropdown.addEventListener("keydown", (event) => {
1313
+ if (matchChromeKey(event)) return;
1314
+ switch (event.key) {
1315
+ case "ArrowDown":
1316
+ focusAt(currentIndex() + 1);
1317
+ break;
1318
+ case "ArrowUp": {
1319
+ const index = currentIndex();
1320
+ focusAt(index < 0 ? items.length - 1 : index - 1);
1321
+ break;
1322
+ }
1323
+ case "Home":
1324
+ focusAt(0);
1325
+ break;
1326
+ case "End":
1327
+ focusAt(items.length - 1);
1328
+ break;
1329
+ case "Tab":
1330
+ close({ restoreFocus: true });
1331
+ return;
1332
+ case "Escape":
1333
+ return;
1334
+ case "Enter":
1335
+ case " ":
1336
+ case "Delete":
1337
+ case "Backspace":
1338
+ event.stopPropagation();
1339
+ return;
1340
+ default:
1341
+ if (!isPrintable(event)) return;
1342
+ typeAhead(event.key);
1343
+ }
1344
+ consume(event);
1345
+ });
1346
+ updateTriggerVisibility();
1347
+ return {
1348
+ trigger,
1349
+ dropdown,
1350
+ isOpen,
1351
+ open,
1352
+ close,
1353
+ refresh,
1354
+ setGroups(next) {
1355
+ groups = next;
1356
+ updateTriggerVisibility();
1357
+ refresh();
1358
+ },
1359
+ destroy() {
1360
+ if (destroyed) return;
1361
+ close();
1362
+ destroyed = true;
1363
+ trigger.remove();
1364
+ dropdown.remove();
1365
+ }
1366
+ };
1367
+ }
1368
+
1369
+ // src/dom/problems.ts
1370
+ function createProblemsPill(context) {
1371
+ const { ownerDocument, messages, lang, tooltip, options } = context;
1372
+ const icon = createElement(ownerDocument, "span", { class: "cd-shell-problems-icon" });
1373
+ const text = createElement(ownerDocument, "span", { class: "cd-shell-problems-text" });
1374
+ const element = createElement(
1375
+ ownerDocument,
1376
+ "button",
1377
+ { type: "button", class: "cd-shell-problems cd-shell-island", "data-chrome": "problems" },
1378
+ icon,
1379
+ text
1380
+ );
1381
+ element.hidden = true;
1382
+ element.addEventListener("click", () => options.onActivate());
1383
+ const detachTooltip = context.showTooltip ? tooltip.attach(element, { label: messages.showProblems }) : void 0;
1384
+ let severity;
1385
+ return {
1386
+ element,
1387
+ isShown: () => !element.hidden,
1388
+ set({ count, errors }) {
1389
+ const shown = count > 0;
1390
+ element.hidden = !shown;
1391
+ if (!shown) {
1392
+ tooltip.hide();
1393
+ return;
1394
+ }
1395
+ const formattedCount = formatNumber(count, lang);
1396
+ text.textContent = messages.problems(count, formattedCount);
1397
+ element.setAttribute(
1398
+ "aria-label",
1399
+ messages.problemsLabel(count, errors, formattedCount, formatNumber(errors, lang))
1400
+ );
1401
+ const next = errors > 0 ? "danger" : "warning";
1402
+ if (next !== severity) {
1403
+ severity = next;
1404
+ element.dataset.severity = next;
1405
+ icon.replaceChildren(iconElement(next === "danger" ? "circle-x" : "triangle-alert"));
1406
+ }
1407
+ },
1408
+ destroy() {
1409
+ detachTooltip?.();
1410
+ element.remove();
1411
+ }
1412
+ };
1413
+ }
1414
+
1415
+ // src/dom/status.ts
1416
+ function createStatusBox(ownerDocument) {
1417
+ const icon = createElement(ownerDocument, "span", { class: "cd-shell-status-icon" });
1418
+ const text = createElement(ownerDocument, "p", { class: "cd-shell-status-text" });
1419
+ const element = createElement(
1420
+ ownerDocument,
1421
+ "div",
1422
+ { class: "cd-shell-status", "data-chrome": "status" },
1423
+ icon,
1424
+ text
1425
+ );
1426
+ element.hidden = true;
1427
+ let pending;
1428
+ function cancelPending() {
1429
+ if (pending) clearTimeout(pending.timer);
1430
+ pending = void 0;
1431
+ }
1432
+ function show(status) {
1433
+ pending = void 0;
1434
+ if (element.dataset.severity !== status.severity) {
1435
+ element.dataset.severity = status.severity;
1436
+ element.setAttribute("role", status.severity === "danger" ? "alert" : "status");
1437
+ icon.replaceChildren(iconElement(status.severity === "danger" ? "circle-x" : "triangle-alert"));
1438
+ }
1439
+ if (text.textContent !== status.text) text.textContent = status.text;
1440
+ element.hidden = false;
1441
+ }
1442
+ return {
1443
+ element,
1444
+ set(status, delayMs) {
1445
+ if (status === null) {
1446
+ cancelPending();
1447
+ element.hidden = true;
1448
+ return;
1449
+ }
1450
+ if (!element.hidden) {
1451
+ show(status);
1452
+ return;
1453
+ }
1454
+ if (pending) {
1455
+ pending.status = status;
1456
+ return;
1457
+ }
1458
+ if (delayMs > 0) {
1459
+ const entry = { status, timer: setTimeout(() => show(entry.status), delayMs) };
1460
+ pending = entry;
1461
+ } else {
1462
+ show(status);
1463
+ }
1464
+ },
1465
+ destroy() {
1466
+ cancelPending();
1467
+ element.remove();
1468
+ }
1469
+ };
1470
+ }
1471
+
1472
+ // src/dom/tooltip.ts
1473
+ function createTooltip({ root, layer, id }) {
1474
+ const ownerDocument = root.ownerDocument;
1475
+ const label = createElement(ownerDocument, "span", { class: "cd-shell-tooltip-label" });
1476
+ const shortcut = createElement(ownerDocument, "kbd", { class: "cd-shell-kbd" });
1477
+ const element = createElement(ownerDocument, "div", { class: "cd-shell-tooltip", role: "tooltip", id });
1478
+ element.hidden = true;
1479
+ element.append(label, shortcut);
1480
+ layer.append(element);
1481
+ let shown;
1482
+ let pending;
1483
+ let warmUntil = 0;
1484
+ let pressed;
1485
+ const detachers = /* @__PURE__ */ new Set();
1486
+ function cancelPending() {
1487
+ if (pending) clearTimeout(pending.timer);
1488
+ pending = void 0;
1489
+ }
1490
+ function resolve(source) {
1491
+ return typeof source === "function" ? source() : source;
1492
+ }
1493
+ function place(control) {
1494
+ const island = control.closest("[data-chrome]") ?? control;
1495
+ const point = placeTooltip({
1496
+ anchor: boxRelativeTo(control, root),
1497
+ island: boxRelativeTo(island, root),
1498
+ tooltip: { width: element.offsetWidth, height: element.offsetHeight },
1499
+ container: { width: root.clientWidth, height: root.clientHeight }
1500
+ });
1501
+ element.style.left = `${point.x}px`;
1502
+ element.style.top = `${point.y}px`;
1503
+ }
1504
+ function hide() {
1505
+ cancelPending();
1506
+ if (!shown) return;
1507
+ const { control, describedBy } = shown;
1508
+ shown = void 0;
1509
+ element.hidden = true;
1510
+ if (describedBy === null) control.removeAttribute("aria-describedby");
1511
+ else control.setAttribute("aria-describedby", describedBy);
1512
+ }
1513
+ function show(control, source) {
1514
+ cancelPending();
1515
+ const content = resolve(source);
1516
+ if (!content || !control.isConnected) {
1517
+ if (shown?.control === control) hide();
1518
+ return;
1519
+ }
1520
+ if (shown && shown.control !== control) hide();
1521
+ label.textContent = content.label;
1522
+ shortcut.textContent = content.shortcut ?? "";
1523
+ shortcut.hidden = !content.shortcut;
1524
+ element.hidden = false;
1525
+ if (!shown) {
1526
+ const describedBy = control.getAttribute("aria-describedby");
1527
+ shown = { control, describedBy };
1528
+ const saysMore = !accessibleName(control).includes(content.label) || Boolean(content.shortcut) && !control.hasAttribute("aria-keyshortcuts");
1529
+ if (saysMore) {
1530
+ control.setAttribute("aria-describedby", [describedBy, id].filter(Boolean).join(" "));
1531
+ }
1532
+ }
1533
+ place(control);
1534
+ }
1535
+ function onEnter(control, source, event) {
1536
+ if (event.pointerType === "touch" || pressed === control) return;
1537
+ if (shown || Date.now() < warmUntil) {
1538
+ show(control, source);
1539
+ return;
1540
+ }
1541
+ cancelPending();
1542
+ pending = { control, timer: setTimeout(() => show(control, source), TOOLTIP_DELAY_MS) };
1543
+ }
1544
+ function onLeave(control) {
1545
+ if (pressed === control) pressed = void 0;
1546
+ if (pending?.control === control) cancelPending();
1547
+ if (shown?.control === control) {
1548
+ hide();
1549
+ warmUntil = Date.now() + TOOLTIP_DELAY_MS;
1550
+ }
1551
+ }
1552
+ function onFocus(control, source) {
1553
+ if (hasVisibleFocus(control)) show(control, source);
1554
+ }
1555
+ function onBlur(control) {
1556
+ if (pending?.control === control) cancelPending();
1557
+ if (shown?.control === control) hide();
1558
+ }
1559
+ function onPress(control, event) {
1560
+ if (event.type === "pointerdown") pressed = control;
1561
+ if (pending?.control === control) cancelPending();
1562
+ if (shown?.control === control) hide();
1563
+ }
1564
+ function attach(control, source) {
1565
+ const enter = (event) => onEnter(control, source, event);
1566
+ const leave = () => onLeave(control);
1567
+ const focus = () => onFocus(control, source);
1568
+ const blur = () => onBlur(control);
1569
+ const press = (event) => onPress(control, event);
1570
+ control.addEventListener("pointerenter", enter);
1571
+ control.addEventListener("pointerleave", leave);
1572
+ control.addEventListener("focus", focus);
1573
+ control.addEventListener("blur", blur);
1574
+ control.addEventListener("pointerdown", press);
1575
+ control.addEventListener("click", press);
1576
+ const detach = () => {
1577
+ if (!detachers.delete(detach)) return;
1578
+ control.removeEventListener("pointerenter", enter);
1579
+ control.removeEventListener("pointerleave", leave);
1580
+ control.removeEventListener("focus", focus);
1581
+ control.removeEventListener("blur", blur);
1582
+ control.removeEventListener("pointerdown", press);
1583
+ control.removeEventListener("click", press);
1584
+ if (shown?.control === control || pending?.control === control) hide();
1585
+ if (pressed === control) pressed = void 0;
1586
+ };
1587
+ detachers.add(detach);
1588
+ return detach;
1589
+ }
1590
+ function delegate(container, selector, content) {
1591
+ let hovered;
1592
+ const controlOf = (target) => {
1593
+ if (!target || typeof target.closest !== "function") return void 0;
1594
+ const match = target.closest(selector);
1595
+ return match && container.contains(match) ? match : void 0;
1596
+ };
1597
+ const sourceOf = (control) => () => content(control);
1598
+ const over = (event) => {
1599
+ const control = controlOf(event.target);
1600
+ if (control === hovered) return;
1601
+ if (hovered) onLeave(hovered);
1602
+ hovered = control;
1603
+ if (control) onEnter(control, sourceOf(control), event);
1604
+ };
1605
+ const out = (event) => {
1606
+ if (!hovered) return;
1607
+ if (controlOf(event.relatedTarget) === hovered) return;
1608
+ onLeave(hovered);
1609
+ hovered = void 0;
1610
+ };
1611
+ const focusIn = (event) => {
1612
+ const control = controlOf(event.target);
1613
+ if (control) onFocus(control, sourceOf(control));
1614
+ };
1615
+ const focusOut = (event) => {
1616
+ const control = controlOf(event.target);
1617
+ if (control) onBlur(control);
1618
+ };
1619
+ const press = (event) => {
1620
+ const control = controlOf(event.target);
1621
+ if (control) onPress(control, event);
1622
+ };
1623
+ container.addEventListener("pointerover", over);
1624
+ container.addEventListener("pointerout", out);
1625
+ container.addEventListener("focusin", focusIn);
1626
+ container.addEventListener("focusout", focusOut);
1627
+ container.addEventListener("pointerdown", press);
1628
+ container.addEventListener("click", press);
1629
+ const detach = () => {
1630
+ if (!detachers.delete(detach)) return;
1631
+ container.removeEventListener("pointerover", over);
1632
+ container.removeEventListener("pointerout", out);
1633
+ container.removeEventListener("focusin", focusIn);
1634
+ container.removeEventListener("focusout", focusOut);
1635
+ container.removeEventListener("pointerdown", press);
1636
+ container.removeEventListener("click", press);
1637
+ if (shown && container.contains(shown.control) || pending && container.contains(pending.control)) {
1638
+ hide();
1639
+ }
1640
+ hovered = void 0;
1641
+ };
1642
+ detachers.add(detach);
1643
+ return detach;
1644
+ }
1645
+ return {
1646
+ element,
1647
+ attach,
1648
+ delegate,
1649
+ hide,
1650
+ destroy() {
1651
+ hide();
1652
+ for (const detach of [...detachers]) detach();
1653
+ element.remove();
1654
+ }
1655
+ };
1656
+ }
1657
+
1658
+ // src/dom/zoom-panel.ts
1659
+ function createZoomPanel(context) {
1660
+ const { ownerDocument, messages, lang, platform, tooltip } = context;
1661
+ let target = null;
1662
+ let unsubscribe;
1663
+ let frame;
1664
+ const detachers = [];
1665
+ function button(zoom, key, tooltipLabel, options) {
1666
+ const element2 = createElement(ownerDocument, "button", {
1667
+ type: "button",
1668
+ class: options.className ?? "cd-shell-target",
1669
+ "data-zoom": zoom,
1670
+ "aria-label": options.label,
1671
+ "aria-keyshortcuts": ariaKeyShortcuts(key, platform)
1672
+ });
1673
+ if (options.icon) element2.append(iconElement(options.icon));
1674
+ element2.addEventListener("click", () => run(key));
1675
+ detachers.push(
1676
+ tooltip.attach(element2, {
1677
+ label: tooltipLabel,
1678
+ shortcut: shortcutLabel(key, platform, messages.keyNames)
1679
+ })
1680
+ );
1681
+ return element2;
1682
+ }
1683
+ const zoomOut = button("out", "zoomOut", messages.zoomOut, { label: messages.zoomOut, icon: "minus" });
1684
+ const readout = button("actual", "zoomToActual", messages.zoomToActual(formatPercent(1, lang)), {
1685
+ className: "cd-shell-target cd-shell-zoom-readout"
1686
+ });
1687
+ const zoomIn = button("in", "zoomIn", messages.zoomIn, { label: messages.zoomIn, icon: "plus" });
1688
+ const fit = button("fit", "fit", messages.fit, { label: messages.fit, icon: "maximize" });
1689
+ const separator = createElement(ownerDocument, "span", {
1690
+ class: "cd-shell-zoom-separator",
1691
+ "aria-hidden": "true"
1692
+ });
1693
+ const element = createElement(
1694
+ ownerDocument,
1695
+ "div",
1696
+ {
1697
+ class: "cd-shell-zoom cd-shell-island",
1698
+ "data-chrome": "zoom",
1699
+ role: "group",
1700
+ "aria-label": messages.zoom
1701
+ },
1702
+ zoomOut,
1703
+ readout,
1704
+ zoomIn,
1705
+ separator,
1706
+ fit
1707
+ );
1708
+ function render() {
1709
+ frame = void 0;
1710
+ const zoom = target?.zoom() ?? 1;
1711
+ const state = zoomReadout(zoom, lang, messages);
1712
+ if (readout.textContent !== state.text) readout.textContent = state.text;
1713
+ readout.setAttribute("aria-label", state.ariaLabel);
1714
+ toggleAttribute(zoomOut, "aria-disabled", (!target || !state.canZoomOut) && "true");
1715
+ toggleAttribute(zoomIn, "aria-disabled", (!target || !state.canZoomIn) && "true");
1716
+ toggleAttribute(fit, "aria-disabled", (!target || !target.canFit()) && "true");
1717
+ }
1718
+ function schedule() {
1719
+ if (frame === void 0) frame = requestAnimationFrame(render);
1720
+ }
1721
+ function cancelFrame() {
1722
+ if (frame !== void 0) cancelAnimationFrame(frame);
1723
+ frame = void 0;
1724
+ }
1725
+ function run(key) {
1726
+ if (!target) return;
1727
+ const zoom = target.zoom();
1728
+ switch (key) {
1729
+ case "zoomIn":
1730
+ if (canZoomIn(zoom)) target.stepZoom(1);
1731
+ break;
1732
+ case "zoomOut":
1733
+ if (canZoomOut(zoom)) target.stepZoom(-1);
1734
+ break;
1735
+ case "zoomToActual":
1736
+ if (zoom !== 1) target.zoomToActual();
1737
+ break;
1738
+ case "fit":
1739
+ if (target.canFit()) target.fit();
1740
+ break;
1741
+ }
1742
+ }
1743
+ render();
1744
+ return {
1745
+ element,
1746
+ run,
1747
+ setTarget(next) {
1748
+ unsubscribe?.();
1749
+ unsubscribe = void 0;
1750
+ cancelFrame();
1751
+ target = next;
1752
+ if (next) unsubscribe = next.onChange(schedule);
1753
+ render();
1754
+ },
1755
+ destroy() {
1756
+ unsubscribe?.();
1757
+ unsubscribe = void 0;
1758
+ cancelFrame();
1759
+ target = null;
1760
+ for (const detach of detachers) detach();
1761
+ element.remove();
1762
+ }
1763
+ };
1764
+ }
1765
+
1766
+ // src/dom/mount.ts
1767
+ var MOUNT_COUNTER = /* @__PURE__ */ Symbol.for("@miragon/modeler-shell.mounts");
1768
+ function nextMountIndex() {
1769
+ const scope = globalThis;
1770
+ const index = (scope[MOUNT_COUNTER] ?? 0) + 1;
1771
+ scope[MOUNT_COUNTER] = index;
1772
+ return index;
1773
+ }
1774
+ var DEFAULT_THEMES = ["light"];
1775
+ function languageOf(root, host, requested) {
1776
+ if (requested) return requested;
1777
+ const declared = host === "embedded" ? root.getAttribute("lang") : root.closest("[lang]")?.getAttribute("lang");
1778
+ return declared || "en";
1779
+ }
1780
+ function hostKontrast(root) {
1781
+ const carrier = root.parentElement?.closest("[data-cd-theme], [data-cd-kontrast]");
1782
+ return carrier?.getAttribute("data-cd-kontrast") === "hoch" ? "hoch" : null;
1783
+ }
1784
+ function mountShell(root, options) {
1785
+ const themes = options.themes ?? DEFAULT_THEMES;
1786
+ if (themes.length === 0) throw new Error("@miragon/modeler-shell: mountShell needs at least one theme");
1787
+ const ledger = new Ledger();
1788
+ try {
1789
+ return renderShell(root, options, themes, ledger);
1790
+ } catch (error) {
1791
+ ledger.close();
1792
+ throw error;
1793
+ }
1794
+ }
1795
+ function renderShell(root, options, themes, ledger) {
1796
+ const ownerDocument = root.ownerDocument;
1797
+ const { host } = options;
1798
+ const index = nextMountIndex();
1799
+ const messages = resolveMessages(options.messages);
1800
+ const platform = options.platform ?? detectPlatform(globalThis.navigator);
1801
+ const lang = languageOf(root, host, options.lang);
1802
+ const keyScope = options.keyScope ?? (host === "embedded" ? "root" : "document");
1803
+ ledger.addClass(root, "cd-shell");
1804
+ ledger.attribute(root, "data-cd-shell-host", host);
1805
+ ledger.attribute(root, "data-palette", "top");
1806
+ if (host === "embedded") {
1807
+ ledger.attribute(root, "lang", lang);
1808
+ carryOwnMode(root, themes[0] ?? "light", ledger);
1809
+ } else {
1810
+ ledger.addClass(ownerDocument.documentElement, "cd-shell-document");
1811
+ }
1812
+ const setState = (name, value) => {
1813
+ if (!ledger.isClosed) ledger.attribute(root, name, value);
1814
+ };
1815
+ const layer = createElement(ownerDocument, "div", { class: "cd-shell-layer" });
1816
+ ledger.defer(() => layer.remove());
1817
+ const tooltip = createTooltip({ root, layer, id: `cd-shell-tooltip-${index}` });
1818
+ ledger.defer(() => tooltip.destroy());
1819
+ let readOnly = false;
1820
+ let readOnlyShown = false;
1821
+ let readOnlyTimer;
1822
+ ledger.defer(() => clearTimeout(readOnlyTimer));
1823
+ const legend = options.legend ? createLegend({
1824
+ ownerDocument,
1825
+ messages,
1826
+ tooltip,
1827
+ titleId: `cd-shell-legend-title-${index}`,
1828
+ options: options.legend,
1829
+ onChange(open) {
1830
+ setState("data-legend", open ? "open" : null);
1831
+ placeHint();
1832
+ menu.refresh();
1833
+ },
1834
+ focusAfterClose: () => menu.trigger.focus()
1835
+ }) : void 0;
1836
+ if (legend) ledger.defer(() => legend.destroy());
1837
+ const legendEntry = legend && options.legend?.menuEntry !== false ? {
1838
+ kind: "checkbox",
1839
+ id: "legend",
1840
+ label: messages.legend,
1841
+ checked: () => legend.isOpen(),
1842
+ onToggle: (checked) => legend.setOpen(checked),
1843
+ keepEnabledWhenReadOnly: true
1844
+ } : void 0;
1845
+ const shellModelEntries = legendEntry ? [legendEntry] : [];
1846
+ const menu = createMenu({
1847
+ root,
1848
+ layer,
1849
+ id: `cd-shell-menu-${index}`,
1850
+ messages,
1851
+ tooltip,
1852
+ groups: options.menu ?? {},
1853
+ shellModelEntries: () => shellModelEntries,
1854
+ isReadOnly: () => readOnly
1855
+ });
1856
+ ledger.defer(() => menu.destroy());
1857
+ ledger.defer(tooltip.attach(menu.trigger, () => menu.isOpen() ? null : { label: messages.menu }));
1858
+ const canvas = options.canvas ?? createElement(ownerDocument, "div");
1859
+ ledger.addClass(canvas, "cd-shell-canvas");
1860
+ if (!options.canvas) ledger.defer(() => canvas.remove());
1861
+ function moveFocusOutOf(island) {
1862
+ if (!island.contains(ownerDocument.activeElement)) return;
1863
+ const canvasTarget = canvas.querySelector("svg[tabindex]");
1864
+ canvasTarget?.focus({ preventScroll: true });
1865
+ if (canvasTarget && ownerDocument.activeElement === canvasTarget) return;
1866
+ if (!menu.trigger.hidden) menu.trigger.focus({ preventScroll: true });
1867
+ }
1868
+ const status = createStatusBox(ownerDocument);
1869
+ ledger.defer(() => status.destroy());
1870
+ const hint = options.emptyHint ? createEmptyHint(ownerDocument, messages, options.emptyHint) : void 0;
1871
+ if (hint) ledger.defer(() => hint.destroy());
1872
+ const inspector = options.inspector ? createInspector(ownerDocument, options.inspector, (open) => {
1873
+ setState("data-inspector", open ? "open" : null);
1874
+ placeHint();
1875
+ }) : void 0;
1876
+ if (inspector) ledger.defer(() => inspector.destroy());
1877
+ const pill = options.problems ? createProblemsPill({
1878
+ ownerDocument,
1879
+ messages,
1880
+ lang,
1881
+ tooltip,
1882
+ showTooltip: host === "ide",
1883
+ options: options.problems
1884
+ }) : void 0;
1885
+ if (pill) ledger.defer(() => pill.destroy());
1886
+ const zoomPanel = createZoomPanel({ ownerDocument, messages, lang, platform, tooltip });
1887
+ ledger.defer(() => zoomPanel.destroy());
1888
+ root.prepend(menu.trigger, canvas);
1889
+ root.append(
1890
+ ...[
1891
+ status.element,
1892
+ hint?.element,
1893
+ inspector?.element,
1894
+ pill?.element,
1895
+ legend?.element,
1896
+ zoomPanel.element
1897
+ ].filter((element) => element !== void 0),
1898
+ layer
1899
+ );
1900
+ let palette;
1901
+ let paletteRow = "top";
1902
+ function containerSize() {
1903
+ return { width: root.clientWidth, height: root.clientHeight };
1904
+ }
1905
+ function menuRight() {
1906
+ const box = boxRelativeTo(menu.trigger, root);
1907
+ return box.width > 0 ? box.x + box.width : SHELL.edge + SHELL.row;
1908
+ }
1909
+ function paletteWidth() {
1910
+ const element = palette?.registration.element;
1911
+ return element ? element.offsetWidth - element.clientWidth + element.scrollWidth : 0;
1912
+ }
1913
+ function currentFitInsets() {
1914
+ const container = containerSize();
1915
+ const layout = {
1916
+ container,
1917
+ host,
1918
+ paletteRow,
1919
+ problemsVisible: pill?.isShown() ?? false
1920
+ };
1921
+ if (legend?.isOpen()) layout.legendWidth = legend.element.getBoundingClientRect().width;
1922
+ if (inspector?.isOpen()) {
1923
+ layout.inspector = container.width > SHELL.narrow ? {} : { sheetTop: boxRelativeTo(inspector.element, root).y };
1924
+ }
1925
+ return fitInsets(layout);
1926
+ }
1927
+ function placeHint() {
1928
+ if (hint?.isShown()) hint.place(currentFitInsets());
1929
+ }
1930
+ function updateLayout() {
1931
+ if (ledger.isClosed) return;
1932
+ const size = containerSize();
1933
+ if (size.width === 0 || size.height === 0) return;
1934
+ paletteRow = decidePaletteRow({
1935
+ containerWidth: size.width,
1936
+ paletteWidth: paletteWidth(),
1937
+ menuRight: menuRight()
1938
+ });
1939
+ setState("data-palette", paletteRow);
1940
+ if (legend && !legend.isStarted()) legend.start(options.legend?.open ?? legendStartsOpen(size.height));
1941
+ placeHint();
1942
+ }
1943
+ const ResizeObserverClass = globalThis.ResizeObserver;
1944
+ const resizeObserver = ResizeObserverClass ? new ResizeObserverClass(() => updateLayout()) : void 0;
1945
+ resizeObserver?.observe(root);
1946
+ ledger.defer(() => resizeObserver?.disconnect());
1947
+ if (legend && options.legend?.open !== void 0) legend.start(options.legend.open);
1948
+ updateLayout();
1949
+ const keyTarget = keyScope === "root" ? root : ownerDocument;
1950
+ const onKeydown = createKeyListener({
1951
+ host,
1952
+ dialogScope: keyTarget,
1953
+ menuOpen: () => menu.isOpen(),
1954
+ closeMenu() {
1955
+ const active = ownerDocument.activeElement;
1956
+ menu.close({ restoreFocus: active === menu.trigger || menu.dropdown.contains(active) });
1957
+ },
1958
+ hideTooltip: () => tooltip.hide(),
1959
+ run: (key) => zoomPanel.run(key)
1960
+ });
1961
+ keyTarget.addEventListener("keydown", onKeydown);
1962
+ ledger.defer(() => keyTarget.removeEventListener("keydown", onKeydown));
1963
+ function readOnlyState() {
1964
+ return { locked: readOnly, shown: readOnlyShown };
1965
+ }
1966
+ function applyReadOnly() {
1967
+ setState("data-readonly", readOnlyShown ? "" : null);
1968
+ palette?.registration.setReadOnly(readOnlyState());
1969
+ menu.refresh();
1970
+ }
1971
+ function showReadOnly() {
1972
+ readOnlyTimer = void 0;
1973
+ if (ledger.isClosed || !readOnly) return;
1974
+ readOnlyShown = true;
1975
+ applyReadOnly();
1976
+ }
1977
+ function unregisterPalette() {
1978
+ if (!palette) return;
1979
+ palette.detachTooltip();
1980
+ resizeObserver?.unobserve(palette.registration.element);
1981
+ palette = void 0;
1982
+ }
1983
+ ledger.defer(unregisterPalette);
1984
+ const menuHandle = {
1985
+ trigger: menu.trigger,
1986
+ dropdown: menu.dropdown,
1987
+ isOpen: () => menu.isOpen(),
1988
+ open: (focus) => menu.open(focus),
1989
+ close: (closeOptions) => menu.close(closeOptions),
1990
+ refresh: () => menu.refresh(),
1991
+ setGroups: (groups) => {
1992
+ if (!ledger.isClosed) menu.setGroups(groups);
1993
+ }
1994
+ };
1995
+ const tooltipHandle = {
1996
+ attach: (control, content) => ledger.isClosed ? () => {
1997
+ } : tooltip.attach(control, content),
1998
+ hide: () => tooltip.hide()
1999
+ };
2000
+ const legendHandle = legend && {
2001
+ element: legend.element,
2002
+ body: legend.body,
2003
+ isOpen: () => legend.isOpen(),
2004
+ setOpen: (open) => {
2005
+ if (!ledger.isClosed) legend.setOpen(open);
2006
+ },
2007
+ toggle: () => {
2008
+ if (!ledger.isClosed) legend.toggle();
2009
+ }
2010
+ };
2011
+ const inspectorHandle = inspector && {
2012
+ element: inspector.element,
2013
+ isOpen: () => inspector.isOpen(),
2014
+ open: () => {
2015
+ if (!ledger.isClosed) inspector.open();
2016
+ },
2017
+ close: () => {
2018
+ if (ledger.isClosed) return;
2019
+ if (inspector.isOpen()) moveFocusOutOf(inspector.element);
2020
+ inspector.close();
2021
+ }
2022
+ };
2023
+ return {
2024
+ root,
2025
+ canvas,
2026
+ host,
2027
+ lang,
2028
+ platform,
2029
+ messages,
2030
+ menu: menuHandle,
2031
+ tooltip: tooltipHandle,
2032
+ legend: legendHandle,
2033
+ inspector: inspectorHandle,
2034
+ setZoomTarget(target) {
2035
+ if (!ledger.isClosed) zoomPanel.setTarget(target);
2036
+ },
2037
+ fitInsets: currentFitInsets,
2038
+ registerPalette(registration) {
2039
+ if (ledger.isClosed) return () => {
2040
+ };
2041
+ unregisterPalette();
2042
+ palette = {
2043
+ registration,
2044
+ detachTooltip: tooltip.delegate(
2045
+ registration.element,
2046
+ ".entry",
2047
+ (entry) => registration.entryTooltip(entry)
2048
+ )
2049
+ };
2050
+ resizeObserver?.observe(registration.element);
2051
+ registration.setReadOnly(readOnlyState());
2052
+ updateLayout();
2053
+ const registered = palette;
2054
+ return () => {
2055
+ if (palette !== registered) return;
2056
+ unregisterPalette();
2057
+ updateLayout();
2058
+ };
2059
+ },
2060
+ setProblems(summary) {
2061
+ if (ledger.isClosed || !pill) return;
2062
+ pill.set(summary);
2063
+ setState("data-problems", pill.isShown() ? "shown" : null);
2064
+ placeHint();
2065
+ },
2066
+ setStatus(next, statusOptions) {
2067
+ if (!ledger.isClosed) status.set(next, statusOptions?.delayMs ?? 0);
2068
+ },
2069
+ setEmptyHint(visible) {
2070
+ if (ledger.isClosed || !hint) return;
2071
+ if (!visible && hint.isShown()) moveFocusOutOf(hint.element);
2072
+ hint.setShown(visible);
2073
+ placeHint();
2074
+ },
2075
+ setReadOnly(next, readOnlyOptions) {
2076
+ if (ledger.isClosed) return;
2077
+ if (!next) {
2078
+ clearTimeout(readOnlyTimer);
2079
+ readOnlyTimer = void 0;
2080
+ readOnly = false;
2081
+ readOnlyShown = false;
2082
+ } else {
2083
+ readOnly = true;
2084
+ const delayMs = readOnlyOptions?.delayMs ?? 0;
2085
+ if (!readOnlyShown && readOnlyTimer === void 0) {
2086
+ if (delayMs > 0) readOnlyTimer = setTimeout(showReadOnly, delayMs);
2087
+ else readOnlyShown = true;
2088
+ }
2089
+ }
2090
+ applyReadOnly();
2091
+ },
2092
+ isReadOnly: () => readOnly,
2093
+ destroy() {
2094
+ ledger.close();
2095
+ }
2096
+ };
2097
+ }
2098
+ function carryOwnMode(root, theme, ledger) {
2099
+ ledger.attribute(root, "data-cd-theme", theme);
2100
+ const mirror = () => {
2101
+ if (ledger.isClosed) return;
2102
+ const kontrast = hostKontrast(root);
2103
+ if (root.getAttribute("data-cd-kontrast") !== kontrast)
2104
+ ledger.attribute(root, "data-cd-kontrast", kontrast);
2105
+ };
2106
+ mirror();
2107
+ const MutationObserverClass = globalThis.MutationObserver;
2108
+ if (!MutationObserverClass) return;
2109
+ const observer = new MutationObserverClass(mirror);
2110
+ observer.observe(root.ownerDocument.documentElement, {
2111
+ attributes: true,
2112
+ attributeFilter: ["data-cd-theme", "data-cd-kontrast"],
2113
+ subtree: true
2114
+ });
2115
+ ledger.defer(() => observer.disconnect());
2116
+ }
2117
+ // Annotate the CommonJS export names for ESM import in node:
2118
+ 0 && (module.exports = {
2119
+ CHROME_ISLANDS,
2120
+ FIT_ANIMATION_MS,
2121
+ ICONS,
2122
+ INVALID_STATUS_DELAY_MS,
2123
+ MAX_FIT_ZOOM,
2124
+ PALETTE_CLEARANCE,
2125
+ PALETTE_TOOL_ICON,
2126
+ REDUCED_MOTION_CLASS,
2127
+ SHELL,
2128
+ SHELL_TOKENS,
2129
+ TEXT_FIELD_SELECTOR,
2130
+ TOOLTIP_DELAY_MS,
2131
+ VIEW_TOLERANCE,
2132
+ ZOOM_MAX,
2133
+ ZOOM_MIN,
2134
+ appearanceAttributes,
2135
+ appearanceFromBodyClasses,
2136
+ appearanceFromColorThemeKind,
2137
+ ariaKeyShortcuts,
2138
+ ariaKeyShortcutsFromLabel,
2139
+ canZoomIn,
2140
+ canZoomOut,
2141
+ decideKey,
2142
+ decidePaletteRow,
2143
+ defaultMessages,
2144
+ detectPlatform,
2145
+ easeStandard,
2146
+ fitInsets,
2147
+ fitViewbox,
2148
+ formatNumber,
2149
+ formatPercent,
2150
+ freeRectCenter,
2151
+ iconElement,
2152
+ iconMarkup,
2153
+ interpolateViewbox,
2154
+ isUndoRedoKey,
2155
+ legendStartsOpen,
2156
+ matchChromeKey,
2157
+ mountShell,
2158
+ placeTooltip,
2159
+ reducedMotionFromBodyClasses,
2160
+ requestedAppearance,
2161
+ resolveAppearance,
2162
+ resolveMessages,
2163
+ sameView,
2164
+ shellSizesFromTokens,
2165
+ shortcutLabel,
2166
+ zoomReadout
2167
+ });
2168
+ /*! @miragon/modeler-shell icon geometry
2169
+ * Lucide (lucide-static 1.59.0) @license ISC
2170
+ * Copyright (c) 2026 Lucide Icons and Contributors
2171
+ * Derived from Feather, as Lucide's LICENSE lists them: check, circle-x (x-circle), maximize,
2172
+ * minus, plus, triangle-alert (alert-triangle) and x. Feather @license MIT
2173
+ * Copyright (c) 2013-present Cole Bemis
2174
+ * Full notices: THIRD_PARTY_NOTICES.md of @miragon/modeler-shell */
2175
+ //# sourceMappingURL=index.cjs.map