@miragon/modeler-shell 0.0.0-stage → 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 +2869 -0
- package/LICENSE +21 -0
- package/README.md +566 -2
- package/THIRD_PARTY_NOTICES.md +52 -0
- package/dist/appearance-BBaM_4bD.d.ts +323 -0
- package/dist/appearance-D3FUcbu6.d.cts +53 -0
- package/dist/appearance-D3FUcbu6.d.ts +53 -0
- package/dist/appearance-XX3JC3pI.d.cts +323 -0
- package/dist/chunk-66HCSJNY.js +2792 -0
- package/dist/chunk-66HCSJNY.js.map +1 -0
- package/dist/chunk-KYDG5DMX.js +136 -0
- package/dist/chunk-KYDG5DMX.js.map +1 -0
- package/dist/chunk-N4QXFF3A.js +23 -0
- package/dist/chunk-N4QXFF3A.js.map +1 -0
- package/dist/chunk-OADW3GPA.js +785 -0
- package/dist/chunk-OADW3GPA.js.map +1 -0
- package/dist/core.cjs +873 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +246 -0
- package/dist/core.d.ts +246 -0
- package/dist/core.js +131 -0
- package/dist/core.js.map +1 -0
- package/dist/diagram-js.cjs +1147 -0
- package/dist/diagram-js.cjs.map +1 -0
- package/dist/diagram-js.d.cts +170 -0
- package/dist/diagram-js.d.ts +170 -0
- package/dist/diagram-js.js +879 -0
- package/dist/diagram-js.js.map +1 -0
- package/dist/index.cjs +3632 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +29 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.js +143 -0
- package/dist/index.js.map +1 -0
- package/dist/protocol-DhYBakWL.d.cts +15 -0
- package/dist/protocol-IeW8bf4f.d.ts +15 -0
- package/dist/shell.css +1603 -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 +4663 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +231 -0
- package/dist/testing.d.ts +231 -0
- package/dist/testing.js +4483 -0
- package/dist/testing.js.map +1 -0
- package/dist/toasts-Bp0REqdq.d.cts +142 -0
- package/dist/toasts-Bp0REqdq.d.ts +142 -0
- package/dist/types-CnRrINRs.d.cts +445 -0
- package/dist/types-PuDZczzy.d.ts +445 -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 +3561 -0
- package/dist/vscode-webview.cjs.map +1 -0
- package/dist/vscode-webview.d.cts +84 -0
- package/dist/vscode-webview.d.ts +84 -0
- package/dist/vscode-webview.js +259 -0
- package/dist/vscode-webview.js.map +1 -0
- package/package.json +145 -4
- package/src/core/appearance.ts +104 -0
- 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 +220 -0
- package/src/core/index.ts +127 -0
- package/src/core/keymap.ts +295 -0
- package/src/core/layout.ts +69 -0
- package/src/core/messages.ts +159 -0
- package/src/core/sizes.ts +85 -0
- package/src/core/types.ts +106 -0
- 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/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 +243 -0
- package/src/diagram-js/types.ts +67 -0
- package/src/diagram-js/viewport.ts +583 -0
- package/src/diagram-js.ts +2 -0
- package/src/dom/buttons.css +124 -0
- package/src/dom/element.ts +51 -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/hint.ts +51 -0
- package/src/dom/icons.ts +309 -0
- package/src/dom/index.ts +42 -0
- package/src/dom/inspector.ts +35 -0
- package/src/dom/keys.ts +67 -0
- package/src/dom/ledger.ts +87 -0
- package/src/dom/legend.ts +85 -0
- package/src/dom/menu.ts +404 -0
- package/src/dom/mount.ts +731 -0
- package/src/dom/problems.ts +73 -0
- package/src/dom/shell-dom.css +969 -0
- package/src/dom/status.ts +72 -0
- package/src/dom/tooltip.ts +237 -0
- package/src/dom/types.ts +459 -0
- 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/dom/zoom-panel.ts +154 -0
- package/src/index.ts +3 -0
- package/src/shell.css +9 -0
- package/src/testing/case-tools.ts +164 -0
- package/src/testing/cases.ts +1077 -0
- package/src/testing/checks.ts +868 -0
- package/src/testing/facts.ts +641 -0
- package/src/testing/geometry.ts +652 -0
- package/src/testing/harness.ts +187 -0
- package/src/testing/index.ts +19 -0
- package/src/testing/page-tools.ts +457 -0
- package/src/testing/page.ts +27 -0
- package/src/testing/vscode-stub.ts +237 -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/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 +88 -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,567 @@
|
|
|
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, 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,
|
|
8
|
+
embedded in a foreign page), so the chrome looks and behaves the same everywhere.
|
|
9
|
+
|
|
10
|
+
- **Look and behaviour** follow the Miragon design system, guide
|
|
11
|
+
[`modeler-design-system.md`][guide] §5, §6, §8, §9, §11, §12 and §13.3 to §13.5 (skill
|
|
12
|
+
`miragon-brand:modeler-tool-design`; the repository is private, the link opens for Miragon staff).
|
|
13
|
+
- **Mechanics** (diagram-js services, VS Code quirks, why things are done the way they are) are in
|
|
14
|
+
the [implementation notes][notes].
|
|
15
|
+
- **API**: every entry point, type and rule in [API.md][api].
|
|
16
|
+
- **Plan**: Phase 4 of [Miragon/modeler-api#4](https://github.com/Miragon/modeler-api/issues/4); the
|
|
17
|
+
Event Storming modeler is the pilot.
|
|
18
|
+
|
|
19
|
+
Zero runtime dependencies: diagram-js services are typed structurally, the `vscode` namespace and
|
|
20
|
+
Playwright's `page` and `test` are passed in, nothing is imported at runtime. The stylesheet reads
|
|
21
|
+
only the design system's `--cd-ui-*` roles and `--cd-shell-*` sizes. All texts are English by default
|
|
22
|
+
and overridable through one messages table; numbers go through `Intl` in the UI language.
|
|
23
|
+
|
|
24
|
+
## Install
|
|
25
|
+
|
|
26
|
+
```sh
|
|
27
|
+
npm i --save-exact @miragon/modeler-shell
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
The modeler brings what the shell styles against and draws into:
|
|
31
|
+
|
|
32
|
+
- **diagram-js** 15.28 or later (directly or through its renderer);
|
|
33
|
+
- **the design tokens**: `cd-tokens.generated.css` (and `cd-tokens.generated.json` where scripts
|
|
34
|
+
need values) of the design system, 1.12.0 or later (the release with the `shell` group). Copy
|
|
35
|
+
both unchanged from the skill's `assets/` or from
|
|
36
|
+
<https://corporate-identity.netlify.app/brand/cd-tokens.generated.css> and
|
|
37
|
+
<https://corporate-identity.netlify.app/brand/cd-tokens.generated.json>, mark them as generated,
|
|
38
|
+
and re-copy instead of editing. The shell ships no tokens and never forks a value;
|
|
39
|
+
- **the fonts** Geist and Geist Mono, bundled locally, for example `@fontsource-variable/geist` and
|
|
40
|
+
`@fontsource-variable/geist-mono` (no CDN);
|
|
41
|
+
- for `./vscode/extension`: **VS Code 1.90 or later**, whose extension host runs Node 20 (the global
|
|
42
|
+
`crypto` that `createNonce` uses; the types are tested against `@types/vscode` 1.96). Declare
|
|
43
|
+
`engines.vscode` accordingly and target `node20` in the extension-host bundle.
|
|
44
|
+
|
|
45
|
+
Stylesheets, in this order. The tokens come first, unless the renderer's stylesheet already imports
|
|
46
|
+
them (Event Storming's does), and are never loaded twice:
|
|
47
|
+
|
|
48
|
+
```ts
|
|
49
|
+
import "./theme/cd-tokens.generated.css"; // vendored, unchanged, first; left out when the renderer CSS brings it
|
|
50
|
+
import "@fontsource-variable/geist/wght.css";
|
|
51
|
+
import "@fontsource-variable/geist-mono/wght.css";
|
|
52
|
+
import "diagram-js/assets/diagram-js.css"; // usually through the renderer's CSS
|
|
53
|
+
import "@miragon/<notation>-renderer/…css";
|
|
54
|
+
import "@miragon/modeler-shell/shell.css"; // last
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
The shell computes fit insets, breakpoints and layers from its own copy of the sizes (`SHELL`), while
|
|
58
|
+
its stylesheet reads the vendored `--cd-shell-*` tokens. Pin the two together in a unit test (Vitest
|
|
59
|
+
or `node:test`), not in the e2e suite, since the check is pure data:
|
|
60
|
+
|
|
61
|
+
```ts
|
|
62
|
+
// test/tokens.sync.test.ts
|
|
63
|
+
import { SHELL, SHELL_TOKENS, shellSizesFromTokens } from "@miragon/modeler-shell/core";
|
|
64
|
+
|
|
65
|
+
expect(SHELL).toEqual(shellSizesFromTokens(vendoredTokens.shell));
|
|
66
|
+
expect(Object.keys(vendoredTokens.shell)).toEqual(expect.arrayContaining(Object.values(SHELL_TOKENS)));
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
| Entry | For |
|
|
70
|
+
| -------------------------------------------- | ----------------------------------------------------------- |
|
|
71
|
+
| `@miragon/modeler-shell` | `mountShell`, icons, and everything of `./core` |
|
|
72
|
+
| `@miragon/modeler-shell/core` | DOM-free: fit math, keymap, zoom readout, appearance, sizes |
|
|
73
|
+
| `@miragon/modeler-shell/diagram-js` | `shellModule`, the viewport, `connectDiagramJs` |
|
|
74
|
+
| `@miragon/modeler-shell/vscode/extension` | webview HTML with CSP and nonce, theme bridge |
|
|
75
|
+
| `@miragon/modeler-shell/vscode/webview` | `mountVsCodeShell`, appearance handler, webview state |
|
|
76
|
+
| `@miragon/modeler-shell/testing` | the Playwright-driven shell conformance kit |
|
|
77
|
+
| `@miragon/modeler-shell/testing/vscode-stub` | a stand-in for VS Code around a webview test page |
|
|
78
|
+
| `@miragon/modeler-shell/shell.css` | every style of the shell |
|
|
79
|
+
|
|
80
|
+
TypeScript: every entry resolves under `moduleResolution` `node16`, `bundler` and `node10`, and
|
|
81
|
+
every optional input field accepts `undefined`, so a modeler compiling with
|
|
82
|
+
`exactOptionalPropertyTypes` forwards optional values as they are.
|
|
83
|
+
|
|
84
|
+
## Usage in a VS Code webview
|
|
85
|
+
|
|
86
|
+
Extension host:
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import * as vscode from "vscode";
|
|
90
|
+
import {
|
|
91
|
+
connectThemeBridge,
|
|
92
|
+
currentAppearance,
|
|
93
|
+
renderWebviewHtml,
|
|
94
|
+
} from "@miragon/modeler-shell/vscode/extension";
|
|
95
|
+
|
|
96
|
+
const THEMES = ["light"] as const; // until the shared dark-mode release of the modelers
|
|
97
|
+
|
|
98
|
+
// activate(): a hidden tab keeps document, view and selection
|
|
99
|
+
vscode.window.registerCustomEditorProvider(viewType, provider, {
|
|
100
|
+
webviewOptions: { retainContextWhenHidden: true },
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
// resolveCustomTextEditor():
|
|
104
|
+
const dist = vscode.Uri.joinPath(extensionUri, "dist");
|
|
105
|
+
webview.options = { enableScripts: true, localResourceRoots: [dist] }; // script, CSS and font files
|
|
106
|
+
const asset = (file: string) => webview.asWebviewUri(vscode.Uri.joinPath(dist, file)).toString();
|
|
107
|
+
webview.html = renderWebviewHtml({
|
|
108
|
+
cspSource: webview.cspSource,
|
|
109
|
+
scriptUri: asset("webview.js"),
|
|
110
|
+
styleUris: [asset("webview.css")],
|
|
111
|
+
title: "Event Storming Board",
|
|
112
|
+
lang: "en",
|
|
113
|
+
appearance: currentAppearance(vscode, { themes: THEMES }), // on <html> before the first paint
|
|
114
|
+
});
|
|
115
|
+
connectThemeBridge(vscode, webviewPanel, { themes: THEMES }); // disposes itself with the panel
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
The bridge's disposable is only for ending it before the panel closes; never push it into
|
|
119
|
+
`context.subscriptions`, which would keep one dead bridge per opened editor until the extension
|
|
120
|
+
deactivates.
|
|
121
|
+
|
|
122
|
+
Bundle the fonts as files next to the webview bundle, not as data URLs: with esbuild
|
|
123
|
+
`loader: { ".woff2": "file", ".woff": "file" }` and `assetNames: "fonts/[name]-[hash]"` the browser
|
|
124
|
+
fetches only the subsets a page's `unicode-range` needs (Geist Mono alone brings six; inlined they
|
|
125
|
+
roughly double the webview CSS). The CSP of `renderWebviewHtml` allows fonts from `cspSource`, and
|
|
126
|
+
`dist` is in `localResourceRoots`. A webview that bundles zod 4 runs `z.config({ jitless: true })` in
|
|
127
|
+
a module it imports before any schema module: the CSP forbids eval, zod probes once for eval when it
|
|
128
|
+
builds its first object schema, and the browser reports that caught `new Function` as a
|
|
129
|
+
`securitypolicyviolation`; under `jitless` zod skips the probe.
|
|
130
|
+
|
|
131
|
+
Webview:
|
|
132
|
+
|
|
133
|
+
```ts
|
|
134
|
+
import { INVALID_STATUS_DELAY_MS } from "@miragon/modeler-shell";
|
|
135
|
+
import { connectDiagramJs, shellModule } from "@miragon/modeler-shell/diagram-js";
|
|
136
|
+
import {
|
|
137
|
+
mountVsCodeShell,
|
|
138
|
+
persistView,
|
|
139
|
+
readShellState,
|
|
140
|
+
writeShellState,
|
|
141
|
+
} from "@miragon/modeler-shell/vscode/webview";
|
|
142
|
+
|
|
143
|
+
const vscode = acquireVsCodeApi();
|
|
144
|
+
const stored = readShellState(vscode);
|
|
145
|
+
|
|
146
|
+
// The diagram first: a modeler keyboard on document must run before the shell's key listener.
|
|
147
|
+
// A renderer that builds its diagram lazily builds it now; width and height follow the shell's slot.
|
|
148
|
+
// A renderer with its own Cmd/Ctrl+Z binding turns it off here, because VS Code's document history
|
|
149
|
+
// owns undo (Event Storming: createCollaborativeModeler({ undoRedoKeys: false }), see API.md).
|
|
150
|
+
const modeler = new Modeler({ width: "100%", height: "100%", additionalModules: [shellModule] });
|
|
151
|
+
modeler.get("canvas");
|
|
152
|
+
const shell = mountVsCodeShell(document.getElementById("app")!, {
|
|
153
|
+
themes: ["light"],
|
|
154
|
+
canvas: modeler.container,
|
|
155
|
+
menu: {
|
|
156
|
+
model: [/* Level; Type captions with keepEnabledWhenReadOnly, a view toggle */],
|
|
157
|
+
export: [/* Export as PNG, SVG */],
|
|
158
|
+
help: [/* Keyboard shortcuts… (the modeler's own dialog) */],
|
|
159
|
+
},
|
|
160
|
+
legend: {
|
|
161
|
+
render: renderLegend,
|
|
162
|
+
open: stored.legendOpen,
|
|
163
|
+
onToggle: (open) => writeShellState(vscode, { legendOpen: open }),
|
|
164
|
+
},
|
|
165
|
+
problems: { onActivate: () => vscode.postMessage({ type: "showProblems" }) },
|
|
166
|
+
emptyHint: { onInsertExample: () => vscode.postMessage({ type: "insertExample" }) },
|
|
167
|
+
});
|
|
168
|
+
const connection = connectDiagramJs(shell, modeler, {
|
|
169
|
+
// the tight bounds of the content, no padding: the shell's fit insets are the only margin
|
|
170
|
+
contentBounds: () => elementBounds(), // undefined while there is nothing to fit
|
|
171
|
+
isEmpty: () => elementCount() === 0, // cheap: no export, no validation
|
|
172
|
+
});
|
|
173
|
+
const { viewport } = connection;
|
|
174
|
+
persistView(vscode, viewport); // writes the view once it rested, not on every frame
|
|
175
|
+
|
|
176
|
+
async function importText(text: string, first: boolean): Promise<void> {
|
|
177
|
+
const keep = first ? undefined : viewport.getViewbox();
|
|
178
|
+
try {
|
|
179
|
+
await modeler.importDSL(text);
|
|
180
|
+
lockCanvas(false);
|
|
181
|
+
shell.setReadOnly(false);
|
|
182
|
+
shell.setStatus(null);
|
|
183
|
+
if (keep) viewport.restore(keep);
|
|
184
|
+
else if (first && stored.view) viewport.setState(stored.view);
|
|
185
|
+
viewport.afterImport(); // first content fits, an empty file shows 100 %, the hint follows by itself
|
|
186
|
+
} catch {
|
|
187
|
+
// locked at once, visible after 800ms without a valid import (no flicker while typing)
|
|
188
|
+
lockCanvas(true); // the modeler's own lock: context pad, move, resize, delete, paste, label editor
|
|
189
|
+
shell.setReadOnly(true, { delayMs: INVALID_STATUS_DELAY_MS });
|
|
190
|
+
shell.setStatus(
|
|
191
|
+
{ severity: "danger", text: shell.messages.invalidFile("Event Storming board") },
|
|
192
|
+
{ delayMs: INVALID_STATUS_DELAY_MS },
|
|
193
|
+
);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Imports strictly one after another; `ready` resolves once the first one settled.
|
|
198
|
+
let imports = Promise.resolve();
|
|
199
|
+
let firstImportSettled = () => {};
|
|
200
|
+
const ready = new Promise<void>((resolve) => (firstImportSettled = resolve));
|
|
201
|
+
window.addEventListener("message", ({ data }) => {
|
|
202
|
+
if (data?.type === "init")
|
|
203
|
+
imports = imports.then(() => importText(data.text, true)).then(firstImportSettled);
|
|
204
|
+
if (data?.type === "update") imports = imports.then(() => importText(data.text, false));
|
|
205
|
+
});
|
|
206
|
+
vscode.postMessage({ type: "ready" });
|
|
207
|
+
|
|
208
|
+
// For the e2e test page below; the bundle publishes nothing on window.
|
|
209
|
+
export const webview = { shell, modeler, connection, ready };
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
`mountVsCodeShell` stops every key the webview handles on `document`, before VS Code's listener on
|
|
213
|
+
`window` forwards it to the workbench, and keeps the webview in the mode the extension resolved: a
|
|
214
|
+
modeler declaring `["light"]` stays light under any VS Code theme, high contrast passes, and VS
|
|
215
|
+
Code's `workbench.reduceMotion` stops the chrome's animations. Undo and redo on the canvas go to VS
|
|
216
|
+
Code (the shell keeps diagram-js's bindings off them), so a modeler's own keyboard binds no Cmd/Ctrl+Z
|
|
217
|
+
in the webview either. Keys the canvas uses act only there: Cmd/Ctrl+A on the canvas selects every
|
|
218
|
+
element (or is at least prevented), so VS Code's Select All never selects the chrome. The modeler's
|
|
219
|
+
keyboard also ignores keys with `defaultPrevented` and keys from outside the canvas container, and
|
|
220
|
+
puts no `tabindex` on it.
|
|
221
|
+
|
|
222
|
+
`shell.setReadOnly(true)` locks the palette (all but Lasso and Hand) and the Model entries of the
|
|
223
|
+
menu. View toggles stay usable: the shell's Legend checkbox and the legend's close button, and every
|
|
224
|
+
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,
|
|
225
|
+
resize, delete, paste or label editor. Content the modeler adds to `shell.root` (a "Keyboard
|
|
226
|
+
shortcuts…" dialog) is the modeler's too: it removes it in its own teardown, for example on
|
|
227
|
+
`diagram.destroy`, since `shell.destroy()` removes only what the shell added. The complete Event
|
|
228
|
+
Storming example, including the export notification, is in [API.md][api]. Document sync, undo through
|
|
229
|
+
the document and diagnostics stay in the modeler for now.
|
|
230
|
+
|
|
231
|
+
## Usage in a web app
|
|
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
|
+
|
|
250
|
+
```ts
|
|
251
|
+
// src/main.ts
|
|
252
|
+
import {
|
|
253
|
+
commandKeyLabel,
|
|
254
|
+
defaultMessages,
|
|
255
|
+
detectPlatform,
|
|
256
|
+
mountShell,
|
|
257
|
+
problemSummary,
|
|
258
|
+
} from "@miragon/modeler-shell";
|
|
259
|
+
import { connectDiagramJs, shellModule } from "@miragon/modeler-shell/diagram-js";
|
|
260
|
+
|
|
261
|
+
// The diagram first: a modeler keyboard on document must run before the shell's key listener.
|
|
262
|
+
const modeler = new Modeler({ width: "100%", height: "100%", additionalModules: [shellModule] });
|
|
263
|
+
modeler.get("canvas");
|
|
264
|
+
const platform = detectPlatform(navigator);
|
|
265
|
+
const shell = mountShell(document.getElementById("app")!, {
|
|
266
|
+
host: "web",
|
|
267
|
+
canvas: modeler.container,
|
|
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) },
|
|
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) },
|
|
289
|
+
});
|
|
290
|
+
const { viewport } = connectDiagramJs(shell, modeler, { contentBounds: () => elementBounds() });
|
|
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
|
|
306
|
+
```
|
|
307
|
+
|
|
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.
|
|
317
|
+
|
|
318
|
+
## Usage in a foreign page
|
|
319
|
+
|
|
320
|
+
`mountShell(container, { host: "embedded", themes: ["light"], canvas })` on an explicitly sized
|
|
321
|
+
container: the root carries the tool's `lang` itself, keys act only while the focus is inside the
|
|
322
|
+
modeler, and the page's `<html>` stays untouched. Until the `@miragon/modeler-api` contract gains
|
|
323
|
+
its `appearance` surface (a later 1.x minor), an embedded modeler has no appearance channel, so the
|
|
324
|
+
root carries its own `data-cd-theme` (the first of `themes`, light) and stays light inside a dark
|
|
325
|
+
host page; it takes the page's `data-cd-kontrast="hoch"` over from the nearest mode carrier above
|
|
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).
|
|
329
|
+
|
|
330
|
+
## The conformance kit
|
|
331
|
+
|
|
332
|
+
Every modeler runs the kit in its own Playwright suite against its real webview or page. It checks
|
|
333
|
+
the geometry of every island (±1px), ARIA and the keyboard models, that every chrome key acts exactly
|
|
334
|
+
once and never reaches VS Code, that the canvas's own keys (Cmd/Ctrl+A) stay on the canvas, that
|
|
335
|
+
nothing is ever half-dark (the painted canvas surround and the palette's colours included), that
|
|
336
|
+
Enter on a creating palette entry adds one selected element, that an invalid file locks the canvas,
|
|
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.
|
|
340
|
+
|
|
341
|
+
For the `ide` host the suite serves a test page that puts a stand-in for VS Code around the
|
|
342
|
+
modeler's webview code: VS Code's default webview styles, `<html>` as `renderWebviewHtml` writes it,
|
|
343
|
+
the host page's theme classes, `acquireVsCodeApi()` and the real theme bridge. Serve
|
|
344
|
+
`e2e/webview/` as the root of a Vite server, so the page is `/webview-test.html`:
|
|
345
|
+
|
|
346
|
+
```ts
|
|
347
|
+
// e2e/webview/webview-test.ts, the entry of e2e/webview/webview-test.html
|
|
348
|
+
import { installVsCodeWebviewStub, stubOptionsFromSearch } from "@miragon/modeler-shell/testing/vscode-stub";
|
|
349
|
+
|
|
350
|
+
// before the webview runs, which calls acquireVsCodeApi() and mounts the shell
|
|
351
|
+
installVsCodeWebviewStub({ ...stubOptionsFromSearch(location.search), themes: ["light"] });
|
|
352
|
+
const { webview } = await import("../../src/webview/main"); // exports { shell, modeler, connection, ready }
|
|
353
|
+
|
|
354
|
+
// for the harness only: the shipped bundle publishes nothing on window
|
|
355
|
+
window.shell = webview.shell;
|
|
356
|
+
window.modeler = webview.modeler;
|
|
357
|
+
window.connection = webview.connection;
|
|
358
|
+
void webview.ready.then(() => (window.webviewReady = true)); // the first import settled
|
|
359
|
+
|
|
360
|
+
// there is no extension host: post the document the way the extension would
|
|
361
|
+
window.postMessage({ type: "init", text: EXAMPLE_BOARD }, "*");
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
`ready`, `webviewReady` and the window globals belong to the modeler's test page (declared in a
|
|
365
|
+
`.d.ts` of the suite), not to the shell: the webview resolves `ready` once its first `init` import
|
|
366
|
+
settled.
|
|
367
|
+
|
|
368
|
+
```ts
|
|
369
|
+
// e2e/shell-conformance.spec.ts
|
|
370
|
+
import { test } from "@playwright/test";
|
|
371
|
+
import { registerShellConformance, vsCodeStubSearch } from "@miragon/modeler-shell/testing";
|
|
372
|
+
|
|
373
|
+
test.describe("shell conformance", () => {
|
|
374
|
+
test.describe.configure({ timeout: 60_000 }); // geometry measures three sizes
|
|
375
|
+
registerShellConformance(
|
|
376
|
+
{
|
|
377
|
+
host: "ide",
|
|
378
|
+
themes: ["light"],
|
|
379
|
+
absent: ["inspector"], // islands this modeler does not have: never visible, never skipped
|
|
380
|
+
async open(page, { size, requested }) {
|
|
381
|
+
await page.setViewportSize(size);
|
|
382
|
+
await page.goto(`/webview-test.html?${vsCodeStubSearch(requested)}`);
|
|
383
|
+
await page.waitForFunction(() => window.webviewReady === true); // set by the test page
|
|
384
|
+
},
|
|
385
|
+
async destroy(page) {
|
|
386
|
+
// the webview's own teardown path: connection, diagram (with the modeler's dialogs), shell
|
|
387
|
+
await page.evaluate(() => {
|
|
388
|
+
window.connection.destroy();
|
|
389
|
+
window.modeler.destroy();
|
|
390
|
+
window.shell.destroy();
|
|
391
|
+
});
|
|
392
|
+
},
|
|
393
|
+
show: {
|
|
394
|
+
async legend(page) {
|
|
395
|
+
await page.evaluate(() => window.shell.legend?.setOpen(true));
|
|
396
|
+
},
|
|
397
|
+
async problems(page) {
|
|
398
|
+
await page.evaluate(() => window.postMessage({ type: "problems", count: 2, errors: 1 }, "*"));
|
|
399
|
+
},
|
|
400
|
+
async status(page) {
|
|
401
|
+
await page.evaluate(() =>
|
|
402
|
+
window.shell.setStatus({ severity: "danger", text: window.shell.messages.invalidFile("board") }),
|
|
403
|
+
);
|
|
404
|
+
},
|
|
405
|
+
async invalid(page) {
|
|
406
|
+
// the way the host makes the file invalid: a document update the parser rejects
|
|
407
|
+
await page.evaluate((text) => window.postMessage({ type: "update", text }, "*"), INVALID_BOARD);
|
|
408
|
+
},
|
|
409
|
+
},
|
|
410
|
+
},
|
|
411
|
+
test,
|
|
412
|
+
{
|
|
413
|
+
onSkip(caseName, skips) {
|
|
414
|
+
// with `absent` listing what the modeler lacks, every remaining skip is a gap of this suite
|
|
415
|
+
throw new Error(`${caseName}: ${skips.map(({ hook, reason }) => `${hook}: ${reason}`).join("; ")}`);
|
|
416
|
+
},
|
|
417
|
+
},
|
|
418
|
+
);
|
|
419
|
+
});
|
|
420
|
+
```
|
|
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
|
+
|
|
476
|
+
The harness opens the modeler with the appearance the kit requests, the way its host would; for
|
|
477
|
+
the `embedded` host the kit puts the foreign page itself in that mode. A skip names the harness
|
|
478
|
+
field it misses (`{ hook: "legend", reason }`; `hook: "destroy"` without a `destroy`; `hook: "open"`
|
|
479
|
+
when the opened document lacks what a case needs, such as a shape for `readOnly`); without `onSkip`
|
|
480
|
+
the kit warns on the console. What each case asserts, and what the kit installs in the page, is
|
|
481
|
+
listed in [API.md][api].
|
|
482
|
+
|
|
483
|
+
`registerShellConformance` declares every test inside the kit, so a runner reports them at the kit's
|
|
484
|
+
source (the package ships `src/` without its tests, so the location resolves). For test locations in
|
|
485
|
+
the suite's own spec file, register the cases there:
|
|
486
|
+
|
|
487
|
+
```ts
|
|
488
|
+
import { shellConformanceCases } from "@miragon/modeler-shell/testing";
|
|
489
|
+
|
|
490
|
+
for (const conformanceCase of shellConformanceCases()) {
|
|
491
|
+
test(`shell conformance: ${conformanceCase.name}`, async ({ page }) => {
|
|
492
|
+
const outcome = await conformanceCase.run(page, harness);
|
|
493
|
+
if (outcome?.skipped) failOnSkips(conformanceCase.name, outcome.skipped);
|
|
494
|
+
});
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
The test page runs the webview from source without a CSP. Add one smoke test of the production bundle
|
|
499
|
+
behind the HTML and CSP of `renderWebviewHtml` that fails on any `securitypolicyviolation` (the recipe
|
|
500
|
+
is in [API.md][api], `./vscode/extension`).
|
|
501
|
+
|
|
502
|
+
## Development
|
|
503
|
+
|
|
504
|
+
Run from the repository root (`-w packages/shell` for this package only):
|
|
505
|
+
|
|
506
|
+
```sh
|
|
507
|
+
npm run build -w packages/shell # tsup: ESM + CJS + d.ts, dist/shell.css
|
|
508
|
+
npm run typecheck -w packages/shell # src, core without the DOM library, demo
|
|
509
|
+
npm test -w packages/shell # Vitest: node (core, boundaries) and happy-dom projects
|
|
510
|
+
npx playwright install chromium # once
|
|
511
|
+
npm run test:e2e -w packages/shell # Playwright against demo/
|
|
512
|
+
npm run demo -w packages/shell # the demo page on http://localhost:5181
|
|
513
|
+
```
|
|
514
|
+
|
|
515
|
+
The demo page (`demo/`) is a small diagram-js modeler around the shell. Its query parameters choose
|
|
516
|
+
the host (`host=ide|web|embedded`), the modes the modeler declares (`themes=light,dark`), what the
|
|
517
|
+
host requests (`theme`, `kontrast=hoch`; embedded: the foreign page's own mode), the `ide` host's
|
|
518
|
+
`colorTheme` setting and `reduceMotion`, and the state to start in (`legend=open`, `inspector`,
|
|
519
|
+
`problems=2:1`, `status=danger`, `invalid`, `empty`, which opens at 100 %). In the `ide` host it runs
|
|
520
|
+
inside the stub of `./testing/vscode-stub`, and keeps view and legend state through
|
|
521
|
+
`acquireVsCodeApi()`.
|
|
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
|
+
|
|
528
|
+
The e2e suite runs the conformance kit for the `ide`, `web` and `embedded` hosts with a light-only
|
|
529
|
+
and a dark-capable modeler, switches VS Code themes and the `colorTheme` setting at runtime, checks
|
|
530
|
+
VS Code's reduce motion, the chrome's typography against VS Code's default styles and a hostile
|
|
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 |
|
|
549
|
+
|
|
550
|
+
A module imports another one only through its `index.ts`; `src/core` imports nothing outside
|
|
551
|
+
itself and compiles without the DOM library. `test/package-boundaries.test.ts` enforces this, the
|
|
552
|
+
zero runtime dependencies, the exact devDependency pins, the token-only stylesheets and the published
|
|
553
|
+
files (`dist`, `src` without its tests, `API.md` and the notices).
|
|
554
|
+
|
|
555
|
+
## License
|
|
556
|
+
|
|
557
|
+
MIT. The inlined Lucide icons are ISC (© Lucide Icons and Contributors), and `check`, `circle-x`,
|
|
558
|
+
`maximize`, `minus`, `plus`, `triangle-alert` and `x` derive from Feather, MIT (© Cole Bemis), as
|
|
559
|
+
Lucide's LICENSE lists them; see [THIRD_PARTY_NOTICES.md](THIRD_PARTY_NOTICES.md). `dist` keeps that
|
|
560
|
+
notice as a legal comment in the chunk with the icons, but a bundler's `legalComments` setting may
|
|
561
|
+
drop it: a VSIX or a web bundle that contains the shell carries the Lucide and Feather notices of
|
|
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.
|
|
564
|
+
|
|
565
|
+
[guide]: https://github.com/Miragon/corporate-identity/blob/main/plugins/miragon-brand/skills/modeler-tool-design/assets/modeler-design-system.md
|
|
566
|
+
[notes]: https://github.com/Miragon/modeler-api/blob/main/docs/shell/implementation-notes.md
|
|
567
|
+
[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.
|