@miragon/modeler-shell 0.1.0 → 0.2.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 +1051 -84
- package/README.md +162 -31
- package/dist/appearance-BBaM_4bD.d.ts +323 -0
- package/dist/appearance-XX3JC3pI.d.cts +323 -0
- package/dist/{chunk-6LQOFACK.js → chunk-66HCSJNY.js} +1247 -31
- package/dist/chunk-66HCSJNY.js.map +1 -0
- package/dist/{chunk-RJRFR6N3.js → chunk-KYDG5DMX.js} +2 -2
- package/dist/{chunk-5JSZ4VGA.js → chunk-OADW3GPA.js} +240 -6
- package/dist/chunk-OADW3GPA.js.map +1 -0
- package/dist/core.cjs +260 -6
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +81 -6
- package/dist/core.d.ts +81 -6
- package/dist/core.js +43 -3
- package/dist/diagram-js.cjs +34 -2
- package/dist/diagram-js.cjs.map +1 -1
- package/dist/diagram-js.d.cts +10 -3
- package/dist/diagram-js.d.ts +10 -3
- package/dist/diagram-js.js +14 -2
- package/dist/diagram-js.js.map +1 -1
- package/dist/index.cjs +1654 -197
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -9
- package/dist/index.d.ts +18 -9
- package/dist/index.js +44 -4
- package/dist/shell.css +692 -3
- package/dist/testing-vscode-stub.js +2 -2
- package/dist/testing.cjs +2849 -550
- package/dist/testing.cjs.map +1 -1
- package/dist/testing.d.cts +75 -9
- package/dist/testing.d.ts +75 -9
- package/dist/testing.js +2744 -558
- package/dist/testing.js.map +1 -1
- package/dist/toasts-Bp0REqdq.d.cts +142 -0
- package/dist/toasts-Bp0REqdq.d.ts +142 -0
- package/dist/{types-OuFlqZp0.d.cts → types-CnRrINRs.d.cts} +170 -9
- package/dist/{types-DnZn9CPw.d.ts → types-PuDZczzy.d.ts} +170 -9
- package/dist/vscode-extension.js +2 -2
- package/dist/vscode-webview.cjs +1541 -178
- package/dist/vscode-webview.cjs.map +1 -1
- package/dist/vscode-webview.d.cts +6 -4
- package/dist/vscode-webview.d.ts +6 -4
- package/dist/vscode-webview.js +11 -2
- package/dist/vscode-webview.js.map +1 -1
- package/package.json +2 -2
- package/src/core/feedback/appearance.ts +141 -0
- package/src/core/feedback/problems.ts +23 -0
- package/src/core/feedback/toasts.ts +118 -0
- package/src/core/fit.ts +18 -3
- package/src/core/index.ts +43 -0
- package/src/core/keymap.ts +20 -0
- package/src/core/messages.ts +71 -0
- package/src/core/types.ts +24 -2
- package/src/core/web-chrome/bottom-row.ts +37 -0
- package/src/core/web-chrome/drop.ts +78 -0
- package/src/core/web-chrome/top-row.ts +66 -0
- package/src/diagram-js/service.ts +4 -1
- package/src/diagram-js/viewport.ts +19 -0
- package/src/dom/buttons.css +124 -0
- package/src/dom/feedback/appearance.ts +184 -0
- package/src/dom/feedback/feedback.css +435 -0
- package/src/dom/feedback/problems-popover.ts +287 -0
- package/src/dom/feedback/toasts.ts +431 -0
- package/src/dom/index.ts +13 -0
- package/src/dom/keys.ts +8 -0
- package/src/dom/menu.ts +28 -17
- package/src/dom/mount.ts +290 -20
- package/src/dom/problems.ts +5 -4
- package/src/dom/shell-dom.css +7 -4
- package/src/dom/types.ts +188 -6
- package/src/dom/web/drop-zone.ts +181 -0
- package/src/dom/web/empty-state.ts +144 -0
- package/src/dom/web/legal.ts +75 -0
- package/src/dom/web/menu-groups.ts +69 -0
- package/src/dom/web/primary.ts +124 -0
- package/src/dom/web/web.css +386 -0
- package/src/shell.css +3 -0
- package/src/testing/case-tools.ts +164 -0
- package/src/testing/cases.ts +209 -144
- package/src/testing/checks.ts +51 -15
- package/src/testing/facts.ts +56 -2
- package/src/testing/geometry.ts +206 -26
- package/src/testing/harness.ts +56 -7
- package/src/testing/index.ts +4 -0
- package/src/testing/page-tools.ts +24 -1
- package/src/testing/page.ts +10 -0
- package/src/testing/web-cases.ts +913 -0
- package/src/testing/web-checks.ts +1049 -0
- package/src/testing/web-facts.ts +713 -0
- package/src/vscode/webview/mount.ts +13 -1
- package/dist/chunk-5JSZ4VGA.js.map +0 -1
- package/dist/chunk-6LQOFACK.js.map +0 -1
- package/dist/keymap-CObQE6q6.d.cts +0 -151
- package/dist/keymap-DORzHnK-.d.ts +0 -151
- package/dist/types-B6NfkiUG.d.cts +0 -66
- package/dist/types-B6NfkiUG.d.ts +0 -66
- /package/dist/{chunk-RJRFR6N3.js.map → chunk-KYDG5DMX.js.map} +0 -0
package/README.md
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
The shared, framework-free chrome around Miragon diagram-js modelers: the menu button with its
|
|
4
4
|
dropdown, the horizontal palette skin, the chrome tooltip, the zoom panel with its keys and fit, the
|
|
5
|
-
problems pill, the status box, the empty-file hint, legend and inspector slots,
|
|
6
|
-
|
|
5
|
+
problems pill, the status box, the empty-file hint, legend and inspector slots, the plumbing to a
|
|
6
|
+
VS Code webview, and the web-app chrome (primary action, legal line, File and Appearance groups,
|
|
7
|
+
problems list, toasts, empty-state card, drop zone, the web app's mode). One implementation for every modeler and every host (VS Code webview, web app,
|
|
7
8
|
embedded in a foreign page), so the chrome looks and behaves the same everywhere.
|
|
8
9
|
|
|
9
10
|
- **Look and behaviour** follow the Miragon design system, guide
|
|
@@ -154,7 +155,7 @@ const shell = mountVsCodeShell(document.getElementById("app")!, {
|
|
|
154
155
|
menu: {
|
|
155
156
|
model: [/* Level; Type captions with keepEnabledWhenReadOnly, a view toggle */],
|
|
156
157
|
export: [/* Export as PNG, SVG */],
|
|
157
|
-
help: [/* Keyboard shortcuts… (the modeler's dialog
|
|
158
|
+
help: [/* Keyboard shortcuts… (the modeler's own dialog) */],
|
|
158
159
|
},
|
|
159
160
|
legend: {
|
|
160
161
|
render: renderLegend,
|
|
@@ -225,31 +226,94 @@ resize, delete, paste or label editor. Content the modeler adds to `shell.root`
|
|
|
225
226
|
shortcuts…" dialog) is the modeler's too: it removes it in its own teardown, for example on
|
|
226
227
|
`diagram.destroy`, since `shell.destroy()` removes only what the shell added. The complete Event
|
|
227
228
|
Storming example, including the export notification, is in [API.md][api]. Document sync, undo through
|
|
228
|
-
the document and diagnostics stay in the modeler for
|
|
229
|
+
the document and diagnostics stay in the modeler for now.
|
|
229
230
|
|
|
230
231
|
## Usage in a web app
|
|
231
232
|
|
|
233
|
+
The page's head writes the mode before the first paint. A light-only modeler keeps
|
|
234
|
+
`<html lang="en" data-cd-theme="light">` static and lets the build put the metas and the contrast
|
|
235
|
+
line in place of a marker in `index.html`:
|
|
236
|
+
|
|
237
|
+
```ts
|
|
238
|
+
// vite.config.ts
|
|
239
|
+
import { webHeadMarkup } from "@miragon/modeler-shell/core";
|
|
240
|
+
import tokens from "./src/theme/cd-tokens.generated.json" with { type: "json" };
|
|
241
|
+
|
|
242
|
+
const themeColors = { light: tokens.fest["--cd-blau"], dark: tokens.dunkel["--cd-ui-grund"] };
|
|
243
|
+
const modePlugin = {
|
|
244
|
+
name: "miragon-mode",
|
|
245
|
+
transformIndexHtml: (html: string) =>
|
|
246
|
+
html.replace("<!-- miragon-mode -->", webHeadMarkup({ themes: ["light"], themeColors })),
|
|
247
|
+
};
|
|
248
|
+
```
|
|
249
|
+
|
|
232
250
|
```ts
|
|
233
|
-
|
|
251
|
+
// src/main.ts
|
|
252
|
+
import {
|
|
253
|
+
commandKeyLabel,
|
|
254
|
+
defaultMessages,
|
|
255
|
+
detectPlatform,
|
|
256
|
+
mountShell,
|
|
257
|
+
problemSummary,
|
|
258
|
+
} from "@miragon/modeler-shell";
|
|
234
259
|
import { connectDiagramJs, shellModule } from "@miragon/modeler-shell/diagram-js";
|
|
235
260
|
|
|
236
|
-
//
|
|
261
|
+
// The diagram first: a modeler keyboard on document must run before the shell's key listener.
|
|
237
262
|
const modeler = new Modeler({ width: "100%", height: "100%", additionalModules: [shellModule] });
|
|
263
|
+
modeler.get("canvas");
|
|
264
|
+
const platform = detectPlatform(navigator);
|
|
238
265
|
const shell = mountShell(document.getElementById("app")!, {
|
|
239
266
|
host: "web",
|
|
240
267
|
canvas: modeler.container,
|
|
241
|
-
|
|
268
|
+
lang: "en",
|
|
269
|
+
themes: ["light"], // with "dark": also appearance: { themeColors }, and the menu gets Appearance
|
|
270
|
+
primary: { onSelect: share }, // "Share", top right; the label collapses before the palette moves
|
|
271
|
+
legal: { links: [{ label: "GitHub", href: "https://github.com/Miragon/…" }] },
|
|
272
|
+
file: {
|
|
273
|
+
newDiagram: { onSelect: newDiagram },
|
|
274
|
+
// ⌘O or Ctrl+O; also the start screen's "Open a file…" link
|
|
275
|
+
open: { onSelect: pickFile, shortcut: commandKeyLabel("O", platform, defaultMessages.keyNames) },
|
|
276
|
+
showExample: { onSelect: example },
|
|
277
|
+
},
|
|
278
|
+
menu: { model: [/* … */], export: [/* … */], help: [/* Keyboard shortcuts… */] },
|
|
279
|
+
problems: { onSelectItem: (item) => selectElement(item.elementId) },
|
|
242
280
|
legend: { render: renderLegend, open: storedLegendOpen(), onToggle: storeLegendOpen },
|
|
281
|
+
emptyState: {
|
|
282
|
+
title: "BPMN Modeler",
|
|
283
|
+
text: "Start a new diagram, or explore the example.",
|
|
284
|
+
komet: kometUrl, // the vendored, unchanged miragon-komet-blau.svg
|
|
285
|
+
onNewDiagram: newDiagram,
|
|
286
|
+
onShowExample: example,
|
|
287
|
+
},
|
|
288
|
+
dropZone: { accept: [".bpmn", "application/xml", "text/xml"], onDrop: ([file]) => openFile(file) },
|
|
243
289
|
});
|
|
244
290
|
const { viewport } = connectDiagramJs(shell, modeler, { contentBounds: () => elementBounds() });
|
|
245
|
-
|
|
246
|
-
|
|
291
|
+
|
|
292
|
+
// Every new document opens like the first, and the start screen goes with it.
|
|
293
|
+
async function openDocument(xml: string): Promise<void> {
|
|
294
|
+
viewport.newDocument();
|
|
295
|
+
const { warnings } = await modeler.importXML(xml);
|
|
296
|
+
viewport.afterImport();
|
|
297
|
+
shell.setEmptyState(false);
|
|
298
|
+
shell.setProblems(
|
|
299
|
+
problemSummary(warnings.map((warning) => ({ severity: "warning", message: warning.message }))),
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
// A fresh visit without a document in the link shows the start screen.
|
|
304
|
+
if (!linkedDocument) shell.setEmptyState(true);
|
|
305
|
+
shell.toast({ status: "success", text: "Link copied" }); // after a share
|
|
247
306
|
```
|
|
248
307
|
|
|
249
|
-
The `web` host
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
308
|
+
The `web` host renders the whole web column of guide §5.2: the primary action at the top right,
|
|
309
|
+
the legal line at the bottom left with pill and legend above it, the File group first and, for a
|
|
310
|
+
dark-capable modeler, the Appearance group in the menu, the problems list above the pill, toasts at
|
|
311
|
+
the bottom centre (announced through two live regions), the start screen and a page-wide drop zone.
|
|
312
|
+
It prevents the browser's page zoom for the chrome keys (outside text fields, modal dialogs and the
|
|
313
|
+
start screen) and fits with the web insets. The web app's
|
|
314
|
+
own menu, zoom, fit, toast, legal and empty-state markup goes away; what stays the modeler's is its
|
|
315
|
+
content: the share link, file reading, examples, the Model and Export entries and the shortcuts
|
|
316
|
+
dialog. See [API.md][api], "Web host (0.2)", and the complete Event Storming example there.
|
|
253
317
|
|
|
254
318
|
## Usage in a foreign page
|
|
255
319
|
|
|
@@ -259,8 +323,9 @@ modeler, and the page's `<html>` stays untouched. Until the `@miragon/modeler-ap
|
|
|
259
323
|
its `appearance` surface (a later 1.x minor), an embedded modeler has no appearance channel, so the
|
|
260
324
|
root carries its own `data-cd-theme` (the first of `themes`, light) and stays light inside a dark
|
|
261
325
|
host page; it takes the page's `data-cd-kontrast="hoch"` over from the nearest mode carrier above
|
|
262
|
-
it. The page's element styles
|
|
263
|
-
|
|
326
|
+
it. The page's element styles (`button { text-transform: uppercase }`) do not reach the chrome.
|
|
327
|
+
Toasts show at the bottom centre of the modeler, and the pill opens the problems list unless the
|
|
328
|
+
modeler passes `problems.onActivate` (an embedding host with a list of its own).
|
|
264
329
|
|
|
265
330
|
## The conformance kit
|
|
266
331
|
|
|
@@ -269,7 +334,9 @@ the geometry of every island (±1px), ARIA and the keyboard models, that every c
|
|
|
269
334
|
once and never reaches VS Code, that the canvas's own keys (Cmd/Ctrl+A) stay on the canvas, that
|
|
270
335
|
nothing is ever half-dark (the painted canvas surround and the palette's colours included), that
|
|
271
336
|
Enter on a creating palette entry adds one selected element, that an invalid file locks the canvas,
|
|
272
|
-
and that `destroy()` leaves nothing behind.
|
|
337
|
+
and that `destroy()` leaves nothing behind. Five more cases cover the web column (`webChrome`,
|
|
338
|
+
`emptyState`, `toasts`, `problemsList`, `dropZone`); in the IDE and embedded hosts they assert that
|
|
339
|
+
none of it shows.
|
|
273
340
|
|
|
274
341
|
For the `ide` host the suite serves a test page that puts a stand-in for VS Code around the
|
|
275
342
|
modeler's webview code: VS Code's default webview styles, `<html>` as `renderWebviewHtml` writes it,
|
|
@@ -352,6 +419,60 @@ test.describe("shell conformance", () => {
|
|
|
352
419
|
});
|
|
353
420
|
```
|
|
354
421
|
|
|
422
|
+
A web app's harness opens its page and adds the web column's hooks and the files to drop. The kit
|
|
423
|
+
emulates the system's colour scheme and contrast (`page.emulateMedia`) before every `open`, so the
|
|
424
|
+
page loads without a stored mode choice:
|
|
425
|
+
|
|
426
|
+
```ts
|
|
427
|
+
// e2e/shell-conformance.spec.ts of a web app
|
|
428
|
+
registerShellConformance(
|
|
429
|
+
{
|
|
430
|
+
host: "web",
|
|
431
|
+
themes: ["light"],
|
|
432
|
+
absent: ["inspector", "invalid"],
|
|
433
|
+
async open(page, { size }) {
|
|
434
|
+
await page.setViewportSize(size);
|
|
435
|
+
await page.goto("/?example");
|
|
436
|
+
await page.waitForFunction(() => window.appReady === true);
|
|
437
|
+
},
|
|
438
|
+
async destroy(page) {
|
|
439
|
+
await page.evaluate(() => window.app.destroy());
|
|
440
|
+
},
|
|
441
|
+
show: {
|
|
442
|
+
async legend(page) {
|
|
443
|
+
/* … */
|
|
444
|
+
},
|
|
445
|
+
async problems(page) {
|
|
446
|
+
/* setProblems with items, so the list has rows */
|
|
447
|
+
},
|
|
448
|
+
async status(page) {
|
|
449
|
+
/* … */
|
|
450
|
+
},
|
|
451
|
+
async emptyState(page) {
|
|
452
|
+
/* the start screen of a fresh visit */
|
|
453
|
+
},
|
|
454
|
+
async toast(page, toast) {
|
|
455
|
+
await page.evaluate(
|
|
456
|
+
({ status, text, actionLabel }) =>
|
|
457
|
+
window.app.shell.toast({
|
|
458
|
+
status,
|
|
459
|
+
text,
|
|
460
|
+
...(actionLabel ? { action: { label: actionLabel, onSelect() {} } } : {}),
|
|
461
|
+
}),
|
|
462
|
+
toast,
|
|
463
|
+
);
|
|
464
|
+
},
|
|
465
|
+
},
|
|
466
|
+
drop: {
|
|
467
|
+
accepted: { name: "board.json", type: "application/json", text: EXAMPLE_JSON },
|
|
468
|
+
rejected: { name: "notes.md", type: "", text: "# Notes" },
|
|
469
|
+
},
|
|
470
|
+
},
|
|
471
|
+
test,
|
|
472
|
+
{ onSkip: failOnSkips },
|
|
473
|
+
);
|
|
474
|
+
```
|
|
475
|
+
|
|
355
476
|
The harness opens the modeler with the appearance the kit requests, the way its host would; for
|
|
356
477
|
the `embedded` host the kit puts the foreign page itself in that mode. A skip names the harness
|
|
357
478
|
field it misses (`{ hook: "legend", reason }`; `hook: "destroy"` without a `destroy`; `hook: "open"`
|
|
@@ -399,23 +520,32 @@ host requests (`theme`, `kontrast=hoch`; embedded: the foreign page's own mode),
|
|
|
399
520
|
inside the stub of `./testing/vscode-stub`, and keeps view and legend state through
|
|
400
521
|
`acquireVsCodeApi()`.
|
|
401
522
|
|
|
523
|
+
The web demo (`/web.html`) is the same modeler as a Miragon web app passes it to `mountShell`: the
|
|
524
|
+
whole web column, the head markup of `webHeadMarkup` through the Vite config, and `window.__webDemo`
|
|
525
|
+
for the harness. Its query parameters: `themes=light,dark`, `empty` (the start screen of a fresh
|
|
526
|
+
visit), `legend` (`legend=open`), `inspector`, `problems` (the pill with its list) and `invalid`.
|
|
527
|
+
|
|
402
528
|
The e2e suite runs the conformance kit for the `ide`, `web` and `embedded` hosts with a light-only
|
|
403
529
|
and a dark-capable modeler, switches VS Code themes and the `colorTheme` setting at runtime, checks
|
|
404
530
|
VS Code's reduce motion, the chrome's typography against VS Code's default styles and a hostile
|
|
405
|
-
foreign page, the canvas slot's paint and focus ring,
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
|
411
|
-
|
|
|
412
|
-
| `src/
|
|
413
|
-
| `src/
|
|
414
|
-
| `src/
|
|
415
|
-
| `src
|
|
416
|
-
| `
|
|
417
|
-
| `
|
|
418
|
-
| `
|
|
531
|
+
foreign page, the canvas slot's paint and focus ring, the web chrome's colours, radii and states
|
|
532
|
+
(primary action, legal line, toasts, drop zone, empty state, high contrast and forced colours) and
|
|
533
|
+
the web demo's own handlers, and proves with saboteurs that every case of the kit fails when its
|
|
534
|
+
rule is broken.
|
|
535
|
+
|
|
536
|
+
| Directory | Module |
|
|
537
|
+
| --------------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
538
|
+
| `src/core` | DOM-free: fit math, keymap, zoom readout, appearance, sizes, texts |
|
|
539
|
+
| `src/dom` | `mountShell`: menu, zoom panel, tooltip, pill, status, slots, keys |
|
|
540
|
+
| `src/dom/web`, `src/core/web-chrome` | web column: primary action, legal line, File and Appearance groups, empty state, drop zone |
|
|
541
|
+
| `src/dom/feedback`, `src/core/feedback` | toasts, problems popover, the web app's mode |
|
|
542
|
+
| `src/diagram-js` | `shellModule`, viewport, `connectDiagramJs`, palette skin |
|
|
543
|
+
| `src/vscode` | extension side (HTML, theme bridge) and webview side |
|
|
544
|
+
| `src/testing` | the Playwright-driven conformance kit and the VS Code stub |
|
|
545
|
+
| `src/*.ts`, `src/shell.css` | the entry points |
|
|
546
|
+
| `test/` | package boundaries, token drift, the unit tests' helpers |
|
|
547
|
+
| `demo/`, `e2e/` | the demo modeler and the e2e suite |
|
|
548
|
+
| `fixtures/tokens` | the token files, vendored unchanged for the tests and the demo |
|
|
419
549
|
|
|
420
550
|
A module imports another one only through its `index.ts`; `src/core` imports nothing outside
|
|
421
551
|
itself and compiles without the DOM library. `test/package-boundaries.test.ts` enforces this, the
|
|
@@ -429,7 +559,8 @@ MIT. The inlined Lucide icons are ISC (© Lucide Icons and Contributors), and `c
|
|
|
429
559
|
Lucide's LICENSE lists them; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). `dist` keeps that
|
|
430
560
|
notice as a legal comment in the chunk with the icons, but a bundler's `legalComments` setting may
|
|
431
561
|
drop it: a VSIX or a web bundle that contains the shell carries the Lucide and Feather notices of
|
|
432
|
-
THIRD_PARTY_NOTICES.md itself.
|
|
562
|
+
THIRD_PARTY_NOTICES.md itself. Vite 8 (Rolldown with the Oxc minifier) drops legal comments by
|
|
563
|
+
default; `build.rolldownOptions.output.comments.legal: true` keeps the notice in the bundle.
|
|
433
564
|
|
|
434
565
|
[guide]: https://github.com/Miragon/corporate-identity/blob/main/plugins/miragon-brand/skills/modeler-tool-design/assets/modeler-design-system.md
|
|
435
566
|
[notes]: https://github.com/Miragon/modeler-api/blob/main/docs/shell/implementation-notes.md
|
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
import { d as ShellHost } from './toasts-Bp0REqdq.js';
|
|
2
|
+
import { T as Theme } from './appearance-D3FUcbu6.js';
|
|
3
|
+
|
|
4
|
+
/** The severity of one problem; the pill and the list show `circle-x` or `triangle-alert` (guide §5.10). */
|
|
5
|
+
type ProblemSeverity = "error" | "warning";
|
|
6
|
+
/** One row of the web app's problems list (guide §5.10). */
|
|
7
|
+
interface ProblemItem {
|
|
8
|
+
severity: ProblemSeverity;
|
|
9
|
+
/** 13px `--cd-ui-text`: "Unknown sticky type “evnt”." */
|
|
10
|
+
message: string;
|
|
11
|
+
/** The place in 13px `--cd-ui-text-leise` below the message: "Line 12", "“Order Placed”". */
|
|
12
|
+
location?: string | undefined;
|
|
13
|
+
/** The diagram element a click on the row selects (`ProblemsOptions.onSelectItem`). */
|
|
14
|
+
elementId?: string | undefined;
|
|
15
|
+
}
|
|
16
|
+
/** `ShellHandle.setProblems` input built from the items: count, errors and the items themselves. */
|
|
17
|
+
declare function problemSummary(items: readonly ProblemItem[]): {
|
|
18
|
+
count: number;
|
|
19
|
+
errors: number;
|
|
20
|
+
items: readonly ProblemItem[];
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/** Why a drag cannot be dropped (guide §9 Drop forbidden); `messages.dropForbidden` words each one. */
|
|
24
|
+
type DropForbiddenReason = "multiple" | "type" | "dialog";
|
|
25
|
+
/** One entry of `DataTransfer.items` as a drag shows it (names are not readable before the drop). */
|
|
26
|
+
interface DragItemLike {
|
|
27
|
+
kind: string;
|
|
28
|
+
type: string;
|
|
29
|
+
}
|
|
30
|
+
interface DragContext {
|
|
31
|
+
/** `DropZoneOptions.accept`: extensions (".storm") and MIME types ("application/json", "text/*"). */
|
|
32
|
+
accept: readonly string[];
|
|
33
|
+
/** `DropZoneOptions.multiple`. */
|
|
34
|
+
multiple: boolean;
|
|
35
|
+
/** A modal dialog is open (the shell's dialog test, as for the keys). */
|
|
36
|
+
dialogOpen: boolean;
|
|
37
|
+
}
|
|
38
|
+
/** What the drop zone shows for the drag over the page right now (guide §9). */
|
|
39
|
+
type DragVerdict = {
|
|
40
|
+
state: "none";
|
|
41
|
+
} | {
|
|
42
|
+
state: "over";
|
|
43
|
+
} | {
|
|
44
|
+
state: "forbidden";
|
|
45
|
+
reason: DropForbiddenReason;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* The drop zone's state for a drag (guide §9, Drag-over and Drop forbidden):
|
|
49
|
+
*
|
|
50
|
+
* - `none`: no item of kind `file` (a palette drag, selected text): the drop zone stays out of it.
|
|
51
|
+
* - `forbidden` / `dialog`: a modal dialog is open.
|
|
52
|
+
* - `forbidden` / `multiple`: more than one file and `multiple` is false.
|
|
53
|
+
* - `forbidden` / `type`: `accept` lists at least one MIME type and every file item carries a
|
|
54
|
+
* non-empty type that none of them matches (`type/*` wildcards included). Extensions cannot be
|
|
55
|
+
* checked before the drop, and an empty type (".storm" has none) never forbids.
|
|
56
|
+
* - `over` otherwise.
|
|
57
|
+
*/
|
|
58
|
+
declare function dragVerdict(items: readonly DragItemLike[], context: DragContext): DragVerdict;
|
|
59
|
+
/**
|
|
60
|
+
* A dropped file matches `accept` like `<input accept>`: an entry starting with "." matches the
|
|
61
|
+
* end of the name (case-insensitive), an entry with "/" the MIME type exactly or as `type/*`; an
|
|
62
|
+
* empty `accept` matches every file.
|
|
63
|
+
*/
|
|
64
|
+
declare function acceptsFile(file: {
|
|
65
|
+
name: string;
|
|
66
|
+
type: string;
|
|
67
|
+
}, accept: readonly string[]): boolean;
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Every text the shell shows or announces, in one table (guide §2, shared terms). English by
|
|
71
|
+
* default; a localized modeler overrides the whole table in its language, numbers arrive already
|
|
72
|
+
* formatted through `Intl` in that language.
|
|
73
|
+
*/
|
|
74
|
+
interface ShellMessages {
|
|
75
|
+
/** Menu trigger `aria-label` and tooltip, dropdown `aria-label`. */
|
|
76
|
+
menu: string;
|
|
77
|
+
/** Palette `aria-label`. */
|
|
78
|
+
tools: string;
|
|
79
|
+
/** `aria-label` of the focusable canvas `<svg>` (`role="application"`); a modeler names its notation. */
|
|
80
|
+
canvas: string;
|
|
81
|
+
/** Zoom panel `aria-label`. */
|
|
82
|
+
zoom: string;
|
|
83
|
+
zoomOut: string;
|
|
84
|
+
zoomIn: string;
|
|
85
|
+
/** Tooltip of the readout, e.g. `(percent) => "Zoom to " + percent` with percent "100%". */
|
|
86
|
+
zoomToActual(percent: string): string;
|
|
87
|
+
/** `aria-label` of the readout: "Zoom to 100% (now 86%)". */
|
|
88
|
+
zoomToActualNow(percent: string, now: string): string;
|
|
89
|
+
fit: string;
|
|
90
|
+
/** Legend title and the Model-group checkbox. */
|
|
91
|
+
legend: string;
|
|
92
|
+
closeLegend: string;
|
|
93
|
+
/** Pill text: "1 problem", "2 problems". */
|
|
94
|
+
problems(count: number, formattedCount: string): string;
|
|
95
|
+
/** Pill `aria-label`: "2 problems, 1 error", "2 problems, no errors". */
|
|
96
|
+
problemsLabel(count: number, errors: number, formattedCount: string, formattedErrors: string): string;
|
|
97
|
+
/** Pill tooltip in the IDE host. */
|
|
98
|
+
showProblems: string;
|
|
99
|
+
/** Empty-file hint (guide §13.5). */
|
|
100
|
+
emptyHint: string;
|
|
101
|
+
insertExample: string;
|
|
102
|
+
/** Status box for an invalid file; `diagram` names the notation ("Event Storming board"). */
|
|
103
|
+
invalidFile(diagram: string): string;
|
|
104
|
+
/** Key names in shortcut chips outside macOS (German: Strg, Umschalt, Plus, Minus). */
|
|
105
|
+
keyNames: {
|
|
106
|
+
ctrl: string;
|
|
107
|
+
shift: string;
|
|
108
|
+
plus: string;
|
|
109
|
+
minus: string;
|
|
110
|
+
};
|
|
111
|
+
/** Default label and `aria-label` of the primary action (web app, top right). */
|
|
112
|
+
share: string;
|
|
113
|
+
/** `aria-label` of the legal line's `<nav>`. "Impressum" and "Datenschutz" are never translated (guide §2). */
|
|
114
|
+
legal: string;
|
|
115
|
+
/** First entry of the legal line, linking the Miragon home page: "© 2026 Miragon GmbH". */
|
|
116
|
+
copyright(year: number): string;
|
|
117
|
+
/** The description of every legal link for screen readers: they open in a new tab. */
|
|
118
|
+
opensInNewTab: string;
|
|
119
|
+
/** File group (web app) and the empty state's primary button. */
|
|
120
|
+
newDiagram: string;
|
|
121
|
+
/** File group: "Open…" (U+2026, it opens the browser's file dialog). */
|
|
122
|
+
openFile: string;
|
|
123
|
+
/** File group and the empty state's ghost button. */
|
|
124
|
+
showExample: string;
|
|
125
|
+
/** File group: "Import…". */
|
|
126
|
+
importFile: string;
|
|
127
|
+
/** Heading and `aria-label` of the Appearance radio group (web app with dark only, guide §13.3). */
|
|
128
|
+
appearance: string;
|
|
129
|
+
appearanceSystem: string;
|
|
130
|
+
appearanceLight: string;
|
|
131
|
+
appearanceDark: string;
|
|
132
|
+
/** Title of the problems popover (web app). */
|
|
133
|
+
problemsTitle: string;
|
|
134
|
+
/** A row's severity for screen readers, the `aria-label` of its icon: "Error", "Warning". */
|
|
135
|
+
problemSeverity(severity: ProblemSeverity): string;
|
|
136
|
+
/** `aria-label` of a Close button (toasts). */
|
|
137
|
+
close: string;
|
|
138
|
+
/** Chip of the drop zone while a drag can be dropped (guide §9 Drag-over). */
|
|
139
|
+
dropFile: string;
|
|
140
|
+
/** Chip of the drop zone while the drag cannot be dropped (guide §9 Drop forbidden). */
|
|
141
|
+
dropForbidden(reason: DropForbiddenReason): string;
|
|
142
|
+
/** The empty state's drag-and-drop hint when the modeler has a drop zone. */
|
|
143
|
+
dropHint: string;
|
|
144
|
+
/** The text link after the empty state's hint that runs the File group's "Open…". */
|
|
145
|
+
openFileLink: string;
|
|
146
|
+
/**
|
|
147
|
+
* Danger toast for a dropped file outside `DropZoneOptions.accept`; `accepted` is the accept list
|
|
148
|
+
* joined by `Intl.ListFormat` (disjunction) in `lang`: ".storm, .json, or .txt".
|
|
149
|
+
*/
|
|
150
|
+
cannotOpenFile(name: string, accepted: string): string;
|
|
151
|
+
}
|
|
152
|
+
/** Every key optional; an `undefined` value keeps the default (a conditional override). */
|
|
153
|
+
type ShellMessageOverrides = {
|
|
154
|
+
[Key in Exclude<keyof ShellMessages, "keyNames">]?: ShellMessages[Key] | undefined;
|
|
155
|
+
} & {
|
|
156
|
+
keyNames?: {
|
|
157
|
+
[Key in keyof ShellMessages["keyNames"]]?: string | undefined;
|
|
158
|
+
} | undefined;
|
|
159
|
+
};
|
|
160
|
+
declare const defaultMessages: ShellMessages;
|
|
161
|
+
/** `defaultMessages` with `overrides` applied (`keyNames` merged key by key). */
|
|
162
|
+
declare function resolveMessages(overrides?: ShellMessageOverrides | undefined): ShellMessages;
|
|
163
|
+
|
|
164
|
+
/** The only keys the chrome handles (guide §5.8). */
|
|
165
|
+
type ChromeKey = "zoomIn" | "zoomOut" | "zoomToActual" | "fit";
|
|
166
|
+
/** The `KeyboardEvent` fields the keymap reads. */
|
|
167
|
+
interface KeyLike {
|
|
168
|
+
key: string;
|
|
169
|
+
code: string;
|
|
170
|
+
metaKey: boolean;
|
|
171
|
+
ctrlKey: boolean;
|
|
172
|
+
shiftKey: boolean;
|
|
173
|
+
altKey: boolean;
|
|
174
|
+
isComposing?: boolean | undefined;
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Which chrome key `event` is, ignoring where it was pressed:
|
|
178
|
+
* - `zoomIn`: Cmd/Ctrl + `+` or `=` (by `key`, with or without Shift), or `NumpadAdd` (by `code`);
|
|
179
|
+
* - `zoomOut`: Cmd/Ctrl + `-` or `_`, or `NumpadSubtract`;
|
|
180
|
+
* - `zoomToActual`: Cmd/Ctrl + `0`, or `Numpad0` (also with NumLock off);
|
|
181
|
+
* - `fit`: Shift + `Digit1` (by `code`, every layout) without Cmd, Ctrl or Alt.
|
|
182
|
+
* Cmd/Ctrl is `metaKey || ctrlKey` on every platform; nothing matches with `altKey` (AltGr).
|
|
183
|
+
*/
|
|
184
|
+
declare function matchChromeKey(event: KeyLike): ChromeKey | undefined;
|
|
185
|
+
/**
|
|
186
|
+
* Cmd/Ctrl + Z, Cmd/Ctrl + Shift + Z or Cmd/Ctrl + Y, without Alt (by `key`, `code` as fallback).
|
|
187
|
+
* Cmd/Ctrl + Shift + Y is not redo (VS Code toggles its Debug Console with it).
|
|
188
|
+
*/
|
|
189
|
+
declare function isUndoRedoKey(event: KeyLike): boolean;
|
|
190
|
+
/** A text field: every input, select list and editable text (label editor, inspector, dialogs). */
|
|
191
|
+
declare const TEXT_FIELD_SELECTOR = "input, textarea, select, [contenteditable]:not([contenteditable='false'])";
|
|
192
|
+
/** Where a key was pressed, as the shell's one `keydown` listener sees it. */
|
|
193
|
+
interface KeyContext {
|
|
194
|
+
host: ShellHost;
|
|
195
|
+
/** The event target matches `TEXT_FIELD_SELECTOR` or is `isContentEditable`. */
|
|
196
|
+
inTextField: boolean;
|
|
197
|
+
/** The shell's menu dropdown is open. */
|
|
198
|
+
menuOpen: boolean;
|
|
199
|
+
/** A popover or dialog (`[role="dialog"]`, modal or not) is open. */
|
|
200
|
+
dialogOpen: boolean;
|
|
201
|
+
/** A modal dialog (`[role="dialog"][aria-modal="true"]`) is open: chrome keys do not act. */
|
|
202
|
+
modalOpen: boolean;
|
|
203
|
+
/** `event.defaultPrevented` when the listener ran: the canvas (or the modeler) handled the key. */
|
|
204
|
+
defaultPrevented: boolean;
|
|
205
|
+
/**
|
|
206
|
+
* The web app's start screen shows (`setEmptyState(true)`): there is no diagram to zoom, so the
|
|
207
|
+
* zoom keys and Shift+1 are left to the browser, which zooms the page.
|
|
208
|
+
*/
|
|
209
|
+
startScreen?: boolean | undefined;
|
|
210
|
+
}
|
|
211
|
+
type KeyAction = ChromeKey | "closeMenu";
|
|
212
|
+
interface KeyDecision {
|
|
213
|
+
/** What the shell executes; `undefined` = nothing. */
|
|
214
|
+
action?: KeyAction;
|
|
215
|
+
preventDefault: boolean;
|
|
216
|
+
stopPropagation: boolean;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* The key rules of guide §5.8 and §13.5 in one place:
|
|
220
|
+
* 1. Escape with the menu open: `closeMenu`, prevented; in the IDE host stopped whenever the menu
|
|
221
|
+
* or a dialog/popover is open. During IME composition Escape belongs to the IME: no `closeMenu`.
|
|
222
|
+
* 2. Zoom keys: executed and prevented unless composing, in a text field or with a modal dialog;
|
|
223
|
+
* in the IDE host always stopped, also when not executed (VS Code must never zoom the window).
|
|
224
|
+
* Behind the web app's start screen untouched: the browser zooms the page (WCAG 1.4.4).
|
|
225
|
+
* 3. Shift+1: `fit` under the same conditions; otherwise untouched (it types "!").
|
|
226
|
+
* 4. IDE host, text field, undo/redo key: stopped only (the field keeps its native undo).
|
|
227
|
+
* 5. IDE host, `defaultPrevented`: stopped (the canvas handled it; VS Code must not run it again).
|
|
228
|
+
* Everything else passes untouched. `stopPropagation` is only ever true in the IDE host.
|
|
229
|
+
*/
|
|
230
|
+
declare function decideKey(event: KeyLike, context: KeyContext): KeyDecision;
|
|
231
|
+
/** The keyboard the user sits at; decides the shortcut labels (guide §5.8). */
|
|
232
|
+
type Platform = "mac" | "other";
|
|
233
|
+
/** The `navigator` fields `detectPlatform` reads. */
|
|
234
|
+
interface NavigatorLike {
|
|
235
|
+
platform?: string | undefined;
|
|
236
|
+
userAgentData?: {
|
|
237
|
+
platform?: string | undefined;
|
|
238
|
+
} | undefined;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* `mac` when `userAgentData.platform ?? platform` matches `/mac/i`, else `other`; `other` without a
|
|
242
|
+
* navigator. Read it in the webview or page itself, never from the extension host (VS Code Remote).
|
|
243
|
+
*/
|
|
244
|
+
declare function detectPlatform(navigator?: NavigatorLike | undefined): Platform;
|
|
245
|
+
/**
|
|
246
|
+
* The tooltip chip text: macOS `⌘+`, `⌘−` (U+2212), `⌘0`, `⇧1`; elsewhere `Ctrl+Plus`,
|
|
247
|
+
* `Ctrl+Minus`, `Ctrl+0`, `Shift+1` with the key names of the messages table.
|
|
248
|
+
*/
|
|
249
|
+
declare function shortcutLabel(key: ChromeKey, platform: Platform, keyNames: ShellMessages["keyNames"]): string;
|
|
250
|
+
/**
|
|
251
|
+
* The chip of a Cmd/Ctrl key a modeler binds itself (the web app's File entries, guide §5.4): macOS
|
|
252
|
+
* `⌘O`, elsewhere `Ctrl+O` with the key name of the messages table (`Strg+O`). `key` as printed on
|
|
253
|
+
* the key cap; pass the result to `FileMenuAction.shortcut` or `MenuActionEntry.shortcut`.
|
|
254
|
+
*/
|
|
255
|
+
declare function commandKeyLabel(key: string, platform: Platform, keyNames: Pick<ShellMessages["keyNames"], "ctrl">): string;
|
|
256
|
+
/**
|
|
257
|
+
* The `aria-keyshortcuts` value (WAI-ARIA names): zoom in `Meta+= Meta+Plus`, zoom out `Meta+-`,
|
|
258
|
+
* 100 % `Meta+0`, fit `Shift+1`; outside macOS `Control` instead of `Meta`.
|
|
259
|
+
*/
|
|
260
|
+
declare function ariaKeyShortcuts(key: ChromeKey, platform: Platform): string;
|
|
261
|
+
/**
|
|
262
|
+
* The `aria-keyshortcuts` value (WAI-ARIA names, guide §5.6) for a shortcut chip label: `"L"` →
|
|
263
|
+
* `"L"`, `"⇧L"` → `"Shift+L"`, `"⌘⇧Z"` → `"Meta+Shift+Z"`, `"Ctrl+Shift+E"` → `"Control+Shift+E"`,
|
|
264
|
+
* `"Del"` → `"Delete"`, `"Ctrl++"` → `"Control+Plus"`. `undefined` for a label that is not one key
|
|
265
|
+
* combination (`"G then D"`, `"1–9"`, empty).
|
|
266
|
+
*/
|
|
267
|
+
declare function ariaKeyShortcutsFromLabel(label: string): string | undefined;
|
|
268
|
+
|
|
269
|
+
/** The web app's mode choice (guide §13.3): Appearance, System · Light · Dark. */
|
|
270
|
+
type AppearanceChoice = "system" | "light" | "dark";
|
|
271
|
+
/** Where the web app keeps the choice: `light` or `dark`; "System" removes the entry (guide §13.3). */
|
|
272
|
+
declare const APPEARANCE_STORAGE_KEY = "miragon.darstellung";
|
|
273
|
+
/**
|
|
274
|
+
* The two `theme-color` values (guide §11, §13.3): light is the app's own value (`#335DE5`, `fest`
|
|
275
|
+
* `--cd-blau` of the vendored `cd-tokens.generated.json`), dark `#1D1D1D` (`dunkel`
|
|
276
|
+
* `--cd-ui-grund`). The modeler reads both from its vendored JSON; the shell keeps no colour.
|
|
277
|
+
*/
|
|
278
|
+
interface ThemeColors {
|
|
279
|
+
light: string;
|
|
280
|
+
dark: string;
|
|
281
|
+
}
|
|
282
|
+
/** `"light"` and `"dark"` pass, everything else (a missing entry, a foreign value) is `"system"`. */
|
|
283
|
+
declare function parseAppearanceChoice(stored: unknown): AppearanceChoice;
|
|
284
|
+
/** What `<html>` and the meta tags carry for a choice. */
|
|
285
|
+
interface DocumentMode {
|
|
286
|
+
/** `data-cd-theme` on `<html>`. */
|
|
287
|
+
theme: "system" | Theme;
|
|
288
|
+
/** `<meta name="color-scheme" content>`. */
|
|
289
|
+
colorScheme: "light dark" | Theme;
|
|
290
|
+
/** The `content` of the `theme-color` metas with `media="(prefers-color-scheme: light|dark)"`. */
|
|
291
|
+
themeColor: ThemeColors;
|
|
292
|
+
}
|
|
293
|
+
/**
|
|
294
|
+
* The document mode for `choice` (guide §13.3). A modeler that cannot show dark (`themes` without
|
|
295
|
+
* `"dark"`) is `light` whatever the choice: theme `light`, color-scheme `light`, both theme
|
|
296
|
+
* colours `colors.light`. Dark-capable: `system` → theme `system`, color-scheme `light dark`,
|
|
297
|
+
* `colors` as they are; `light`/`dark` → that theme and color-scheme, both theme colours the
|
|
298
|
+
* chosen mode's, so the browser bar matches a fixed choice under the other system mode.
|
|
299
|
+
*/
|
|
300
|
+
declare function documentMode(choice: AppearanceChoice, themes: readonly Theme[], colors: ThemeColors): DocumentMode;
|
|
301
|
+
interface WebModeScriptOptions {
|
|
302
|
+
/** The modes the modeler can show, as passed to `mountShell`. */
|
|
303
|
+
themes: readonly Theme[];
|
|
304
|
+
themeColors: ThemeColors;
|
|
305
|
+
}
|
|
306
|
+
/**
|
|
307
|
+
* The body of the inline `<script>` in `<head>`, before the first stylesheet (guide §13.3). With
|
|
308
|
+
* `themes` including `"dark"`: the guide's script, which reads `APPEARANCE_STORAGE_KEY` inside
|
|
309
|
+
* `try`/`catch`, writes `data-cd-theme`, `color-scheme` and both `theme-color` metas for a fixed
|
|
310
|
+
* choice and sets `data-cd-kontrast="hoch"` under `(prefers-contrast: more)`. Light-only: the
|
|
311
|
+
* contrast line alone. Plain ES5 without the modeler's bundle, so it runs before the first paint.
|
|
312
|
+
*/
|
|
313
|
+
declare function webModeScript(options: WebModeScriptOptions): string;
|
|
314
|
+
/**
|
|
315
|
+
* The `<head>` markup the web app's `index.html` carries for its mode, as one string (for a Vite
|
|
316
|
+
* `transformIndexHtml` hook or to paste): dark-capable `<meta name="color-scheme" content="light
|
|
317
|
+
* dark">`, two `theme-color` metas with `media` and the script; light-only one `theme-color`
|
|
318
|
+
* without `media` and the contrast script. `<html>` itself carries `lang` and
|
|
319
|
+
* `data-cd-theme="<documentMode("system", themes, colors).theme>"` statically.
|
|
320
|
+
*/
|
|
321
|
+
declare function webHeadMarkup(options: WebModeScriptOptions): string;
|
|
322
|
+
|
|
323
|
+
export { APPEARANCE_STORAGE_KEY as A, shortcutLabel as B, type ChromeKey as C, type DocumentMode as D, webHeadMarkup as E, webModeScript as F, type KeyAction as K, type NavigatorLike as N, type Platform as P, type ShellMessageOverrides as S, TEXT_FIELD_SELECTOR as T, type WebModeScriptOptions as W, type AppearanceChoice as a, type DragContext as b, type DragItemLike as c, type DragVerdict as d, type DropForbiddenReason as e, type KeyContext as f, type KeyDecision as g, type KeyLike as h, type ProblemItem as i, type ProblemSeverity as j, type ShellMessages as k, type ThemeColors as l, acceptsFile as m, ariaKeyShortcuts as n, ariaKeyShortcutsFromLabel as o, commandKeyLabel as p, decideKey as q, defaultMessages as r, detectPlatform as s, documentMode as t, dragVerdict as u, isUndoRedoKey as v, matchChromeKey as w, parseAppearanceChoice as x, problemSummary as y, resolveMessages as z };
|