@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
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Miragon
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,436 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @miragon/modeler-shell
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
The shared, framework-free chrome around Miragon diagram-js modelers: the menu button with its
|
|
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, and the plumbing to
|
|
6
|
+
a VS Code webview. One implementation for every modeler and every host (VS Code webview, web app,
|
|
7
|
+
embedded in a foreign page), so the chrome looks and behaves the same everywhere.
|
|
8
|
+
|
|
9
|
+
- **Look and behaviour** follow the Miragon design system, guide
|
|
10
|
+
[`modeler-design-system.md`][guide] §5, §6, §8, §9, §11, §12 and §13.3 to §13.5 (skill
|
|
11
|
+
`miragon-brand:modeler-tool-design`; the repository is private, the link opens for Miragon staff).
|
|
12
|
+
- **Mechanics** (diagram-js services, VS Code quirks, why things are done the way they are) are in
|
|
13
|
+
the [implementation notes][notes].
|
|
14
|
+
- **API**: every entry point, type and rule in [API.md][api].
|
|
15
|
+
- **Plan**: Phase 4 of [Miragon/modeler-api#4](https://github.com/Miragon/modeler-api/issues/4); the
|
|
16
|
+
Event Storming modeler is the pilot.
|
|
17
|
+
|
|
18
|
+
Zero runtime dependencies: diagram-js services are typed structurally, the `vscode` namespace and
|
|
19
|
+
Playwright's `page` and `test` are passed in, nothing is imported at runtime. The stylesheet reads
|
|
20
|
+
only the design system's `--cd-ui-*` roles and `--cd-shell-*` sizes. All texts are English by default
|
|
21
|
+
and overridable through one messages table; numbers go through `Intl` in the UI language.
|
|
22
|
+
|
|
23
|
+
## Install
|
|
24
|
+
|
|
25
|
+
```sh
|
|
26
|
+
npm i --save-exact @miragon/modeler-shell
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
The modeler brings what the shell styles against and draws into:
|
|
30
|
+
|
|
31
|
+
- **diagram-js** 15.28 or later (directly or through its renderer);
|
|
32
|
+
- **the design tokens**: `cd-tokens.generated.css` (and `cd-tokens.generated.json` where scripts
|
|
33
|
+
need values) of the design system, 1.12.0 or later (the release with the `shell` group). Copy
|
|
34
|
+
both unchanged from the skill's `assets/` or from
|
|
35
|
+
<https://corporate-identity.netlify.app/brand/cd-tokens.generated.css> and
|
|
36
|
+
<https://corporate-identity.netlify.app/brand/cd-tokens.generated.json>, mark them as generated,
|
|
37
|
+
and re-copy instead of editing. The shell ships no tokens and never forks a value;
|
|
38
|
+
- **the fonts** Geist and Geist Mono, bundled locally, for example `@fontsource-variable/geist` and
|
|
39
|
+
`@fontsource-variable/geist-mono` (no CDN);
|
|
40
|
+
- for `./vscode/extension`: **VS Code 1.90 or later**, whose extension host runs Node 20 (the global
|
|
41
|
+
`crypto` that `createNonce` uses; the types are tested against `@types/vscode` 1.96). Declare
|
|
42
|
+
`engines.vscode` accordingly and target `node20` in the extension-host bundle.
|
|
43
|
+
|
|
44
|
+
Stylesheets, in this order. The tokens come first, unless the renderer's stylesheet already imports
|
|
45
|
+
them (Event Storming's does), and are never loaded twice:
|
|
46
|
+
|
|
47
|
+
```ts
|
|
48
|
+
import "./theme/cd-tokens.generated.css"; // vendored, unchanged, first; left out when the renderer CSS brings it
|
|
49
|
+
import "@fontsource-variable/geist/wght.css";
|
|
50
|
+
import "@fontsource-variable/geist-mono/wght.css";
|
|
51
|
+
import "diagram-js/assets/diagram-js.css"; // usually through the renderer's CSS
|
|
52
|
+
import "@miragon/<notation>-renderer/…css";
|
|
53
|
+
import "@miragon/modeler-shell/shell.css"; // last
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The shell computes fit insets, breakpoints and layers from its own copy of the sizes (`SHELL`), while
|
|
57
|
+
its stylesheet reads the vendored `--cd-shell-*` tokens. Pin the two together in a unit test (Vitest
|
|
58
|
+
or `node:test`), not in the e2e suite, since the check is pure data:
|
|
59
|
+
|
|
60
|
+
```ts
|
|
61
|
+
// test/tokens.sync.test.ts
|
|
62
|
+
import { SHELL, SHELL_TOKENS, shellSizesFromTokens } from "@miragon/modeler-shell/core";
|
|
63
|
+
|
|
64
|
+
expect(SHELL).toEqual(shellSizesFromTokens(vendoredTokens.shell));
|
|
65
|
+
expect(Object.keys(vendoredTokens.shell)).toEqual(expect.arrayContaining(Object.values(SHELL_TOKENS)));
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
| Entry | For |
|
|
69
|
+
| -------------------------------------------- | ----------------------------------------------------------- |
|
|
70
|
+
| `@miragon/modeler-shell` | `mountShell`, icons, and everything of `./core` |
|
|
71
|
+
| `@miragon/modeler-shell/core` | DOM-free: fit math, keymap, zoom readout, appearance, sizes |
|
|
72
|
+
| `@miragon/modeler-shell/diagram-js` | `shellModule`, the viewport, `connectDiagramJs` |
|
|
73
|
+
| `@miragon/modeler-shell/vscode/extension` | webview HTML with CSP and nonce, theme bridge |
|
|
74
|
+
| `@miragon/modeler-shell/vscode/webview` | `mountVsCodeShell`, appearance handler, webview state |
|
|
75
|
+
| `@miragon/modeler-shell/testing` | the Playwright-driven shell conformance kit |
|
|
76
|
+
| `@miragon/modeler-shell/testing/vscode-stub` | a stand-in for VS Code around a webview test page |
|
|
77
|
+
| `@miragon/modeler-shell/shell.css` | every style of the shell |
|
|
78
|
+
|
|
79
|
+
TypeScript: every entry resolves under `moduleResolution` `node16`, `bundler` and `node10`, and
|
|
80
|
+
every optional input field accepts `undefined`, so a modeler compiling with
|
|
81
|
+
`exactOptionalPropertyTypes` forwards optional values as they are.
|
|
82
|
+
|
|
83
|
+
## Usage in a VS Code webview
|
|
84
|
+
|
|
85
|
+
Extension host:
|
|
86
|
+
|
|
87
|
+
```ts
|
|
88
|
+
import * as vscode from "vscode";
|
|
89
|
+
import {
|
|
90
|
+
connectThemeBridge,
|
|
91
|
+
currentAppearance,
|
|
92
|
+
renderWebviewHtml,
|
|
93
|
+
} from "@miragon/modeler-shell/vscode/extension";
|
|
94
|
+
|
|
95
|
+
const THEMES = ["light"] as const; // until the shared dark-mode release of the modelers
|
|
96
|
+
|
|
97
|
+
// activate(): a hidden tab keeps document, view and selection
|
|
98
|
+
vscode.window.registerCustomEditorProvider(viewType, provider, {
|
|
99
|
+
webviewOptions: { retainContextWhenHidden: true },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
// resolveCustomTextEditor():
|
|
103
|
+
const dist = vscode.Uri.joinPath(extensionUri, "dist");
|
|
104
|
+
webview.options = { enableScripts: true, localResourceRoots: [dist] }; // script, CSS and font files
|
|
105
|
+
const asset = (file: string) => webview.asWebviewUri(vscode.Uri.joinPath(dist, file)).toString();
|
|
106
|
+
webview.html = renderWebviewHtml({
|
|
107
|
+
cspSource: webview.cspSource,
|
|
108
|
+
scriptUri: asset("webview.js"),
|
|
109
|
+
styleUris: [asset("webview.css")],
|
|
110
|
+
title: "Event Storming Board",
|
|
111
|
+
lang: "en",
|
|
112
|
+
appearance: currentAppearance(vscode, { themes: THEMES }), // on <html> before the first paint
|
|
113
|
+
});
|
|
114
|
+
connectThemeBridge(vscode, webviewPanel, { themes: THEMES }); // disposes itself with the panel
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
The bridge's disposable is only for ending it before the panel closes; never push it into
|
|
118
|
+
`context.subscriptions`, which would keep one dead bridge per opened editor until the extension
|
|
119
|
+
deactivates.
|
|
120
|
+
|
|
121
|
+
Bundle the fonts as files next to the webview bundle, not as data URLs: with esbuild
|
|
122
|
+
`loader: { ".woff2": "file", ".woff": "file" }` and `assetNames: "fonts/[name]-[hash]"` the browser
|
|
123
|
+
fetches only the subsets a page's `unicode-range` needs (Geist Mono alone brings six; inlined they
|
|
124
|
+
roughly double the webview CSS). The CSP of `renderWebviewHtml` allows fonts from `cspSource`, and
|
|
125
|
+
`dist` is in `localResourceRoots`. A webview that bundles zod 4 runs `z.config({ jitless: true })` in
|
|
126
|
+
a module it imports before any schema module: the CSP forbids eval, zod probes once for eval when it
|
|
127
|
+
builds its first object schema, and the browser reports that caught `new Function` as a
|
|
128
|
+
`securitypolicyviolation`; under `jitless` zod skips the probe.
|
|
129
|
+
|
|
130
|
+
Webview:
|
|
131
|
+
|
|
132
|
+
```ts
|
|
133
|
+
import { INVALID_STATUS_DELAY_MS } from "@miragon/modeler-shell";
|
|
134
|
+
import { connectDiagramJs, shellModule } from "@miragon/modeler-shell/diagram-js";
|
|
135
|
+
import {
|
|
136
|
+
mountVsCodeShell,
|
|
137
|
+
persistView,
|
|
138
|
+
readShellState,
|
|
139
|
+
writeShellState,
|
|
140
|
+
} from "@miragon/modeler-shell/vscode/webview";
|
|
141
|
+
|
|
142
|
+
const vscode = acquireVsCodeApi();
|
|
143
|
+
const stored = readShellState(vscode);
|
|
144
|
+
|
|
145
|
+
// The diagram first: a modeler keyboard on document must run before the shell's key listener.
|
|
146
|
+
// A renderer that builds its diagram lazily builds it now; width and height follow the shell's slot.
|
|
147
|
+
// A renderer with its own Cmd/Ctrl+Z binding turns it off here, because VS Code's document history
|
|
148
|
+
// owns undo (Event Storming: createCollaborativeModeler({ undoRedoKeys: false }), see API.md).
|
|
149
|
+
const modeler = new Modeler({ width: "100%", height: "100%", additionalModules: [shellModule] });
|
|
150
|
+
modeler.get("canvas");
|
|
151
|
+
const shell = mountVsCodeShell(document.getElementById("app")!, {
|
|
152
|
+
themes: ["light"],
|
|
153
|
+
canvas: modeler.container,
|
|
154
|
+
menu: {
|
|
155
|
+
model: [/* Level; Type captions with keepEnabledWhenReadOnly, a view toggle */],
|
|
156
|
+
export: [/* Export as PNG, SVG */],
|
|
157
|
+
help: [/* Keyboard shortcuts… (the modeler's dialog in 0.1) */],
|
|
158
|
+
},
|
|
159
|
+
legend: {
|
|
160
|
+
render: renderLegend,
|
|
161
|
+
open: stored.legendOpen,
|
|
162
|
+
onToggle: (open) => writeShellState(vscode, { legendOpen: open }),
|
|
163
|
+
},
|
|
164
|
+
problems: { onActivate: () => vscode.postMessage({ type: "showProblems" }) },
|
|
165
|
+
emptyHint: { onInsertExample: () => vscode.postMessage({ type: "insertExample" }) },
|
|
166
|
+
});
|
|
167
|
+
const connection = connectDiagramJs(shell, modeler, {
|
|
168
|
+
// the tight bounds of the content, no padding: the shell's fit insets are the only margin
|
|
169
|
+
contentBounds: () => elementBounds(), // undefined while there is nothing to fit
|
|
170
|
+
isEmpty: () => elementCount() === 0, // cheap: no export, no validation
|
|
171
|
+
});
|
|
172
|
+
const { viewport } = connection;
|
|
173
|
+
persistView(vscode, viewport); // writes the view once it rested, not on every frame
|
|
174
|
+
|
|
175
|
+
async function importText(text: string, first: boolean): Promise<void> {
|
|
176
|
+
const keep = first ? undefined : viewport.getViewbox();
|
|
177
|
+
try {
|
|
178
|
+
await modeler.importDSL(text);
|
|
179
|
+
lockCanvas(false);
|
|
180
|
+
shell.setReadOnly(false);
|
|
181
|
+
shell.setStatus(null);
|
|
182
|
+
if (keep) viewport.restore(keep);
|
|
183
|
+
else if (first && stored.view) viewport.setState(stored.view);
|
|
184
|
+
viewport.afterImport(); // first content fits, an empty file shows 100 %, the hint follows by itself
|
|
185
|
+
} catch {
|
|
186
|
+
// locked at once, visible after 800ms without a valid import (no flicker while typing)
|
|
187
|
+
lockCanvas(true); // the modeler's own lock: context pad, move, resize, delete, paste, label editor
|
|
188
|
+
shell.setReadOnly(true, { delayMs: INVALID_STATUS_DELAY_MS });
|
|
189
|
+
shell.setStatus(
|
|
190
|
+
{ severity: "danger", text: shell.messages.invalidFile("Event Storming board") },
|
|
191
|
+
{ delayMs: INVALID_STATUS_DELAY_MS },
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// Imports strictly one after another; `ready` resolves once the first one settled.
|
|
197
|
+
let imports = Promise.resolve();
|
|
198
|
+
let firstImportSettled = () => {};
|
|
199
|
+
const ready = new Promise<void>((resolve) => (firstImportSettled = resolve));
|
|
200
|
+
window.addEventListener("message", ({ data }) => {
|
|
201
|
+
if (data?.type === "init")
|
|
202
|
+
imports = imports.then(() => importText(data.text, true)).then(firstImportSettled);
|
|
203
|
+
if (data?.type === "update") imports = imports.then(() => importText(data.text, false));
|
|
204
|
+
});
|
|
205
|
+
vscode.postMessage({ type: "ready" });
|
|
206
|
+
|
|
207
|
+
// For the e2e test page below; the bundle publishes nothing on window.
|
|
208
|
+
export const webview = { shell, modeler, connection, ready };
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
`mountVsCodeShell` stops every key the webview handles on `document`, before VS Code's listener on
|
|
212
|
+
`window` forwards it to the workbench, and keeps the webview in the mode the extension resolved: a
|
|
213
|
+
modeler declaring `["light"]` stays light under any VS Code theme, high contrast passes, and VS
|
|
214
|
+
Code's `workbench.reduceMotion` stops the chrome's animations. Undo and redo on the canvas go to VS
|
|
215
|
+
Code (the shell keeps diagram-js's bindings off them), so a modeler's own keyboard binds no Cmd/Ctrl+Z
|
|
216
|
+
in the webview either. Keys the canvas uses act only there: Cmd/Ctrl+A on the canvas selects every
|
|
217
|
+
element (or is at least prevented), so VS Code's Select All never selects the chrome. The modeler's
|
|
218
|
+
keyboard also ignores keys with `defaultPrevented` and keys from outside the canvas container, and
|
|
219
|
+
puts no `tabindex` on it.
|
|
220
|
+
|
|
221
|
+
`shell.setReadOnly(true)` locks the palette (all but Lasso and Hand) and the Model entries of the
|
|
222
|
+
menu. View toggles stay usable: the shell's Legend checkbox and the legend's close button, and every
|
|
223
|
+
entry with `keepEnabledWhenReadOnly` (guide §13.5 since design system 1.13.0). The canvas itself is the modeler's to lock: no context pad, move,
|
|
224
|
+
resize, delete, paste or label editor. Content the modeler adds to `shell.root` (a "Keyboard
|
|
225
|
+
shortcuts…" dialog) is the modeler's too: it removes it in its own teardown, for example on
|
|
226
|
+
`diagram.destroy`, since `shell.destroy()` removes only what the shell added. The complete Event
|
|
227
|
+
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 0.1.
|
|
229
|
+
|
|
230
|
+
## Usage in a web app
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
import { mountShell } from "@miragon/modeler-shell";
|
|
234
|
+
import { connectDiagramJs, shellModule } from "@miragon/modeler-shell/diagram-js";
|
|
235
|
+
|
|
236
|
+
// index.html carries <html lang="en" data-cd-theme="light"> statically while the modeler is light-only
|
|
237
|
+
const modeler = new Modeler({ width: "100%", height: "100%", additionalModules: [shellModule] });
|
|
238
|
+
const shell = mountShell(document.getElementById("app")!, {
|
|
239
|
+
host: "web",
|
|
240
|
+
canvas: modeler.container,
|
|
241
|
+
menu: { export: [/* … */], help: [/* Keyboard shortcuts… */] },
|
|
242
|
+
legend: { render: renderLegend, open: storedLegendOpen(), onToggle: storeLegendOpen },
|
|
243
|
+
});
|
|
244
|
+
const { viewport } = connectDiagramJs(shell, modeler, { contentBounds: () => elementBounds() });
|
|
245
|
+
await modeler.importXML(xml);
|
|
246
|
+
viewport.afterImport();
|
|
247
|
+
```
|
|
248
|
+
|
|
249
|
+
The `web` host prevents the browser's page zoom for the chrome keys (outside text fields and
|
|
250
|
+
dialogs), lifts pill and legend above the legal line and fits with the web insets. The rest of the
|
|
251
|
+
web-app chrome (primary action, legal line, File and Appearance groups, toasts, empty state) is not
|
|
252
|
+
part of 0.1.
|
|
253
|
+
|
|
254
|
+
## Usage in a foreign page
|
|
255
|
+
|
|
256
|
+
`mountShell(container, { host: "embedded", themes: ["light"], canvas })` on an explicitly sized
|
|
257
|
+
container: the root carries the tool's `lang` itself, keys act only while the focus is inside the
|
|
258
|
+
modeler, and the page's `<html>` stays untouched. Until the `@miragon/modeler-api` contract gains
|
|
259
|
+
its `appearance` surface (a later 1.x minor), an embedded modeler has no appearance channel, so the
|
|
260
|
+
root carries its own `data-cd-theme` (the first of `themes`, light) and stays light inside a dark
|
|
261
|
+
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
|
+
(`button { text-transform: uppercase }`) do not reach the chrome.
|
|
264
|
+
|
|
265
|
+
## The conformance kit
|
|
266
|
+
|
|
267
|
+
Every modeler runs the kit in its own Playwright suite against its real webview or page. It checks
|
|
268
|
+
the geometry of every island (±1px), ARIA and the keyboard models, that every chrome key acts exactly
|
|
269
|
+
once and never reaches VS Code, that the canvas's own keys (Cmd/Ctrl+A) stay on the canvas, that
|
|
270
|
+
nothing is ever half-dark (the painted canvas surround and the palette's colours included), that
|
|
271
|
+
Enter on a creating palette entry adds one selected element, that an invalid file locks the canvas,
|
|
272
|
+
and that `destroy()` leaves nothing behind.
|
|
273
|
+
|
|
274
|
+
For the `ide` host the suite serves a test page that puts a stand-in for VS Code around the
|
|
275
|
+
modeler's webview code: VS Code's default webview styles, `<html>` as `renderWebviewHtml` writes it,
|
|
276
|
+
the host page's theme classes, `acquireVsCodeApi()` and the real theme bridge. Serve
|
|
277
|
+
`e2e/webview/` as the root of a Vite server, so the page is `/webview-test.html`:
|
|
278
|
+
|
|
279
|
+
```ts
|
|
280
|
+
// e2e/webview/webview-test.ts, the entry of e2e/webview/webview-test.html
|
|
281
|
+
import { installVsCodeWebviewStub, stubOptionsFromSearch } from "@miragon/modeler-shell/testing/vscode-stub";
|
|
282
|
+
|
|
283
|
+
// before the webview runs, which calls acquireVsCodeApi() and mounts the shell
|
|
284
|
+
installVsCodeWebviewStub({ ...stubOptionsFromSearch(location.search), themes: ["light"] });
|
|
285
|
+
const { webview } = await import("../../src/webview/main"); // exports { shell, modeler, connection, ready }
|
|
286
|
+
|
|
287
|
+
// for the harness only: the shipped bundle publishes nothing on window
|
|
288
|
+
window.shell = webview.shell;
|
|
289
|
+
window.modeler = webview.modeler;
|
|
290
|
+
window.connection = webview.connection;
|
|
291
|
+
void webview.ready.then(() => (window.webviewReady = true)); // the first import settled
|
|
292
|
+
|
|
293
|
+
// there is no extension host: post the document the way the extension would
|
|
294
|
+
window.postMessage({ type: "init", text: EXAMPLE_BOARD }, "*");
|
|
295
|
+
```
|
|
296
|
+
|
|
297
|
+
`ready`, `webviewReady` and the window globals belong to the modeler's test page (declared in a
|
|
298
|
+
`.d.ts` of the suite), not to the shell: the webview resolves `ready` once its first `init` import
|
|
299
|
+
settled.
|
|
300
|
+
|
|
301
|
+
```ts
|
|
302
|
+
// e2e/shell-conformance.spec.ts
|
|
303
|
+
import { test } from "@playwright/test";
|
|
304
|
+
import { registerShellConformance, vsCodeStubSearch } from "@miragon/modeler-shell/testing";
|
|
305
|
+
|
|
306
|
+
test.describe("shell conformance", () => {
|
|
307
|
+
test.describe.configure({ timeout: 60_000 }); // geometry measures three sizes
|
|
308
|
+
registerShellConformance(
|
|
309
|
+
{
|
|
310
|
+
host: "ide",
|
|
311
|
+
themes: ["light"],
|
|
312
|
+
absent: ["inspector"], // islands this modeler does not have: never visible, never skipped
|
|
313
|
+
async open(page, { size, requested }) {
|
|
314
|
+
await page.setViewportSize(size);
|
|
315
|
+
await page.goto(`/webview-test.html?${vsCodeStubSearch(requested)}`);
|
|
316
|
+
await page.waitForFunction(() => window.webviewReady === true); // set by the test page
|
|
317
|
+
},
|
|
318
|
+
async destroy(page) {
|
|
319
|
+
// the webview's own teardown path: connection, diagram (with the modeler's dialogs), shell
|
|
320
|
+
await page.evaluate(() => {
|
|
321
|
+
window.connection.destroy();
|
|
322
|
+
window.modeler.destroy();
|
|
323
|
+
window.shell.destroy();
|
|
324
|
+
});
|
|
325
|
+
},
|
|
326
|
+
show: {
|
|
327
|
+
async legend(page) {
|
|
328
|
+
await page.evaluate(() => window.shell.legend?.setOpen(true));
|
|
329
|
+
},
|
|
330
|
+
async problems(page) {
|
|
331
|
+
await page.evaluate(() => window.postMessage({ type: "problems", count: 2, errors: 1 }, "*"));
|
|
332
|
+
},
|
|
333
|
+
async status(page) {
|
|
334
|
+
await page.evaluate(() =>
|
|
335
|
+
window.shell.setStatus({ severity: "danger", text: window.shell.messages.invalidFile("board") }),
|
|
336
|
+
);
|
|
337
|
+
},
|
|
338
|
+
async invalid(page) {
|
|
339
|
+
// the way the host makes the file invalid: a document update the parser rejects
|
|
340
|
+
await page.evaluate((text) => window.postMessage({ type: "update", text }, "*"), INVALID_BOARD);
|
|
341
|
+
},
|
|
342
|
+
},
|
|
343
|
+
},
|
|
344
|
+
test,
|
|
345
|
+
{
|
|
346
|
+
onSkip(caseName, skips) {
|
|
347
|
+
// with `absent` listing what the modeler lacks, every remaining skip is a gap of this suite
|
|
348
|
+
throw new Error(`${caseName}: ${skips.map(({ hook, reason }) => `${hook}: ${reason}`).join("; ")}`);
|
|
349
|
+
},
|
|
350
|
+
},
|
|
351
|
+
);
|
|
352
|
+
});
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
The harness opens the modeler with the appearance the kit requests, the way its host would; for
|
|
356
|
+
the `embedded` host the kit puts the foreign page itself in that mode. A skip names the harness
|
|
357
|
+
field it misses (`{ hook: "legend", reason }`; `hook: "destroy"` without a `destroy`; `hook: "open"`
|
|
358
|
+
when the opened document lacks what a case needs, such as a shape for `readOnly`); without `onSkip`
|
|
359
|
+
the kit warns on the console. What each case asserts, and what the kit installs in the page, is
|
|
360
|
+
listed in [API.md][api].
|
|
361
|
+
|
|
362
|
+
`registerShellConformance` declares every test inside the kit, so a runner reports them at the kit's
|
|
363
|
+
source (the package ships `src/` without its tests, so the location resolves). For test locations in
|
|
364
|
+
the suite's own spec file, register the cases there:
|
|
365
|
+
|
|
366
|
+
```ts
|
|
367
|
+
import { shellConformanceCases } from "@miragon/modeler-shell/testing";
|
|
368
|
+
|
|
369
|
+
for (const conformanceCase of shellConformanceCases()) {
|
|
370
|
+
test(`shell conformance: ${conformanceCase.name}`, async ({ page }) => {
|
|
371
|
+
const outcome = await conformanceCase.run(page, harness);
|
|
372
|
+
if (outcome?.skipped) failOnSkips(conformanceCase.name, outcome.skipped);
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
The test page runs the webview from source without a CSP. Add one smoke test of the production bundle
|
|
378
|
+
behind the HTML and CSP of `renderWebviewHtml` that fails on any `securitypolicyviolation` (the recipe
|
|
379
|
+
is in [API.md][api], `./vscode/extension`).
|
|
380
|
+
|
|
381
|
+
## Development
|
|
382
|
+
|
|
383
|
+
Run from the repository root (`-w packages/shell` for this package only):
|
|
384
|
+
|
|
385
|
+
```sh
|
|
386
|
+
npm run build -w packages/shell # tsup: ESM + CJS + d.ts, dist/shell.css
|
|
387
|
+
npm run typecheck -w packages/shell # src, core without the DOM library, demo
|
|
388
|
+
npm test -w packages/shell # Vitest: node (core, boundaries) and happy-dom projects
|
|
389
|
+
npx playwright install chromium # once
|
|
390
|
+
npm run test:e2e -w packages/shell # Playwright against demo/
|
|
391
|
+
npm run demo -w packages/shell # the demo page on http://localhost:5181
|
|
392
|
+
```
|
|
393
|
+
|
|
394
|
+
The demo page (`demo/`) is a small diagram-js modeler around the shell. Its query parameters choose
|
|
395
|
+
the host (`host=ide|web|embedded`), the modes the modeler declares (`themes=light,dark`), what the
|
|
396
|
+
host requests (`theme`, `kontrast=hoch`; embedded: the foreign page's own mode), the `ide` host's
|
|
397
|
+
`colorTheme` setting and `reduceMotion`, and the state to start in (`legend=open`, `inspector`,
|
|
398
|
+
`problems=2:1`, `status=danger`, `invalid`, `empty`, which opens at 100 %). In the `ide` host it runs
|
|
399
|
+
inside the stub of `./testing/vscode-stub`, and keeps view and legend state through
|
|
400
|
+
`acquireVsCodeApi()`.
|
|
401
|
+
|
|
402
|
+
The e2e suite runs the conformance kit for the `ide`, `web` and `embedded` hosts with a light-only
|
|
403
|
+
and a dark-capable modeler, switches VS Code themes and the `colorTheme` setting at runtime, checks
|
|
404
|
+
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, and proves with saboteurs that every case of
|
|
406
|
+
the kit fails when its rule is broken.
|
|
407
|
+
|
|
408
|
+
| Directory | Module |
|
|
409
|
+
| --------------------------- | ------------------------------------------------------------------ |
|
|
410
|
+
| `src/core` | DOM-free: fit math, keymap, zoom readout, appearance, sizes, texts |
|
|
411
|
+
| `src/dom` | `mountShell`: menu, zoom panel, tooltip, pill, status, slots, keys |
|
|
412
|
+
| `src/diagram-js` | `shellModule`, viewport, `connectDiagramJs`, palette skin |
|
|
413
|
+
| `src/vscode` | extension side (HTML, theme bridge) and webview side |
|
|
414
|
+
| `src/testing` | the Playwright-driven conformance kit and the VS Code stub |
|
|
415
|
+
| `src/*.ts`, `src/shell.css` | the entry points |
|
|
416
|
+
| `test/` | package boundaries, token drift, the unit tests' helpers |
|
|
417
|
+
| `demo/`, `e2e/` | the demo modeler and the e2e suite |
|
|
418
|
+
| `fixtures/tokens` | the token files, vendored unchanged for the tests and the demo |
|
|
419
|
+
|
|
420
|
+
A module imports another one only through its `index.ts`; `src/core` imports nothing outside
|
|
421
|
+
itself and compiles without the DOM library. `test/package-boundaries.test.ts` enforces this, the
|
|
422
|
+
zero runtime dependencies, the exact devDependency pins, the token-only stylesheets and the published
|
|
423
|
+
files (`dist`, `src` without its tests, `API.md` and the notices).
|
|
424
|
+
|
|
425
|
+
## License
|
|
426
|
+
|
|
427
|
+
MIT. The inlined Lucide icons are ISC (© Lucide Icons and Contributors), and `check`, `circle-x`,
|
|
428
|
+
`maximize`, `minus`, `plus`, `triangle-alert` and `x` derive from Feather, MIT (© Cole Bemis), as
|
|
429
|
+
Lucide's LICENSE lists them; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). `dist` keeps that
|
|
430
|
+
notice as a legal comment in the chunk with the icons, but a bundler's `legalComments` setting may
|
|
431
|
+
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.
|
|
433
|
+
|
|
434
|
+
[guide]: https://github.com/Miragon/corporate-identity/blob/main/plugins/miragon-brand/skills/modeler-tool-design/assets/modeler-design-system.md
|
|
435
|
+
[notes]: https://github.com/Miragon/modeler-api/blob/main/docs/shell/implementation-notes.md
|
|
436
|
+
[api]: https://github.com/Miragon/modeler-api/blob/main/packages/shell/API.md
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
# Third-party notices
|
|
2
|
+
|
|
3
|
+
`@miragon/modeler-shell` inlines icon geometry from [Lucide](https://lucide.dev)
|
|
4
|
+
(`lucide-static` 1.59.0) in `src/dom/icons.ts` and its builds. That file opens with a legal comment
|
|
5
|
+
(`/*! … @license … */`) naming both notices below, which esbuild and most minifiers keep in the
|
|
6
|
+
output chunk that carries the geometry. A bundle built with legal comments dropped or moved to a
|
|
7
|
+
separate file must ship these notices itself.
|
|
8
|
+
|
|
9
|
+
## Lucide
|
|
10
|
+
|
|
11
|
+
ISC License
|
|
12
|
+
|
|
13
|
+
Copyright (c) 2026 Lucide Icons and Contributors
|
|
14
|
+
|
|
15
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
16
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
17
|
+
copyright notice and this permission notice appear in all copies.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
20
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
21
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
22
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
23
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
24
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
25
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Lucide's LICENSE (`lucide-static` 1.59.0) lists the icons derived from the
|
|
28
|
+
Feather project, by their Feather names. Of those the shell inlines `check`,
|
|
29
|
+
`circle-x` (Feather's `x-circle`), `maximize`, `minus`, `plus`,
|
|
30
|
+
`triangle-alert` (Feather's `alert-triangle`) and `x`:
|
|
31
|
+
|
|
32
|
+
The MIT License (MIT)
|
|
33
|
+
|
|
34
|
+
Copyright (c) 2013-present Cole Bemis
|
|
35
|
+
|
|
36
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
37
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
38
|
+
in the Software without restriction, including without limitation the rights
|
|
39
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
40
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
41
|
+
furnished to do so, subject to the following conditions:
|
|
42
|
+
|
|
43
|
+
The above copyright notice and this permission notice shall be included in all
|
|
44
|
+
copies or substantial portions of the Software.
|
|
45
|
+
|
|
46
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
47
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
48
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
49
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
50
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
51
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
52
|
+
SOFTWARE.
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** The modes a Miragon modeler can show (guide §13.3). */
|
|
2
|
+
type Theme = "light" | "dark";
|
|
3
|
+
/** High contrast; written as `data-cd-kontrast="hoch"` on the same element as `data-cd-theme`. */
|
|
4
|
+
type Kontrast = "hoch";
|
|
5
|
+
interface Appearance {
|
|
6
|
+
theme: Theme;
|
|
7
|
+
kontrast?: Kontrast | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The one appearance decision of guide §13.5: a theme the modeler cannot show is never shown
|
|
11
|
+
* (`themes[0]` wins, so `["light"]` stays light whatever the host requests); `kontrast` always
|
|
12
|
+
* passes through. Throws when `themes` is empty.
|
|
13
|
+
*/
|
|
14
|
+
declare function resolveAppearance(requested: Appearance, themes: readonly Theme[]): Appearance;
|
|
15
|
+
/**
|
|
16
|
+
* The appearance VS Code requests for `vscode.window.activeColorTheme.kind`:
|
|
17
|
+
* 1 Light → light, 2 Dark → dark, 3 HighContrast → dark + hoch, 4 HighContrastLight → light + hoch;
|
|
18
|
+
* any other value → light.
|
|
19
|
+
*/
|
|
20
|
+
declare function appearanceFromColorThemeKind(kind: number): Appearance;
|
|
21
|
+
/** The values of the setting `miragon.<modeler>.colorTheme` (it ships once a modeler can show dark). */
|
|
22
|
+
type ColorThemeSetting = "automatic" | "light" | "dark";
|
|
23
|
+
/**
|
|
24
|
+
* The requested appearance in an IDE host: the setting's theme unless it is `automatic` (or
|
|
25
|
+
* missing), the contrast always from the color theme kind.
|
|
26
|
+
*/
|
|
27
|
+
declare function requestedAppearance(kind: number, setting?: ColorThemeSetting | undefined): Appearance;
|
|
28
|
+
/**
|
|
29
|
+
* The fallback signal inside a VS Code webview: the body classes `vscode-high-contrast-light`
|
|
30
|
+
* (checked first, VS Code adds `vscode-high-contrast` to it as well), `vscode-high-contrast`,
|
|
31
|
+
* `vscode-dark`, `vscode-light`. `undefined` when none is set. Never use them for styling.
|
|
32
|
+
*/
|
|
33
|
+
declare function appearanceFromBodyClasses(classes: Iterable<string>): Appearance | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* VS Code's `workbench.reduceMotion` as the webview sees it: the body class `vscode-reduce-motion`
|
|
36
|
+
* (the webview host page sets it). Never use it for styling; the shell mirrors it as
|
|
37
|
+
* `REDUCED_MOTION_CLASS` on `<html>`.
|
|
38
|
+
*/
|
|
39
|
+
declare function reducedMotionFromBodyClasses(classes: Iterable<string>): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* On `<html>` while the host asks for reduced motion although the OS does not (VS Code's
|
|
42
|
+
* `workbench.reduceMotion`): the shell's animations stop as under `prefers-reduced-motion`, and an
|
|
43
|
+
* explicit fit jumps.
|
|
44
|
+
*/
|
|
45
|
+
declare const REDUCED_MOTION_CLASS = "cd-shell-reduce-motion";
|
|
46
|
+
interface AppearanceAttributes {
|
|
47
|
+
"data-cd-theme": Theme;
|
|
48
|
+
"data-cd-kontrast"?: Kontrast;
|
|
49
|
+
}
|
|
50
|
+
/** The attributes for `<html>` (own document) or the modeler container (foreign page). */
|
|
51
|
+
declare function appearanceAttributes(appearance: Appearance): AppearanceAttributes;
|
|
52
|
+
|
|
53
|
+
export { type Appearance as A, type ColorThemeSetting as C, type Kontrast as K, REDUCED_MOTION_CLASS as R, type Theme as T, type AppearanceAttributes as a, appearanceAttributes as b, appearanceFromBodyClasses as c, appearanceFromColorThemeKind as d, requestedAppearance as e, resolveAppearance as f, reducedMotionFromBodyClasses as r };
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/** The modes a Miragon modeler can show (guide §13.3). */
|
|
2
|
+
type Theme = "light" | "dark";
|
|
3
|
+
/** High contrast; written as `data-cd-kontrast="hoch"` on the same element as `data-cd-theme`. */
|
|
4
|
+
type Kontrast = "hoch";
|
|
5
|
+
interface Appearance {
|
|
6
|
+
theme: Theme;
|
|
7
|
+
kontrast?: Kontrast | undefined;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* The one appearance decision of guide §13.5: a theme the modeler cannot show is never shown
|
|
11
|
+
* (`themes[0]` wins, so `["light"]` stays light whatever the host requests); `kontrast` always
|
|
12
|
+
* passes through. Throws when `themes` is empty.
|
|
13
|
+
*/
|
|
14
|
+
declare function resolveAppearance(requested: Appearance, themes: readonly Theme[]): Appearance;
|
|
15
|
+
/**
|
|
16
|
+
* The appearance VS Code requests for `vscode.window.activeColorTheme.kind`:
|
|
17
|
+
* 1 Light → light, 2 Dark → dark, 3 HighContrast → dark + hoch, 4 HighContrastLight → light + hoch;
|
|
18
|
+
* any other value → light.
|
|
19
|
+
*/
|
|
20
|
+
declare function appearanceFromColorThemeKind(kind: number): Appearance;
|
|
21
|
+
/** The values of the setting `miragon.<modeler>.colorTheme` (it ships once a modeler can show dark). */
|
|
22
|
+
type ColorThemeSetting = "automatic" | "light" | "dark";
|
|
23
|
+
/**
|
|
24
|
+
* The requested appearance in an IDE host: the setting's theme unless it is `automatic` (or
|
|
25
|
+
* missing), the contrast always from the color theme kind.
|
|
26
|
+
*/
|
|
27
|
+
declare function requestedAppearance(kind: number, setting?: ColorThemeSetting | undefined): Appearance;
|
|
28
|
+
/**
|
|
29
|
+
* The fallback signal inside a VS Code webview: the body classes `vscode-high-contrast-light`
|
|
30
|
+
* (checked first, VS Code adds `vscode-high-contrast` to it as well), `vscode-high-contrast`,
|
|
31
|
+
* `vscode-dark`, `vscode-light`. `undefined` when none is set. Never use them for styling.
|
|
32
|
+
*/
|
|
33
|
+
declare function appearanceFromBodyClasses(classes: Iterable<string>): Appearance | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* VS Code's `workbench.reduceMotion` as the webview sees it: the body class `vscode-reduce-motion`
|
|
36
|
+
* (the webview host page sets it). Never use it for styling; the shell mirrors it as
|
|
37
|
+
* `REDUCED_MOTION_CLASS` on `<html>`.
|
|
38
|
+
*/
|
|
39
|
+
declare function reducedMotionFromBodyClasses(classes: Iterable<string>): boolean;
|
|
40
|
+
/**
|
|
41
|
+
* On `<html>` while the host asks for reduced motion although the OS does not (VS Code's
|
|
42
|
+
* `workbench.reduceMotion`): the shell's animations stop as under `prefers-reduced-motion`, and an
|
|
43
|
+
* explicit fit jumps.
|
|
44
|
+
*/
|
|
45
|
+
declare const REDUCED_MOTION_CLASS = "cd-shell-reduce-motion";
|
|
46
|
+
interface AppearanceAttributes {
|
|
47
|
+
"data-cd-theme": Theme;
|
|
48
|
+
"data-cd-kontrast"?: Kontrast;
|
|
49
|
+
}
|
|
50
|
+
/** The attributes for `<html>` (own document) or the modeler container (foreign page). */
|
|
51
|
+
declare function appearanceAttributes(appearance: Appearance): AppearanceAttributes;
|
|
52
|
+
|
|
53
|
+
export { type Appearance as A, type ColorThemeSetting as C, type Kontrast as K, REDUCED_MOTION_CLASS as R, type Theme as T, type AppearanceAttributes as a, appearanceAttributes as b, appearanceFromBodyClasses as c, appearanceFromColorThemeKind as d, requestedAppearance as e, resolveAppearance as f, reducedMotionFromBodyClasses as r };
|