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