@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
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/diagram-js.ts","../src/core/fit.ts","../src/core/zoom.ts","../src/core/keymap.ts","../src/core/appearance.ts","../src/core/messages.ts","../src/diagram-js/palette-skin.ts","../src/diagram-js/service.ts","../src/diagram-js/module.ts","../src/diagram-js/viewport.ts","../src/diagram-js/connect.ts"],"sourcesContent":["// \"@miragon/modeler-shell/diagram-js\": the diagram-js adapter (services typed structurally).\nexport * from \"./diagram-js/index.js\";\n","import { SHELL } from \"./sizes.js\";\nimport { LEGAL_LINE_HEIGHT, legalLineHeight } from \"./web-chrome/bottom-row.js\";\nimport type { Bounds, Insets, PaletteRow, Point, ScaledViewbox, ShellHost, Size } from \"./types.js\";\n\n/** Fit never zooms above 100 % (guide §5.9). */\nexport const MAX_FIT_ZOOM = 1;\n\n/** Duration of the explicit, animated fit (`--cd-motion-base`). */\nexport const FIT_ANIMATION_MS = 250;\n\n/**\n * The viewbox that shows `bounds` centred in the free rectangle (`container` minus `insets`) at the\n * largest scale that fits, capped at `maxZoom`. The box has the container's aspect ratio, so\n * diagram-js's `canvas.viewbox(box)` sets exactly this view. Returns `undefined` when the container\n * is 0 × 0 or `bounds` has neither width nor height; a zero width or height scales by the other\n * dimension. Where the insets leave no free room on an axis (a container lower or narrower than\n * its chrome), that axis centres in the whole container instead.\n */\nexport function fitViewbox(\n bounds: Bounds,\n container: Size,\n insets: Insets,\n maxZoom: number = MAX_FIT_ZOOM,\n): ScaledViewbox | undefined {\n if (!(container.width > 0 && container.height > 0)) return undefined;\n const hasWidth = bounds.width > 0;\n const hasHeight = bounds.height > 0;\n if (!hasWidth && !hasHeight) return undefined;\n\n const horizontal = freeExtent(container.width, insets.left, insets.right);\n const vertical = freeExtent(container.height, insets.top, insets.bottom);\n const contentWidth = hasWidth ? bounds.width : 0;\n const contentHeight = hasHeight ? bounds.height : 0;\n const scale = Math.min(\n maxZoom,\n hasWidth ? horizontal.extent / contentWidth : Infinity,\n hasHeight ? vertical.extent / contentHeight : Infinity,\n );\n const box: ScaledViewbox = {\n x: bounds.x + contentWidth / 2 - (horizontal.start + horizontal.extent / 2) / scale,\n y: bounds.y + contentHeight / 2 - (vertical.start + vertical.extent / 2) / scale,\n width: container.width / scale,\n height: container.height / scale,\n scale,\n };\n return Object.values(box).every(Number.isFinite) && scale > 0 ? box : undefined;\n}\n\nfunction freeExtent(total: number, before: number, after: number): { start: number; extent: number } {\n const extent = total - before - after;\n return extent > 0 ? { start: before, extent } : { start: 0, extent: total };\n}\n\n/**\n * The centre of the free rectangle (`container` minus `insets`) in pixels from the container's top\n * left, where `fitViewbox` centres the content and keyboard creation places an element (guide\n * §5.5). An axis the insets leave no room on centres in the whole container, as in `fitViewbox`.\n * Returns `undefined` when the container is 0 × 0.\n */\nexport function freeRectCenter(container: Size, insets: Insets): Point | undefined {\n if (!(container.width > 0 && container.height > 0)) return undefined;\n const horizontal = freeExtent(container.width, insets.left, insets.right);\n const vertical = freeExtent(container.height, insets.top, insets.bottom);\n return {\n x: horizontal.start + horizontal.extent / 2,\n y: vertical.start + vertical.extent / 2,\n };\n}\n\n/** What is open or shown when a fit runs (guide §5.9: every fit counts what is open right now). */\nexport interface FitLayout {\n container: Size;\n host: ShellHost;\n paletteRow: PaletteRow;\n /** The problems pill is shown (only the web app reserves room for it). */\n problemsVisible: boolean;\n /**\n * Web app: the zoom panel stands above the legal line (`bottom: 40px`) because the two would\n * meet in the bottom row of a narrow container (`zoomRowRaised`); the bottom then keeps 92px as\n * with the pill.\n */\n zoomRaised?: boolean | undefined;\n /**\n * Web app: the measured height of the legal line (`LEGAL_LINE_HEIGHT`, more where a narrow\n * container wraps it); the raised bottom row stands on it. Default one line.\n */\n legalHeight?: number | undefined;\n /** Rendered width of the open legend; `undefined` when it is closed or the modeler has none. */\n legendWidth?: number | undefined;\n /**\n * The open inspector; `undefined` when closed. Above `SHELL.narrow` it is the side panel; up to it\n * a bottom sheet whose top edge (`sheetTop`, px from the container top) must be measured.\n */\n inspector?: { sheetTop?: number | undefined } | undefined;\n}\n\n/**\n * The fit insets of guide §5.9: top 88 (second row 140), sides 24, bottom 64 (web app with pill or\n * with the zoom panel raised above the legal line 92, plus 20 per extra line of a wrapped legal line),\n * right 324 with an open side inspector, left 12 + legend width + 24 with an open legend above\n * `SHELL.veryNarrow`, bottom = distance to the sheet's top edge + 12 with an open bottom sheet.\n */\nexport function fitInsets(layout: FitLayout): Insets {\n const { container, host, paletteRow, problemsVisible, zoomRaised, legendWidth, inspector } = layout;\n const raisedBottomRow = host === \"web\" && (problemsVisible || zoomRaised === true);\n const wrappedLegalLines = legalLineHeight(layout.legalHeight) - LEGAL_LINE_HEIGHT;\n const insets: Insets = {\n top: paletteRow === \"second-row\" ? SHELL.fitInsetTopSecondRow : SHELL.fitInsetTop,\n right: SHELL.fitInsetSide,\n bottom: raisedBottomRow ? SHELL.fitInsetBottomWeb + wrappedLegalLines : SHELL.fitInsetBottom,\n left: SHELL.fitInsetSide,\n };\n\n const bottomSheetOpen = inspector !== undefined && container.width <= SHELL.narrow;\n if (inspector !== undefined && !bottomSheetOpen) {\n insets.right = SHELL.edge + SHELL.inspectorWidth + SHELL.fitInsetSide;\n }\n if (bottomSheetOpen) {\n // Guide §5.11: the sheet hides zoom panel, pill and legend, so only the sheet itself counts.\n insets.bottom =\n inspector.sheetTop === undefined\n ? SHELL.fitInsetBottom\n : container.height - inspector.sheetTop + SHELL.edge;\n } else if (legendWidth !== undefined && legendWidth > 0 && container.width > SHELL.veryNarrow) {\n insets.left = SHELL.edge + legendWidth + SHELL.fitInsetSide;\n }\n return insets;\n}\n\n/** Relative tolerance of `sameView` (diagram-js rounds the transform to three decimals). */\nexport const VIEW_TOLERANCE = 1e-3;\n\n/**\n * True when two views are the same within `tolerance`: the scale and the translation in screen\n * pixels (`x · scale`, `y · scale`) agree relative to `max(1, |value|)`. Width and height are\n * ignored, so a container resize is never a difference.\n */\nexport function sameView(\n a: Pick<ScaledViewbox, \"x\" | \"y\" | \"scale\">,\n b: Pick<ScaledViewbox, \"x\" | \"y\" | \"scale\">,\n tolerance: number = VIEW_TOLERANCE,\n): boolean {\n const close = (first: number, second: number): boolean =>\n Math.abs(first - second) <= tolerance * Math.max(1, Math.abs(first), Math.abs(second));\n return (\n close(a.scale, b.scale) && close(a.x * a.scale, b.x * b.scale) && close(a.y * a.scale, b.y * b.scale)\n );\n}\n\n/**\n * The view at `progress` (0…1) between two viewboxes of the same container: the scale is\n * interpolated in log space, the centre linearly. Used per animation frame by the animated fit.\n */\nexport function interpolateViewbox(from: ScaledViewbox, to: ScaledViewbox, progress: number): ScaledViewbox {\n if (!(progress > 0)) return { ...from };\n if (progress >= 1) return { ...to };\n const linear = (start: number, end: number): number => start + (end - start) * progress;\n const scale =\n from.scale > 0 && to.scale > 0\n ? Math.exp(linear(Math.log(from.scale), Math.log(to.scale)))\n : linear(from.scale, to.scale);\n const centreX = linear(from.x + from.width / 2, to.x + to.width / 2);\n const centreY = linear(from.y + from.height / 2, to.y + to.height / 2);\n const width = linear(from.width * from.scale, to.width * to.scale) / scale;\n const height = linear(from.height * from.scale, to.height * to.scale) / scale;\n return { x: centreX - width / 2, y: centreY - height / 2, width, height, scale };\n}\n\n/** `--cd-ease`, `cubic-bezier(0.4, 0, 0.2, 1)`, as a function of time 0…1. */\nexport function easeStandard(progress: number): number {\n return STANDARD_EASING(progress);\n}\n\nconst STANDARD_EASING = cubicBezier(0.4, 0, 0.2, 1);\n\n/** A CSS `cubic-bezier(x1, y1, x2, y2)` timing function (`x1`, `x2` within 0…1, so x(t) is monotone). */\nfunction cubicBezier(x1: number, y1: number, x2: number, y2: number): (progress: number) => number {\n const polynomial = (first: number, second: number) => {\n const c = 3 * first;\n const b = 3 * (second - first) - c;\n const a = 1 - c - b;\n return {\n at: (t: number): number => ((a * t + b) * t + c) * t,\n slope: (t: number): number => (3 * a * t + 2 * b) * t + c,\n };\n };\n const curveX = polynomial(x1, x2);\n const curveY = polynomial(y1, y2);\n const precision = 1e-7;\n\n const parameterFor = (x: number): number => {\n let t = x;\n for (let iteration = 0; iteration < 8; iteration++) {\n const error = curveX.at(t) - x;\n if (Math.abs(error) < precision) return t;\n const slope = curveX.slope(t);\n if (Math.abs(slope) < 1e-6) break;\n t -= error / slope;\n if (t < 0 || t > 1) break;\n }\n // Newton left the curve or stalled on a flat stretch: bisection always converges.\n let low = 0;\n let high = 1;\n t = x;\n for (let iteration = 0; iteration < 60; iteration++) {\n const value = curveX.at(t);\n if (Math.abs(value - x) < precision) break;\n if (value < x) low = t;\n else high = t;\n t = (low + high) / 2;\n }\n return t;\n };\n\n return (progress) => {\n if (!(progress > 0)) return 0;\n if (progress >= 1) return 1;\n return curveY.at(parameterFor(progress));\n };\n}\n","import type { ShellMessages } from \"./messages.js\";\n\n/** diagram-js `ZoomScroll` RANGE (not configurable): 20 % … 400 %. */\nexport const ZOOM_MIN = 0.2;\nexport const ZOOM_MAX = 4;\n\n/** Zoom out is disabled at 20 % and below (`zoom <= 0.2 · 1.001`), so it never zooms in. */\nexport function canZoomOut(zoom: number): boolean {\n return zoom > ZOOM_MIN * 1.001;\n}\n\n/** Zoom in is disabled at 400 % (`zoom >= 4 · 0.999`). */\nexport function canZoomIn(zoom: number): boolean {\n return zoom < ZOOM_MAX * 0.999;\n}\n\n/**\n * A zoom as a whole percentage in the UI language: `Intl.NumberFormat(lang, { style: \"percent\",\n * maximumFractionDigits: 0 })`, so English \"86%\", German \"86 %\" (with U+00A0).\n */\nexport function formatPercent(zoom: number, lang: string): string {\n return numberFormat(lang, \"percent\").format(zoom);\n}\n\n/** Any other number in the chrome (\"2 problems\"), via `Intl.NumberFormat(lang)`. */\nexport function formatNumber(value: number, lang: string): string {\n return numberFormat(lang, \"decimal\").format(value);\n}\n\nconst FALLBACK_LANG = \"en\";\nconst formats = new Map<string, Intl.NumberFormat>();\n\n// The readout formats on every animation frame of a zoom, so each formatter is built once. A\n// language tag Intl rejects (an empty `lang=\"\"`, a typo) falls back to English, the shell's default\n// language, instead of breaking the chrome.\nfunction numberFormat(lang: string, style: \"percent\" | \"decimal\"): Intl.NumberFormat {\n const key = `${style}|${lang}`;\n let format = formats.get(key);\n if (!format) {\n const options: Intl.NumberFormatOptions =\n style === \"percent\" ? { style, maximumFractionDigits: 0 } : { style };\n try {\n format = new Intl.NumberFormat(lang, options);\n } catch {\n format = new Intl.NumberFormat(FALLBACK_LANG, options);\n }\n formats.set(key, format);\n }\n return format;\n}\n\nexport interface ZoomReadout {\n /** \"86%\" */\n text: string;\n /** \"Zoom to 100% (now 86%)\" */\n ariaLabel: string;\n canZoomOut: boolean;\n canZoomIn: boolean;\n}\n\n/** Everything the zoom panel shows for a zoom: readout text, its accessible name and the limits. */\nexport function zoomReadout(\n zoom: number,\n lang: string,\n messages: Pick<ShellMessages, \"zoomToActualNow\">,\n): ZoomReadout {\n const text = formatPercent(zoom, lang);\n return {\n text,\n ariaLabel: messages.zoomToActualNow(formatPercent(1, lang), text),\n canZoomOut: canZoomOut(zoom),\n canZoomIn: canZoomIn(zoom),\n };\n}\n","import type { ShellMessages } from \"./messages.js\";\nimport type { ShellHost } from \"./types.js\";\n\n/** The only keys the chrome handles (guide §5.8). */\nexport type ChromeKey = \"zoomIn\" | \"zoomOut\" | \"zoomToActual\" | \"fit\";\n\n/** The `KeyboardEvent` fields the keymap reads. */\nexport interface KeyLike {\n key: string;\n code: string;\n metaKey: boolean;\n ctrlKey: boolean;\n shiftKey: boolean;\n altKey: boolean;\n isComposing?: boolean | undefined;\n}\n\n/**\n * Which chrome key `event` is, ignoring where it was pressed:\n * - `zoomIn`: Cmd/Ctrl + `+` or `=` (by `key`, with or without Shift), or `NumpadAdd` (by `code`);\n * - `zoomOut`: Cmd/Ctrl + `-` or `_`, or `NumpadSubtract`;\n * - `zoomToActual`: Cmd/Ctrl + `0`, or `Numpad0` (also with NumLock off);\n * - `fit`: Shift + `Digit1` (by `code`, every layout) without Cmd, Ctrl or Alt.\n * Cmd/Ctrl is `metaKey || ctrlKey` on every platform; nothing matches with `altKey` (AltGr).\n */\nexport function matchChromeKey(event: KeyLike): ChromeKey | undefined {\n if (event.altKey) return undefined;\n if (isCommand(event)) {\n if (event.key === \"+\" || event.key === \"=\" || event.code === \"NumpadAdd\") return \"zoomIn\";\n if (event.key === \"-\" || event.key === \"_\" || event.code === \"NumpadSubtract\") return \"zoomOut\";\n if (event.key === \"0\" || event.code === \"Numpad0\") return \"zoomToActual\";\n return undefined;\n }\n return event.shiftKey && event.code === \"Digit1\" ? \"fit\" : undefined;\n}\n\nfunction isCommand(event: KeyLike): boolean {\n return event.metaKey || event.ctrlKey;\n}\n\n/**\n * Cmd/Ctrl + Z, Cmd/Ctrl + Shift + Z or Cmd/Ctrl + Y, without Alt (by `key`, `code` as fallback).\n * Cmd/Ctrl + Shift + Y is not redo (VS Code toggles its Debug Console with it).\n */\nexport function isUndoRedoKey(event: KeyLike): boolean {\n if (event.altKey || !isCommand(event)) return false;\n const letter = undoRedoLetter(event);\n return letter === \"z\" || (letter === \"y\" && !event.shiftKey);\n}\n\n// The produced character decides on Latin layouts (QWERTZ swaps the Y and Z keys); only where the\n// layout produces no ASCII character (Cyrillic, Greek, …) the physical key stands in.\nfunction undoRedoLetter(event: KeyLike): string | undefined {\n if (event.key.length === 1 && event.key.charCodeAt(0) < 0x80) return event.key.toLowerCase();\n if (event.code === \"KeyZ\") return \"z\";\n if (event.code === \"KeyY\") return \"y\";\n return undefined;\n}\n\n/** A text field: every input, select list and editable text (label editor, inspector, dialogs). */\nexport const TEXT_FIELD_SELECTOR =\n \"input, textarea, select, [contenteditable]:not([contenteditable='false'])\";\n\n/** Where a key was pressed, as the shell's one `keydown` listener sees it. */\nexport interface KeyContext {\n host: ShellHost;\n /** The event target matches `TEXT_FIELD_SELECTOR` or is `isContentEditable`. */\n inTextField: boolean;\n /** The shell's menu dropdown is open. */\n menuOpen: boolean;\n /** A popover or dialog (`[role=\"dialog\"]`, modal or not) is open. */\n dialogOpen: boolean;\n /** A modal dialog (`[role=\"dialog\"][aria-modal=\"true\"]`) is open: chrome keys do not act. */\n modalOpen: boolean;\n /** `event.defaultPrevented` when the listener ran: the canvas (or the modeler) handled the key. */\n defaultPrevented: boolean;\n /**\n * The web app's start screen shows (`setEmptyState(true)`): there is no diagram to zoom, so the\n * zoom keys and Shift+1 are left to the browser, which zooms the page.\n */\n startScreen?: boolean | undefined;\n}\n\nexport type KeyAction = ChromeKey | \"closeMenu\";\n\nexport interface KeyDecision {\n /** What the shell executes; `undefined` = nothing. */\n action?: KeyAction;\n preventDefault: boolean;\n stopPropagation: boolean;\n}\n\n/**\n * The key rules of guide §5.8 and §13.5 in one place:\n * 1. Escape with the menu open: `closeMenu`, prevented; in the IDE host stopped whenever the menu\n * or a dialog/popover is open. During IME composition Escape belongs to the IME: no `closeMenu`.\n * 2. Zoom keys: executed and prevented unless composing, in a text field or with a modal dialog;\n * in the IDE host always stopped, also when not executed (VS Code must never zoom the window).\n * Behind the web app's start screen untouched: the browser zooms the page (WCAG 1.4.4).\n * 3. Shift+1: `fit` under the same conditions; otherwise untouched (it types \"!\").\n * 4. IDE host, text field, undo/redo key: stopped only (the field keeps its native undo).\n * 5. IDE host, `defaultPrevented`: stopped (the canvas handled it; VS Code must not run it again).\n * Everything else passes untouched. `stopPropagation` is only ever true in the IDE host.\n */\nexport function decideKey(event: KeyLike, context: KeyContext): KeyDecision {\n const stop = context.host === \"ide\";\n const composing = event.isComposing === true;\n\n if (event.key === \"Escape\" && !event.metaKey && !event.ctrlKey && !event.altKey && !event.shiftKey) {\n if (context.menuOpen && !composing) {\n return { action: \"closeMenu\", preventDefault: true, stopPropagation: stop };\n }\n if (context.menuOpen || context.dialogOpen) return { preventDefault: false, stopPropagation: stop };\n }\n\n const chromeKey = matchChromeKey(event);\n if (chromeKey !== undefined) {\n if (context.startScreen === true && !stop) return { preventDefault: false, stopPropagation: false };\n const blocked = composing || context.inTextField || context.modalOpen;\n if (!blocked) return { action: chromeKey, preventDefault: true, stopPropagation: stop };\n // Blocked zoom keys are still kept from VS Code (it would zoom the window); a blocked Shift+1\n // is a character and travels on like any other.\n return { preventDefault: false, stopPropagation: chromeKey === \"fit\" ? false : stop };\n }\n\n if (context.inTextField && isUndoRedoKey(event)) return { preventDefault: false, stopPropagation: stop };\n if (context.defaultPrevented) return { preventDefault: false, stopPropagation: stop };\n return { preventDefault: false, stopPropagation: false };\n}\n\n/** The keyboard the user sits at; decides the shortcut labels (guide §5.8). */\nexport type Platform = \"mac\" | \"other\";\n\n/** The `navigator` fields `detectPlatform` reads. */\nexport interface NavigatorLike {\n platform?: string | undefined;\n userAgentData?: { platform?: string | undefined } | undefined;\n}\n\n/**\n * `mac` when `userAgentData.platform ?? platform` matches `/mac/i`, else `other`; `other` without a\n * navigator. Read it in the webview or page itself, never from the extension host (VS Code Remote).\n */\nexport function detectPlatform(navigator?: NavigatorLike | undefined): Platform {\n // An empty userAgentData.platform means \"unknown\", not \"not a Mac\": fall back to navigator.platform.\n const platform = navigator?.userAgentData?.platform || navigator?.platform || \"\";\n return /mac/i.test(platform) ? \"mac\" : \"other\";\n}\n\n/**\n * The tooltip chip text: macOS `⌘+`, `⌘−` (U+2212), `⌘0`, `⇧1`; elsewhere `Ctrl+Plus`,\n * `Ctrl+Minus`, `Ctrl+0`, `Shift+1` with the key names of the messages table.\n */\nexport function shortcutLabel(\n key: ChromeKey,\n platform: Platform,\n keyNames: ShellMessages[\"keyNames\"],\n): string {\n if (platform === \"mac\") return MAC_SHORTCUT_LABELS[key];\n switch (key) {\n case \"zoomIn\":\n return `${keyNames.ctrl}+${keyNames.plus}`;\n case \"zoomOut\":\n return `${keyNames.ctrl}+${keyNames.minus}`;\n case \"zoomToActual\":\n return `${keyNames.ctrl}+0`;\n case \"fit\":\n return `${keyNames.shift}+1`;\n }\n}\n\n/**\n * The chip of a Cmd/Ctrl key a modeler binds itself (the web app's File entries, guide §5.4): macOS\n * `⌘O`, elsewhere `Ctrl+O` with the key name of the messages table (`Strg+O`). `key` as printed on\n * the key cap; pass the result to `FileMenuAction.shortcut` or `MenuActionEntry.shortcut`.\n */\nexport function commandKeyLabel(\n key: string,\n platform: Platform,\n keyNames: Pick<ShellMessages[\"keyNames\"], \"ctrl\">,\n): string {\n return platform === \"mac\" ? `⌘${key}` : `${keyNames.ctrl}+${key}`;\n}\n\nconst MAC_SHORTCUT_LABELS: Readonly<Record<ChromeKey, string>> = {\n zoomIn: \"⌘+\",\n zoomOut: \"⌘\\u2212\",\n zoomToActual: \"⌘0\",\n fit: \"⇧1\",\n};\n\n/**\n * The `aria-keyshortcuts` value (WAI-ARIA names): zoom in `Meta+= Meta+Plus`, zoom out `Meta+-`,\n * 100 % `Meta+0`, fit `Shift+1`; outside macOS `Control` instead of `Meta`.\n */\nexport function ariaKeyShortcuts(key: ChromeKey, platform: Platform): string {\n const command = platform === \"mac\" ? \"Meta\" : \"Control\";\n switch (key) {\n case \"zoomIn\":\n return `${command}+= ${command}+Plus`;\n case \"zoomOut\":\n return `${command}+-`;\n case \"zoomToActual\":\n return `${command}+0`;\n case \"fit\":\n return \"Shift+1\";\n }\n}\n\nconst MAC_MODIFIER_SYMBOLS: Readonly<Record<string, string>> = {\n \"⌘\": \"Meta\",\n \"⇧\": \"Shift\",\n \"⌥\": \"Alt\",\n \"⌃\": \"Control\",\n};\n\nconst MODIFIER_NAMES: Readonly<Record<string, string>> = {\n cmd: \"Meta\",\n command: \"Meta\",\n meta: \"Meta\",\n ctrl: \"Control\",\n control: \"Control\",\n strg: \"Control\",\n shift: \"Shift\",\n umschalt: \"Shift\",\n alt: \"Alt\",\n option: \"Alt\",\n opt: \"Alt\",\n};\n\nconst NAMED_KEYS: Readonly<Record<string, string>> = {\n esc: \"Escape\",\n escape: \"Escape\",\n del: \"Delete\",\n delete: \"Delete\",\n entf: \"Delete\",\n backspace: \"Backspace\",\n enter: \"Enter\",\n return: \"Enter\",\n tab: \"Tab\",\n space: \"Space\",\n home: \"Home\",\n end: \"End\",\n pageup: \"PageUp\",\n pagedown: \"PageDown\",\n arrowup: \"ArrowUp\",\n arrowdown: \"ArrowDown\",\n arrowleft: \"ArrowLeft\",\n arrowright: \"ArrowRight\",\n \"↑\": \"ArrowUp\",\n \"↓\": \"ArrowDown\",\n \"←\": \"ArrowLeft\",\n \"→\": \"ArrowRight\",\n plus: \"Plus\",\n};\n\nfunction ariaKeyName(key: string): string | undefined {\n const named = NAMED_KEYS[key.toLowerCase()];\n if (named) return named;\n if (/^f\\d{1,2}$/i.test(key)) return key.toUpperCase();\n if (key.length !== 1 || MAC_MODIFIER_SYMBOLS[key]) return undefined;\n if (key === \"+\") return \"Plus\";\n if (key === \" \") return \"Space\";\n return key.toUpperCase();\n}\n\n/**\n * The `aria-keyshortcuts` value (WAI-ARIA names, guide §5.6) for a shortcut chip label: `\"L\"` →\n * `\"L\"`, `\"⇧L\"` → `\"Shift+L\"`, `\"⌘⇧Z\"` → `\"Meta+Shift+Z\"`, `\"Ctrl+Shift+E\"` → `\"Control+Shift+E\"`,\n * `\"Del\"` → `\"Delete\"`, `\"Ctrl++\"` → `\"Control+Plus\"`. `undefined` for a label that is not one key\n * combination (`\"G then D\"`, `\"1–9\"`, empty).\n */\nexport function ariaKeyShortcutsFromLabel(label: string): string | undefined {\n let rest = label.trim();\n const modifiers: string[] = [];\n for (let symbol = MAC_MODIFIER_SYMBOLS[rest.charAt(0)]; symbol && rest.length > 1;) {\n modifiers.push(symbol);\n rest = rest.slice(1).trim();\n symbol = MAC_MODIFIER_SYMBOLS[rest.charAt(0)];\n }\n if (!rest || /\\s/.test(rest.replace(/\\s*\\+\\s*/g, \"+\"))) return undefined;\n const parts = rest.split(\"+\").map((part) => part.trim());\n let key = parts.pop() ?? \"\";\n if (key === \"\" && parts.at(-1) === \"\") {\n parts.pop();\n key = \"+\";\n }\n for (const part of parts) {\n const modifier = MODIFIER_NAMES[part.toLowerCase()];\n if (!modifier) return undefined;\n modifiers.push(modifier);\n }\n const name = ariaKeyName(key);\n return name ? [...new Set(modifiers), name].join(\"+\") : undefined;\n}\n","/** The modes a Miragon modeler can show (guide §13.3). */\nexport type Theme = \"light\" | \"dark\";\n\n/** High contrast; written as `data-cd-kontrast=\"hoch\"` on the same element as `data-cd-theme`. */\nexport type Kontrast = \"hoch\";\n\nexport interface Appearance {\n theme: Theme;\n kontrast?: Kontrast | undefined;\n}\n\n/**\n * The one appearance decision of guide §13.5: a theme the modeler cannot show is never shown\n * (`themes[0]` wins, so `[\"light\"]` stays light whatever the host requests); `kontrast` always\n * passes through. Throws when `themes` is empty.\n */\nexport function resolveAppearance(requested: Appearance, themes: readonly Theme[]): Appearance {\n const [fallback] = themes;\n if (fallback === undefined) {\n throw new Error(\n \"@miragon/modeler-shell: resolveAppearance needs at least one theme the modeler can show\",\n );\n }\n const theme = themes.includes(requested.theme) ? requested.theme : fallback;\n return withKontrast(theme, requested.kontrast);\n}\n\n// Appearances arrive through postMessage and settings, so only the one known value passes.\nfunction withKontrast(theme: Theme, kontrast: unknown): Appearance {\n return kontrast === \"hoch\" ? { theme, kontrast } : { theme };\n}\n\n/**\n * The appearance VS Code requests for `vscode.window.activeColorTheme.kind`:\n * 1 Light → light, 2 Dark → dark, 3 HighContrast → dark + hoch, 4 HighContrastLight → light + hoch;\n * any other value → light.\n */\nexport function appearanceFromColorThemeKind(kind: number): Appearance {\n switch (kind) {\n case 2:\n return { theme: \"dark\" };\n case 3:\n return { theme: \"dark\", kontrast: \"hoch\" };\n case 4:\n return { theme: \"light\", kontrast: \"hoch\" };\n default:\n return { theme: \"light\" };\n }\n}\n\n/** The values of the setting `miragon.<modeler>.colorTheme` (it ships once a modeler can show dark). */\nexport type ColorThemeSetting = \"automatic\" | \"light\" | \"dark\";\n\n/**\n * The requested appearance in an IDE host: the setting's theme unless it is `automatic` (or\n * missing), the contrast always from the color theme kind.\n */\nexport function requestedAppearance(kind: number, setting?: ColorThemeSetting | undefined): Appearance {\n const fromKind = appearanceFromColorThemeKind(kind);\n const theme = setting === \"light\" || setting === \"dark\" ? setting : fromKind.theme;\n return withKontrast(theme, fromKind.kontrast);\n}\n\n/**\n * The fallback signal inside a VS Code webview: the body classes `vscode-high-contrast-light`\n * (checked first, VS Code adds `vscode-high-contrast` to it as well), `vscode-high-contrast`,\n * `vscode-dark`, `vscode-light`. `undefined` when none is set. Never use them for styling.\n */\nexport function appearanceFromBodyClasses(classes: Iterable<string>): Appearance | undefined {\n const present = new Set(classes);\n if (present.has(\"vscode-high-contrast-light\")) return { theme: \"light\", kontrast: \"hoch\" };\n if (present.has(\"vscode-high-contrast\")) return { theme: \"dark\", kontrast: \"hoch\" };\n if (present.has(\"vscode-dark\")) return { theme: \"dark\" };\n if (present.has(\"vscode-light\")) return { theme: \"light\" };\n return undefined;\n}\n\n/**\n * VS Code's `workbench.reduceMotion` as the webview sees it: the body class `vscode-reduce-motion`\n * (the webview host page sets it). Never use it for styling; the shell mirrors it as\n * `REDUCED_MOTION_CLASS` on `<html>`.\n */\nexport function reducedMotionFromBodyClasses(classes: Iterable<string>): boolean {\n return new Set(classes).has(\"vscode-reduce-motion\");\n}\n\n/**\n * On `<html>` while the host asks for reduced motion although the OS does not (VS Code's\n * `workbench.reduceMotion`): the shell's animations stop as under `prefers-reduced-motion`, and an\n * explicit fit jumps.\n */\nexport const REDUCED_MOTION_CLASS = \"cd-shell-reduce-motion\";\n\nexport interface AppearanceAttributes {\n \"data-cd-theme\": Theme;\n \"data-cd-kontrast\"?: Kontrast;\n}\n\n/** The attributes for `<html>` (own document) or the modeler container (foreign page). */\nexport function appearanceAttributes(appearance: Appearance): AppearanceAttributes {\n return appearance.kontrast === \"hoch\"\n ? { \"data-cd-theme\": appearance.theme, \"data-cd-kontrast\": \"hoch\" }\n : { \"data-cd-theme\": appearance.theme };\n}\n","import type { ProblemSeverity } from \"./feedback/problems.js\";\nimport type { DropForbiddenReason } from \"./web-chrome/drop.js\";\n\n/**\n * Every text the shell shows or announces, in one table (guide §2, shared terms). English by\n * default; a localized modeler overrides the whole table in its language, numbers arrive already\n * formatted through `Intl` in that language.\n */\nexport interface ShellMessages {\n /** Menu trigger `aria-label` and tooltip, dropdown `aria-label`. */\n menu: string;\n /** Palette `aria-label`. */\n tools: string;\n /** `aria-label` of the focusable canvas `<svg>` (`role=\"application\"`); a modeler names its notation. */\n canvas: string;\n /** Zoom panel `aria-label`. */\n zoom: string;\n zoomOut: string;\n zoomIn: string;\n /** Tooltip of the readout, e.g. `(percent) => \"Zoom to \" + percent` with percent \"100%\". */\n zoomToActual(percent: string): string;\n /** `aria-label` of the readout: \"Zoom to 100% (now 86%)\". */\n zoomToActualNow(percent: string, now: string): string;\n fit: string;\n /** Legend title and the Model-group checkbox. */\n legend: string;\n closeLegend: string;\n /** Pill text: \"1 problem\", \"2 problems\". */\n problems(count: number, formattedCount: string): string;\n /** Pill `aria-label`: \"2 problems, 1 error\", \"2 problems, no errors\". */\n problemsLabel(count: number, errors: number, formattedCount: string, formattedErrors: string): string;\n /** Pill tooltip in the IDE host. */\n showProblems: string;\n /** Empty-file hint (guide §13.5). */\n emptyHint: string;\n insertExample: string;\n /** Status box for an invalid file; `diagram` names the notation (\"Event Storming board\"). */\n invalidFile(diagram: string): string;\n /** Key names in shortcut chips outside macOS (German: Strg, Umschalt, Plus, Minus). */\n keyNames: { ctrl: string; shift: string; plus: string; minus: string };\n\n // ---- Web host (0.2): guide §2 shared terms, §5.4, §5.7, §5.10, §8, §9 ----\n\n /** Default label and `aria-label` of the primary action (web app, top right). */\n share: string;\n /** `aria-label` of the legal line's `<nav>`. \"Impressum\" and \"Datenschutz\" are never translated (guide §2). */\n legal: string;\n /** First entry of the legal line, linking the Miragon home page: \"© 2026 Miragon GmbH\". */\n copyright(year: number): string;\n /** The description of every legal link for screen readers: they open in a new tab. */\n opensInNewTab: string;\n /** File group (web app) and the empty state's primary button. */\n newDiagram: string;\n /** File group: \"Open…\" (U+2026, it opens the browser's file dialog). */\n openFile: string;\n /** File group and the empty state's ghost button. */\n showExample: string;\n /** File group: \"Import…\". */\n importFile: string;\n /** Heading and `aria-label` of the Appearance radio group (web app with dark only, guide §13.3). */\n appearance: string;\n appearanceSystem: string;\n appearanceLight: string;\n appearanceDark: string;\n /** Title of the problems popover (web app). */\n problemsTitle: string;\n /** A row's severity for screen readers, the `aria-label` of its icon: \"Error\", \"Warning\". */\n problemSeverity(severity: ProblemSeverity): string;\n /** `aria-label` of a Close button (toasts). */\n close: string;\n /** Chip of the drop zone while a drag can be dropped (guide §9 Drag-over). */\n dropFile: string;\n /** Chip of the drop zone while the drag cannot be dropped (guide §9 Drop forbidden). */\n dropForbidden(reason: DropForbiddenReason): string;\n /** The empty state's drag-and-drop hint when the modeler has a drop zone. */\n dropHint: string;\n /** The text link after the empty state's hint that runs the File group's \"Open…\". */\n openFileLink: string;\n /**\n * Danger toast for a dropped file outside `DropZoneOptions.accept`; `accepted` is the accept list\n * joined by `Intl.ListFormat` (disjunction) in `lang`: \".storm, .json, or .txt\".\n */\n cannotOpenFile(name: string, accepted: string): string;\n}\n\n/** Every key optional; an `undefined` value keeps the default (a conditional override). */\nexport type ShellMessageOverrides = {\n [Key in Exclude<keyof ShellMessages, \"keyNames\">]?: ShellMessages[Key] | undefined;\n} & {\n keyNames?: { [Key in keyof ShellMessages[\"keyNames\"]]?: string | undefined } | undefined;\n};\n\nexport const defaultMessages: ShellMessages = {\n menu: \"Menu\",\n tools: \"Tools\",\n canvas: \"Diagram\",\n zoom: \"Zoom\",\n zoomOut: \"Zoom out\",\n zoomIn: \"Zoom in\",\n zoomToActual: (percent) => `Zoom to ${percent}`,\n zoomToActualNow: (percent, now) => `Zoom to ${percent} (now ${now})`,\n fit: \"Fit to view\",\n legend: \"Legend\",\n closeLegend: \"Close legend\",\n problems: (count, formattedCount) => `${formattedCount} ${count === 1 ? \"problem\" : \"problems\"}`,\n problemsLabel: (count, errors, formattedCount, formattedErrors) =>\n `${formattedCount} ${count === 1 ? \"problem\" : \"problems\"}, ${\n errors === 0 ? \"no errors\" : `${formattedErrors} ${errors === 1 ? \"error\" : \"errors\"}`\n }`,\n showProblems: \"Show in Problems view\",\n emptyHint: \"Pick a tool from the palette to start.\",\n insertExample: \"Insert example\",\n invalidFile: (diagram) =>\n `This file isn't a valid ${diagram} right now. The diagram shows the last valid version and is read-only until you fix the text.`,\n keyNames: { ctrl: \"Ctrl\", shift: \"Shift\", plus: \"Plus\", minus: \"Minus\" },\n share: \"Share\",\n legal: \"Legal\",\n copyright: (year) => `© ${year} Miragon GmbH`,\n opensInNewTab: \"Opens in a new tab\",\n newDiagram: \"New diagram\",\n openFile: \"Open…\",\n showExample: \"Show example\",\n importFile: \"Import…\",\n appearance: \"Appearance\",\n appearanceSystem: \"System\",\n appearanceLight: \"Light\",\n appearanceDark: \"Dark\",\n problemsTitle: \"Problems\",\n problemSeverity: (severity) => (severity === \"error\" ? \"Error\" : \"Warning\"),\n close: \"Close\",\n dropFile: \"Drop file here\",\n dropForbidden: (reason) =>\n reason === \"multiple\"\n ? \"Drop one file at a time\"\n : reason === \"dialog\"\n ? \"Close the dialog to drop a file\"\n : \"This modeler can't open this file type\",\n dropHint: \"Or drop a file here\",\n openFileLink: \"Open a file…\",\n cannotOpenFile: (name, accepted) => `Couldn't open “${name}”. Drop a ${accepted} file.`,\n};\n\n/** `defaultMessages` with `overrides` applied (`keyNames` merged key by key). */\nexport function resolveMessages(overrides?: ShellMessageOverrides | undefined): ShellMessages {\n const { keyNames, ...texts } = overrides ?? {};\n return {\n ...defaultMessages,\n ...definedEntries(texts),\n keyNames: { ...defaultMessages.keyNames, ...definedEntries(keyNames ?? {}) },\n };\n}\n\n// `{ menu: undefined }` (a spread of optional fields, a conditional override) keeps the default\n// instead of blanking the text.\nfunction definedEntries<T extends object>(partial: T): { [Key in keyof T]?: Exclude<T[Key], undefined> } {\n return Object.fromEntries(Object.entries(partial).filter(([, value]) => value !== undefined)) as {\n [Key in keyof T]?: Exclude<T[Key], undefined>;\n };\n}\n","import { ariaKeyShortcutsFromLabel, defaultMessages } from \"../core/index.js\";\nimport type { PaletteReadOnlyState, TooltipContent } from \"../dom/index.js\";\n\n/** The diagram-js palette group of the tools (`Palette#updateToolHighlight` looks for it). */\nexport const TOOLS_GROUP = \"tools\";\n\n/**\n * The entries that stay active on a read-only canvas (guide §13.5: Lasso and Hand). Every other\n * entry changes the diagram, also tools such as drawing, space or connect.\n */\nexport const READ_ONLY_ACTIONS: ReadonlySet<string> = new Set([\"lasso-tool\", \"hand-tool\"]);\n\nconst TOOL_ACTION_SUFFIX = \"-tool\";\nconst ENTRIES_SELECTOR = \".djs-palette-entries\";\nconst ENTRY_SELECTOR = \".entry\";\n\n/** One entry of `palette.getEntries()`, as far as the skin reads it. */\nexport interface PaletteEntryLike {\n title?: string | undefined;\n shortcut?: string | undefined;\n /** `aria-keyshortcuts` (WAI-ARIA names); default `ariaKeyShortcutsFromLabel(shortcut)`. */\n ariaKeyShortcuts?: string | undefined;\n group?: string | undefined;\n}\n\n/**\n * The shell's additions to the diagram-js palette container (guide §5.5): `data-chrome`, the\n * toolbar semantics of the entries, the roving tab stop with its keys, `aria-pressed` for the\n * active tool and `aria-disabled` in read-only. diagram-js clears and rebuilds every entry on each\n * change, so `refresh()` re-applies all of it after every `palette.changed`.\n */\nexport class PaletteSkin {\n private label = defaultMessages.tools;\n private activeTool: string | null = null;\n /** The veto of a read-only canvas, at once with the invalid import. */\n private locked = false;\n /** The `aria-disabled` marks of a read-only canvas, once the shell's delay has passed. */\n private readOnlyShown = false;\n private entries: Readonly<Record<string, PaletteEntryLike>> = {};\n private rovingAction: string | undefined;\n private focusedEntry: HTMLElement | undefined;\n /** The `aria-disabled` an entry had before read-only marked it (`null`: none). */\n private readonly disabledEntries = new WeakMap<Element, string | null>();\n\n constructor(\n readonly element: HTMLElement,\n private readonly readEntries: () => Readonly<Record<string, PaletteEntryLike>>,\n ) {\n element.setAttribute(\"data-chrome\", \"palette\");\n element.addEventListener(\"keydown\", this.onKeydown);\n element.addEventListener(\"focusin\", this.onFocusIn);\n element.addEventListener(\"focusout\", this.onFocusOut);\n this.refresh();\n }\n\n setLabel(label: string): void {\n this.label = label;\n this.apply();\n }\n\n setActiveTool(tool: string | null): void {\n this.activeTool = tool;\n this.apply();\n }\n\n setReadOnly(state: PaletteReadOnlyState): void {\n this.locked = state.locked;\n this.readOnlyShown = state.locked && state.shown;\n this.apply();\n }\n\n /**\n * Whether `palette.trigger` must refuse `entry`: on a locked canvas every entry but Lasso and\n * Hand, also before their marks show. `palette.trigger` carries the entry definition without its\n * id, so the entry element is found through the DOM event; without one, only the tools group\n * passes.\n */\n vetoes(entry: PaletteEntryLike, event: Event | undefined): boolean {\n if (!this.locked) return false;\n const element = this.entryOf(\n (event as { delegateTarget?: EventTarget } | undefined)?.delegateTarget ?? event?.target ?? null,\n );\n if (element) return !staysActive(element);\n return entry.group !== TOOLS_GROUP;\n }\n\n /** After `palette.changed`: re-read the entries and re-apply every attribute. */\n refresh(): void {\n this.entries = this.readEntries();\n this.apply();\n }\n\n entryTooltip(entry: HTMLElement): TooltipContent | null {\n const action = entry.getAttribute(\"data-action\");\n const definition = action === null ? undefined : this.entries[action];\n if (!definition?.title) return null;\n return definition.shortcut\n ? { label: definition.title, shortcut: definition.shortcut }\n : { label: definition.title };\n }\n\n destroy(): void {\n this.element.removeEventListener(\"keydown\", this.onKeydown);\n this.element.removeEventListener(\"focusin\", this.onFocusIn);\n this.element.removeEventListener(\"focusout\", this.onFocusOut);\n this.focusedEntry = undefined;\n }\n\n private apply(): void {\n const container = this.element.querySelector<HTMLElement>(ENTRIES_SELECTOR);\n if (container) {\n container.setAttribute(\"role\", \"toolbar\");\n container.setAttribute(\"aria-label\", this.label);\n }\n const entries = this.entryElements();\n for (const entry of entries) {\n if (!entry.hasAttribute(\"role\")) entry.setAttribute(\"role\", \"button\");\n this.applyKeyShortcuts(entry);\n this.applyPressed(entry);\n this.applyDisabled(entry);\n }\n this.applyTabStops(entries);\n this.restoreFocus(entries);\n }\n\n // Guide §5.6: a button with a shortcut names it for assistive technology, not only in the chip.\n private applyKeyShortcuts(entry: HTMLElement): void {\n const action = actionOf(entry);\n const definition = action === undefined ? undefined : this.entries[action];\n const keyShortcuts =\n definition?.ariaKeyShortcuts ??\n (definition?.shortcut ? ariaKeyShortcutsFromLabel(definition.shortcut) : undefined);\n if (keyShortcuts) entry.setAttribute(\"aria-keyshortcuts\", keyShortcuts);\n }\n\n private applyPressed(entry: HTMLElement): void {\n const action = actionOf(entry);\n if (groupOf(entry) !== TOOLS_GROUP || !action?.endsWith(TOOL_ACTION_SUFFIX)) return;\n const tool = action.slice(0, -TOOL_ACTION_SUFFIX.length);\n entry.setAttribute(\"aria-pressed\", String(tool === this.activeTool));\n }\n\n private applyDisabled(entry: HTMLElement): void {\n if (this.readOnlyShown && !staysActive(entry)) {\n if (!this.disabledEntries.has(entry)) {\n this.disabledEntries.set(entry, entry.getAttribute(\"aria-disabled\"));\n }\n entry.setAttribute(\"aria-disabled\", \"true\");\n return;\n }\n if (!this.disabledEntries.has(entry)) return;\n const previous = this.disabledEntries.get(entry);\n this.disabledEntries.delete(entry);\n if (previous === null || previous === undefined) entry.removeAttribute(\"aria-disabled\");\n else entry.setAttribute(\"aria-disabled\", previous);\n }\n\n private applyTabStops(entries: readonly HTMLElement[]): void {\n const stop = entries.find((entry) => actionOf(entry) === this.rovingAction) ?? entries[0];\n for (const entry of entries) entry.tabIndex = entry === stop ? 0 : -1;\n }\n\n // A rebuild removes the focused entry; without this the focus would fall back to <body>.\n private restoreFocus(entries: readonly HTMLElement[]): void {\n const lost = this.focusedEntry;\n if (!lost || lost.isConnected) return;\n this.focusedEntry = undefined;\n const replacement = entries.find((entry) => actionOf(entry) === actionOf(lost));\n const active = this.element.ownerDocument.activeElement;\n const focusIsNowhere = !active || active === this.element.ownerDocument.body;\n if (replacement && focusIsNowhere) replacement.focus();\n }\n\n private entryElements(): HTMLElement[] {\n const container = this.element.querySelector(ENTRIES_SELECTOR);\n return container ? Array.from(container.querySelectorAll<HTMLElement>(ENTRY_SELECTOR)) : [];\n }\n\n private entryOf(target: EventTarget | null): HTMLElement | undefined {\n if (!(target instanceof Element)) return undefined;\n const entry = target.closest<HTMLElement>(ENTRY_SELECTOR);\n return entry && this.element.contains(entry) ? entry : undefined;\n }\n\n private readonly onFocusIn = (event: FocusEvent): void => {\n const entry = this.entryOf(event.target);\n if (!entry) return;\n this.focusedEntry = entry;\n this.rovingAction = actionOf(entry);\n this.applyTabStops(this.entryElements());\n };\n\n private readonly onFocusOut = (event: FocusEvent): void => {\n const entry = this.entryOf(event.target);\n if (!entry || entry !== this.focusedEntry || !entry.isConnected) return;\n const next = event.relatedTarget;\n if (!(next instanceof Node) || !this.element.contains(next)) this.focusedEntry = undefined;\n };\n\n private readonly onKeydown = (event: KeyboardEvent): void => {\n if (event.defaultPrevented || event.altKey || event.ctrlKey || event.metaKey || event.shiftKey) return;\n const entry = this.entryOf(event.target);\n if (!entry) return;\n const entries = this.entryElements();\n const index = entries.indexOf(entry);\n const last = entries.length - 1;\n let next: HTMLElement | undefined;\n switch (event.key) {\n case \"ArrowLeft\":\n next = entries[index <= 0 ? last : index - 1];\n break;\n case \"ArrowRight\":\n next = entries[index >= last ? 0 : index + 1];\n break;\n case \"Home\":\n next = entries[0];\n break;\n case \"End\":\n next = entries[last];\n break;\n case \"Enter\":\n case \" \":\n event.preventDefault();\n event.stopPropagation();\n if (!event.repeat) activate(entry);\n return;\n default:\n return;\n }\n event.preventDefault();\n event.stopPropagation();\n if (!next) return;\n this.rovingAction = actionOf(next);\n this.applyTabStops(entries);\n next.focus();\n };\n}\n\nfunction actionOf(entry: Element): string | undefined {\n return entry.getAttribute(\"data-action\") ?? undefined;\n}\n\nfunction staysActive(entry: Element): boolean {\n const action = actionOf(entry);\n return action !== undefined && READ_ONLY_ACTIONS.has(action);\n}\n\nfunction groupOf(entry: Element): string | undefined {\n return entry.closest(\".group\")?.getAttribute(\"data-group\") ?? undefined;\n}\n\n// Enter and Space activate an entry with a synthetic click: `detail` 0, clientX/Y at the entry's\n// centre. A creating entry tells it from a pointer click (`detail` ≥ 1) by that and places its\n// element at `viewport.freeRectCenter()` (guide §5.5). diagram-js handles entry clicks through a\n// delegate on the palette container, so it goes through `palette.trigger` and its read-only veto\n// exactly like a pointer click.\nfunction activate(entry: HTMLElement): void {\n const bounds = entry.getBoundingClientRect();\n entry.dispatchEvent(\n new MouseEvent(\"click\", {\n bubbles: true,\n cancelable: true,\n clientX: bounds.left + bounds.width / 2,\n clientY: bounds.top + bounds.height / 2,\n }),\n );\n}\n","import { TEXT_FIELD_SELECTOR, defaultMessages, isUndoRedoKey, matchChromeKey } from \"../core/index.js\";\nimport type { ShellHandle } from \"../dom/index.js\";\nimport { PaletteSkin, type PaletteEntryLike } from \"./palette-skin.js\";\nimport type {\n CanvasLike,\n DiagramEventListener,\n EventBusLike,\n InjectorLike,\n KeyboardLike,\n PaletteLike,\n} from \"./types.js\";\n\n/** The didi name of the service `shellModule` registers. */\nexport const SHELL_SERVICE = \"modelerShell\";\n\n/** Above `KeyboardBindings` and the modelers' keyboard modules (default priority 1000). */\nexport const KEYBOARD_PRIORITY = 2000;\n\n/** Ahead of every default-priority `palette.trigger` listener, so the read-only veto comes first. */\nconst TRIGGER_PRIORITY = 2000;\n\n/** A palette without a shell is editable. */\nconst EDITABLE = { locked: false, shown: false } as const;\n\n/** What the shell sets on the canvas `<svg>` while a shell is attached, restored on detach. */\nconst CANVAS_ATTRIBUTES = [\"role\", \"aria-label\"] as const;\n\nfunction isTextField(target: EventTarget | null): boolean {\n if (!target || typeof (target as Element).closest !== \"function\") return false;\n return (\n (target as HTMLElement).isContentEditable === true ||\n (target as Element).closest(TEXT_FIELD_SELECTOR) !== null\n );\n}\n\n/** didi's injector, which resolves a missing service to `null` with `strict: false`. */\ninterface DidiInjectorLike {\n get(name: string, strict?: boolean): unknown;\n}\n\ninterface KeyboardEventContext {\n keyEvent?: KeyboardEvent;\n}\n\ninterface PaletteTriggerEvent {\n entry?: PaletteEntryLike;\n event?: Event;\n}\n\n/**\n * The diagram-side half of `shellModule`: the palette skin, the chrome-key guard on the diagram-js\n * keyboard and the buffer that holds the palette until `connectDiagramJs` hands it to a shell.\n */\nexport class ShellDiagramService {\n static $inject = [\"eventBus\", \"injector\"];\n\n private svg: Element | undefined;\n /** The canvas `<svg>`'s own `role` and `aria-label` before the shell named it. */\n private canvasOriginals: Map<string, string | null> | undefined;\n private skin: PaletteSkin | undefined;\n private activeTool: string | null = null;\n private shell: ShellHandle | undefined;\n private unregisterPalette: (() => void) | undefined;\n private readonly subscriptions: Array<readonly [string, DiagramEventListener]> = [];\n private destroyed = false;\n\n constructor(\n private readonly eventBus: EventBusLike,\n private readonly injector: DidiInjectorLike,\n ) {\n this.subscribe(\"canvas.init\", (event: { svg?: Element }) => {\n this.svg = event.svg;\n this.nameCanvas();\n });\n this.subscribe(\"palette.create\", (event: { container: HTMLElement }) =>\n this.onPaletteCreate(event.container),\n );\n this.subscribe(\"palette.changed\", () => this.skin?.refresh());\n this.subscribe(\"tool-manager.update\", (event: { tool?: string | null }) => {\n this.activeTool = event.tool ?? null;\n this.skin?.setActiveTool(this.activeTool);\n });\n this.subscribe(\"palette.trigger\", this.onPaletteTrigger, TRIGGER_PRIORITY);\n this.subscribe(\"diagram.destroy\", () => this.destroy());\n\n const keyboard = injector.get(\"keyboard\", false) as KeyboardLike | null;\n if (keyboard) {\n keyboard.addListener(KEYBOARD_PRIORITY, this.onKeyboardKeydown);\n this.subscriptions.push([\"keyboard.keydown\", this.onKeyboardKeydown]);\n }\n }\n\n /**\n * Hands the palette (now or once diagram-js creates it) to `shell`; returns the detach\n * function. A later call replaces the earlier shell.\n */\n attachShell(shell: ShellHandle): () => void {\n this.detachShell();\n if (this.destroyed) return () => undefined;\n this.shell = shell;\n this.skin?.setLabel(shell.messages.tools);\n this.nameCanvas();\n this.registerPalette();\n return () => {\n if (this.shell === shell) this.detachShell();\n };\n }\n\n private registerPalette(): void {\n const { shell, skin } = this;\n if (!shell || !skin) return;\n this.unregisterPalette?.();\n this.unregisterPalette = shell.registerPalette({\n element: skin.element,\n entryTooltip: (entry) => skin.entryTooltip(entry),\n setReadOnly: (state) => skin.setReadOnly(state),\n });\n }\n\n private detachShell(): void {\n const unregister = this.unregisterPalette;\n this.unregisterPalette = undefined;\n this.shell = undefined;\n unregister?.();\n this.unnameCanvas();\n this.skin?.setReadOnly(EDITABLE);\n this.skin?.setLabel(defaultMessages.tools);\n }\n\n /**\n * The focusable canvas is an application with a name (WCAG 4.1.2): screen readers switch to focus\n * mode, so the canvas's single-key commands reach it. A role or name the modeler set stays.\n */\n private nameCanvas(): void {\n const { svg, shell } = this;\n if (!svg || !shell || this.canvasOriginals) return;\n this.canvasOriginals = new Map(CANVAS_ATTRIBUTES.map((name) => [name, svg.getAttribute(name)]));\n if (!svg.hasAttribute(\"role\")) svg.setAttribute(\"role\", \"application\");\n if (!svg.hasAttribute(\"aria-label\") && !svg.hasAttribute(\"aria-labelledby\")) {\n svg.setAttribute(\"aria-label\", shell.messages.canvas);\n }\n }\n\n private unnameCanvas(): void {\n const { svg, canvasOriginals } = this;\n this.canvasOriginals = undefined;\n if (!svg || !canvasOriginals) return;\n for (const [name, value] of canvasOriginals) {\n if (value === null) svg.removeAttribute(name);\n else svg.setAttribute(name, value);\n }\n }\n\n private onPaletteCreate(container: HTMLElement): void {\n this.skin?.destroy();\n const palette = this.injector.get(\"palette\", false) as PaletteLike | null;\n const skin = new PaletteSkin(container, () => palette?.getEntries() ?? {});\n this.skin = skin;\n skin.setActiveTool(this.activeTool);\n if (this.shell) skin.setLabel(this.shell.messages.tools);\n this.moveBeforeCanvas(container);\n this.registerPalette();\n }\n\n // Palette#_init appends the palette after the <svg>; in front of it the palette comes first in\n // the tab order (guide §5.14).\n private moveBeforeCanvas(container: HTMLElement): void {\n const parent = container.parentElement ?? this.canvasContainer();\n if (!parent) return;\n const svg =\n this.svg?.parentNode === parent\n ? this.svg\n : Array.from(parent.children).find((child) => child.localName === \"svg\");\n if (svg) parent.insertBefore(container, svg);\n }\n\n private canvasContainer(): HTMLElement | undefined {\n const canvas = this.injector.get(\"canvas\", false) as CanvasLike | null;\n return canvas?.getContainer();\n }\n\n private readonly onPaletteTrigger = (event: PaletteTriggerEvent): false | undefined => {\n if (!event.entry || !this.skin?.vetoes(event.entry, event.event)) return undefined;\n // A vetoed dragstart would otherwise still start the browser's native drag of the entry.\n event.event?.preventDefault();\n return false;\n };\n\n /**\n * `true` stops the diagram-js keyboard (and makes it call `preventDefault`), `false` stops it\n * without: a chrome key in a text field inside the `<svg>` stays the field's (Shift+1 types \"!\"),\n * and in the IDE host undo and redo stay the document's (guide §13.5), so they reach VS Code\n * instead of the canvas `commandStack`.\n */\n private readonly onKeyboardKeydown = (context: KeyboardEventContext): boolean | undefined => {\n const event = context.keyEvent;\n if (!event) return undefined;\n // `false` keeps every other diagram-js listener off the key without preventing it: in a text\n // field the field keeps it, behind the web app's start screen the browser zooms the page.\n if (matchChromeKey(event))\n return !isTextField(event.target) && this.shell?.isEmptyStateShown?.() !== true;\n if (this.shell?.host === \"ide\" && isUndoRedoKey(event)) return false;\n return undefined;\n };\n\n private subscribe<E>(name: string, listener: (event: E) => unknown, priority?: number): void {\n const handler = listener as DiagramEventListener;\n if (priority === undefined) this.eventBus.on(name, handler);\n else this.eventBus.on(name, priority, handler);\n this.subscriptions.push([name, handler]);\n }\n\n private destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.detachShell();\n this.skin?.destroy();\n this.skin = undefined;\n this.svg = undefined;\n for (const [name, listener] of this.subscriptions.splice(0)) this.eventBus.off(name, listener);\n }\n}\n\n/** The service of `shellModule` in `diagram`; throws when the modeler was built without it. */\nexport function shellServiceOf(diagram: InjectorLike): ShellDiagramService {\n let service: unknown;\n try {\n service = diagram.get<unknown>(SHELL_SERVICE);\n } catch {\n service = undefined;\n }\n if (!isShellService(service)) {\n throw new Error(\n \"@miragon/modeler-shell: connectDiagramJs needs shellModule in the modeler's modules (additionalModules: [shellModule])\",\n );\n }\n return service;\n}\n\n// Duck-typed rather than `instanceof`, so a shellModule from the CJS build connects to the ESM one.\nfunction isShellService(value: unknown): value is ShellDiagramService {\n return typeof (value as Partial<ShellDiagramService> | null)?.attachShell === \"function\";\n}\n","import { SHELL_SERVICE, ShellDiagramService } from \"./service.js\";\nimport type { ModuleDeclarationLike } from \"./types.js\";\n\n/**\n * Add to the modeler's modules (`additionalModules`, after the renderer's own modules):\n * - `paletteTooltip: [\"value\", null]` removes diagram-js's (or the renderer's) palette tooltip; the\n * chrome tooltip takes over;\n * - the palette skin: on `palette.create` it sets `data-chrome=\"palette\"` and moves the palette\n * before the canvas `<svg>`; after every `palette.changed` it re-applies `role=\"toolbar\"`,\n * `aria-label`, roving `tabindex` and `aria-pressed` (from `tool-manager.update`);\n * - `keyboard.addListener(2000, …)` returning `true` for every chrome key, so `KeyboardBindings` and\n * the modeler's keyboard modules (priority 1000) never act on them.\n * It buffers the palette until `connectDiagramJs` hands it to a shell, so the diagram may be\n * constructed before `mountShell`.\n */\nexport const shellModule: ModuleDeclarationLike = {\n __init__: [SHELL_SERVICE],\n [SHELL_SERVICE]: [\"type\", ShellDiagramService],\n paletteTooltip: [\"value\", null],\n};\n","import {\n FIT_ANIMATION_MS,\n REDUCED_MOTION_CLASS,\n VIEW_TOLERANCE,\n ZOOM_MAX,\n ZOOM_MIN,\n canZoomIn,\n canZoomOut,\n easeStandard,\n fitViewbox,\n freeRectCenter,\n interpolateViewbox,\n matchChromeKey,\n sameView,\n type Bounds,\n type Insets,\n type Point,\n type ScaledViewbox,\n type Size,\n type ViewportState,\n type ZoomTarget,\n} from \"../core/index.js\";\nimport type {\n CanvasLike,\n DiagramEventListener,\n DiagramViewbox,\n EventBusLike,\n InjectorLike,\n ZoomScrollLike,\n} from \"./types.js\";\n\nexport interface ViewportOptions {\n /**\n * Content bounds in diagram coordinates, tight around the content: the fit insets are the only\n * margin (guide §5.9). `undefined` = nothing to fit (empty file).\n */\n contentBounds(): Bounds | undefined;\n /** Fit insets at the moment of a fit (`shell.fitInsets()`). */\n insets(): Insets;\n /**\n * Default: `matchMedia(\"(prefers-reduced-motion: reduce)\").matches`, or `<html>` carries\n * `REDUCED_MOTION_CLASS` (VS Code's `workbench.reduceMotion`, mirrored by `mountVsCodeShell`).\n */\n prefersReducedMotion?: (() => boolean) | undefined;\n}\n\n/**\n * The view of one diagram (guide §5.8, §5.9): zoom steps through `zoomScroll.stepZoom`, fit with\n * insets and the 100 % cap, automatic refit on container resize until the user moves the view,\n * user-move detection by comparison with the last box the viewport set itself.\n */\nexport interface Viewport extends ZoomTarget {\n /** Explicit fit; animated (250ms) unless `animate: false` or reduced motion. View becomes automatic. */\n fit(options?: { animate?: boolean }): void;\n /**\n * Call after every successful import: fits (jump) the first time the content bounds are\n * non-empty, unless a persisted view was set (`setState`) or the user moved it. When the first\n * import is empty, under the same conditions, it shows 100 % around the centre of the free\n * rectangle instead, whatever zoom the renderer's import left (an empty file opens at 100 %);\n * that view is the viewport's own, not a user move, so the first content still fits. Otherwise\n * a no-op. A `restore` keeps the view of an empty file, so its first content still fits; content\n * the user created on the canvas does not (guide §5.9), so the next import keeps the view.\n */\n afterImport(): void;\n /**\n * The web app opens another document (New diagram, Open…, Import…, Show example, a shared link,\n * a dropped file): the next `afterImport()` counts as the first again (guide §5.9). Forgets the\n * user's moves, a stored view and a pending restore, and stops a running fit animation, so\n * non-empty content fits (jump) and an empty document shows 100 %. Call it before the import.\n */\n newDocument(): void;\n /** Restores a viewbox after a re-import (external change): reference updated, user-move state kept. */\n restore(viewbox: Bounds): void;\n /** The current viewbox (x, y, width, height), `undefined` before the canvas has a size. */\n getViewbox(): Bounds | undefined;\n /**\n * The centre of the free rectangle (the container minus `insets()`, guide §5.9) in diagram\n * coordinates, where keyboard creation from the palette places an element (guide §5.5).\n * `undefined` while the container is 0 × 0 and after `destroy`.\n */\n freeRectCenter(): Point | undefined;\n /** True until the user zoomed or panned; a resize then refits. */\n isAutomatic(): boolean;\n /** For VS Code `setState`; `undefined` before the first import. */\n getState(): ViewportState | undefined;\n /** Restores a persisted view (it wins over the first automatic fit). */\n setState(state: ViewportState): void;\n /** Disconnects observers, listeners and a running animation; also runs on `diagram.destroy`. */\n destroy(): void;\n}\n\n/** A viewport over `canvas` and `zoomScroll` of `diagram`, observing `canvas.getContainer()`. */\nexport function createViewport(diagram: InjectorLike, options: ViewportOptions): Viewport {\n return new DiagramViewport(diagram, options);\n}\n\ntype View = Pick<ScaledViewbox, \"x\" | \"y\" | \"scale\">;\n\n/**\n * Renderers replace the content between these events and may set the viewbox while doing so\n * (Event Storming frames its board); those changes are not the user's.\n */\nconst IMPORT_START_EVENTS = [\"import.parse.start\", \"import.render.start\"];\nconst IMPORT_DONE_EVENT = \"import.done\";\n\n/** Events after which the content bounds may differ (importers add shapes without `elements.changed`). */\nconst CONTENT_EVENTS = [\n \"shape.added\",\n \"shape.removed\",\n \"connection.added\",\n \"connection.removed\",\n \"diagram.clear\",\n];\n\n/** diagram-js `ZoomScroll`: `NUM_STEPS = 10` over `RANGE`, in log space. */\nconst ZOOM_STEP = Math.log10(ZOOM_MAX / ZOOM_MIN) / 10;\n\nconst CAPTURE = { capture: true } as const;\n\nexport class DiagramViewport implements Viewport {\n private readonly canvas: CanvasLike;\n private readonly eventBus: EventBusLike;\n private readonly container: HTMLElement;\n private readonly listeners = new Set<() => void>();\n private readonly subscriptions: Array<readonly [string | string[], DiagramEventListener]> = [];\n private readonly observer: ResizeObserver | undefined;\n private zoomScrollService: ZoomScrollLike | null | undefined;\n\n /** The last view the viewport set as its own (fit target, restore); user moves differ from it. */\n private reference: View | undefined;\n /** The last view written, animation frames included. */\n private lastWritten: View | undefined;\n /** The container size the current view was set or last resized for. */\n private lastSize: Size | undefined;\n private writing = 0;\n private userMoved = false;\n private importing = false;\n private imported = false;\n private firstFitPending = true;\n /** Nothing has decided the view yet, so an empty first import shows 100 %. */\n private emptyViewPending = true;\n /** The 100 % of an empty first import waits for the container to get a size (hidden tab). */\n private emptyViewDue = false;\n /** `canFit()` of the current content; the zoom panel asks every frame, `contentBounds` may be costly. */\n private fittable: boolean | undefined;\n private lastZoom = 1;\n /** An automatic fit is due but the container had no size yet (hidden tab). */\n private autoFitDue = false;\n /** A restored view waits for the container to get a size. */\n private pendingView: Bounds | undefined;\n private animation: { frame: number } | undefined;\n private resizeFrame: number | undefined;\n private destroyed = false;\n\n constructor(\n private readonly diagram: InjectorLike,\n private readonly options: ViewportOptions,\n ) {\n this.canvas = diagram.get<CanvasLike>(\"canvas\");\n this.eventBus = diagram.get<EventBusLike>(\"eventBus\");\n this.container = this.canvas.getContainer();\n\n this.subscribe(\"canvas.viewbox.changing\", this.notify);\n this.subscribe(\"canvas.viewbox.changed\", this.onViewboxChanged);\n this.subscribe(IMPORT_START_EVENTS, this.onImportStart);\n this.subscribe(IMPORT_DONE_EVENT, this.onImportDone);\n this.subscribe(\"elements.changed\", this.onElementsChanged);\n this.subscribe(CONTENT_EVENTS, this.onContentChanged);\n this.subscribe(\"diagram.destroy\", () => this.destroy());\n\n this.container.addEventListener(\"wheel\", this.onPointerInput, { capture: true, passive: true });\n this.container.addEventListener(\"pointerdown\", this.onPointerInput, CAPTURE);\n this.container.addEventListener(\"keydown\", this.onKeyInput, CAPTURE);\n\n if (typeof ResizeObserver === \"function\") {\n this.observer = new ResizeObserver(this.onResizeObserved);\n this.observer.observe(this.container);\n }\n }\n\n zoom(): number {\n if (!this.destroyed) this.lastZoom = this.canvas.zoom();\n return this.lastZoom;\n }\n\n stepZoom(direction: 1 | -1): void {\n if (this.destroyed) return;\n this.beforeUserChange();\n // Below 20 % ZoomScroll caps a step out to 20 %, which zooms in (guide §5.8).\n const zoom = this.canvas.zoom();\n if (direction < 0 ? !canZoomOut(zoom) : !canZoomIn(zoom)) return;\n const zoomScroll = this.zoomScroll();\n if (zoomScroll) zoomScroll.stepZoom(direction);\n else this.canvas.zoom(stepOnRaster(zoom, direction));\n }\n\n zoomToActual(): void {\n if (this.destroyed) return;\n this.beforeUserChange();\n this.canvas.zoom(1);\n }\n\n fit(options?: { animate?: boolean }): void {\n if (this.destroyed) return;\n this.importing = false;\n this.fitTo(options?.animate !== false && !this.prefersReducedMotion());\n }\n\n canFit(): boolean {\n if (this.destroyed) return false;\n this.fittable ??= hasExtent(this.options.contentBounds());\n return this.fittable;\n }\n\n onChange(listener: () => void): () => void {\n this.listeners.add(listener);\n return () => {\n this.listeners.delete(listener);\n };\n }\n\n afterImport(): void {\n if (this.destroyed) return;\n this.importing = false;\n this.imported = true;\n this.fittable = undefined;\n if (this.firstFitPending && this.canFit()) {\n this.firstFitPending = false;\n this.emptyViewPending = false;\n this.emptyViewDue = false;\n if (!this.userMoved && !this.fitTo(false)) this.autoFitDue = true;\n } else if (this.firstFitPending && this.emptyViewPending) {\n this.emptyViewPending = false;\n if (!this.userMoved && !this.showActualSize()) this.emptyViewDue = true;\n }\n this.notify();\n }\n\n newDocument(): void {\n if (this.destroyed) return;\n this.cancelAnimation();\n this.userMoved = false;\n this.firstFitPending = true;\n this.emptyViewPending = true;\n this.emptyViewDue = false;\n this.autoFitDue = false;\n this.pendingView = undefined;\n this.fittable = undefined;\n }\n\n restore(viewbox: Bounds): void {\n if (this.destroyed) return;\n this.importing = false;\n this.imported = true;\n this.fittable = undefined;\n this.emptyViewPending = false;\n this.emptyViewDue = false;\n this.cancelAnimation();\n this.show(viewbox);\n this.notify();\n }\n\n freeRectCenter(): Point | undefined {\n if (this.destroyed) return undefined;\n const centre = freeRectCenter(this.canvas.getSize(), this.options.insets());\n const view = this.currentView();\n if (!centre || !view) return undefined;\n return { x: view.x + centre.x / view.scale, y: view.y + centre.y / view.scale };\n }\n\n getViewbox(): Bounds | undefined {\n if (this.destroyed) return undefined;\n if (this.pendingView) return { ...this.pendingView };\n const size = this.canvas.getSize();\n if (!hasSize(size)) return undefined;\n const { x, y, scale } = this.canvas.viewbox();\n // The getter caches `outer` until the next change; the size is read fresh.\n return { x, y, width: size.width / scale, height: size.height / scale };\n }\n\n isAutomatic(): boolean {\n return !this.userMoved;\n }\n\n getState(): ViewportState | undefined {\n if (!this.imported) return undefined;\n const viewbox = this.getViewbox();\n return viewbox ? { viewbox, userMoved: this.userMoved } : undefined;\n }\n\n setState(state: ViewportState): void {\n if (this.destroyed) return;\n this.importing = false;\n this.imported = true;\n this.fittable = undefined;\n this.cancelAnimation();\n this.userMoved = state.userMoved;\n this.autoFitDue = false;\n this.emptyViewPending = false;\n this.emptyViewDue = false;\n if (this.canFit()) this.firstFitPending = false;\n this.show(state.viewbox);\n this.notify();\n }\n\n /** `canvas.resized()`; viewbox changes it causes (keep-selection-visible) are not user moves. */\n resized(): void {\n if (this.destroyed) return;\n this.writing++;\n try {\n this.canvas.resized();\n } finally {\n this.writing--;\n }\n }\n\n destroy(): void {\n if (this.destroyed) return;\n this.destroyed = true;\n this.cancelAnimation();\n if (this.resizeFrame !== undefined) cancelAnimationFrame(this.resizeFrame);\n this.resizeFrame = undefined;\n this.observer?.disconnect();\n this.container.removeEventListener(\"wheel\", this.onPointerInput, CAPTURE);\n this.container.removeEventListener(\"pointerdown\", this.onPointerInput, CAPTURE);\n this.container.removeEventListener(\"keydown\", this.onKeyInput, CAPTURE);\n for (const [events, listener] of this.subscriptions.splice(0)) this.eventBus.off(events, listener);\n this.listeners.clear();\n }\n\n /** Fits now (jump) or animated; false when there is nothing to fit or no container size. */\n private fitTo(animate: boolean): boolean {\n this.cancelAnimation();\n const bounds = this.options.contentBounds();\n if (!bounds) return false;\n const target = fitViewbox(bounds, this.canvas.getSize(), this.options.insets());\n if (!target) return false;\n this.userMoved = false;\n this.firstFitPending = false;\n this.autoFitDue = false;\n this.emptyViewPending = false;\n this.emptyViewDue = false;\n this.pendingView = undefined;\n const from = this.currentView();\n if (animate && from && !sameView(from, target) && typeof requestAnimationFrame === \"function\") {\n this.animate(from, target);\n } else {\n this.write(target);\n }\n return true;\n }\n\n private animate(from: ScaledViewbox, target: ScaledViewbox): void {\n this.reference = toView(target);\n const animation = { frame: 0 };\n const startedAt = performance.now();\n const step = (timestamp: number): void => {\n if (this.animation !== animation) return;\n if (!hasSize(this.canvas.getSize())) {\n // Hidden mid-animation: fit again once the container is back.\n this.cancelAnimation();\n this.autoFitDue = true;\n return;\n }\n const progress = Math.min(1, Math.max(0, (timestamp - startedAt) / FIT_ANIMATION_MS));\n if (progress >= 1) {\n this.animation = undefined;\n this.write(target);\n return;\n }\n this.write(interpolateViewbox(from, target, easeStandard(progress)), false);\n animation.frame = requestAnimationFrame(step);\n };\n this.animation = animation;\n animation.frame = requestAnimationFrame(step);\n }\n\n private cancelAnimation(): void {\n const animation = this.animation;\n if (!animation) return;\n this.animation = undefined;\n cancelAnimationFrame(animation.frame);\n if (this.lastWritten) this.reference = this.lastWritten;\n }\n\n /**\n * 100 % around the centre of the free rectangle, written as the viewport's own view; true when\n * the view is at 100 % afterwards, false while the container has no size.\n */\n private showActualSize(): boolean {\n const size = this.canvas.getSize();\n const centre = freeRectCenter(size, this.options.insets());\n const view = this.currentView();\n if (!centre || !view) return false;\n if (Math.abs(view.scale - 1) <= VIEW_TOLERANCE) return true;\n this.cancelAnimation();\n this.write({\n x: view.x + centre.x / view.scale - centre.x,\n y: view.y + centre.y / view.scale - centre.y,\n width: size.width,\n height: size.height,\n });\n return true;\n }\n\n /** Shows a restored view now, or once the container has a size. */\n private show(viewbox: Bounds): void {\n if (hasSize(this.canvas.getSize())) {\n this.pendingView = undefined;\n this.write(viewbox);\n } else {\n this.pendingView = { x: viewbox.x, y: viewbox.y, width: viewbox.width, height: viewbox.height };\n }\n }\n\n /** Callers make sure the container has a size: diagram-js would set a scale of 0 otherwise. */\n private write(box: Bounds, asReference = true): void {\n const size = this.canvas.getSize();\n // The scale diagram-js's setter derives from the box (`Canvas#viewbox`).\n const view: View = {\n x: box.x,\n y: box.y,\n scale: Math.min(size.width / box.width, size.height / box.height),\n };\n this.writing++;\n try {\n this.canvas.viewbox({ x: box.x, y: box.y, width: box.width, height: box.height });\n } finally {\n this.writing--;\n }\n this.lastWritten = view;\n if (asReference) this.reference = view;\n this.lastSize = { width: size.width, height: size.height };\n }\n\n private currentView(): ScaledViewbox | undefined {\n const size = this.canvas.getSize();\n const { x, y, scale } = this.canvas.viewbox();\n if (!(scale > 0 && Number.isFinite(scale))) return undefined;\n return { x, y, width: size.width / scale, height: size.height / scale, scale };\n }\n\n private beforeUserChange(): void {\n this.importing = false;\n this.cancelAnimation();\n }\n\n private zoomScroll(): ZoomScrollLike | null {\n if (this.zoomScrollService === undefined) {\n try {\n this.zoomScrollService = this.diagram.get<ZoomScrollLike | null>(\"zoomScroll\") ?? null;\n } catch {\n this.zoomScrollService = null;\n }\n }\n return this.zoomScrollService;\n }\n\n private prefersReducedMotion(): boolean {\n if (this.options.prefersReducedMotion) return this.options.prefersReducedMotion();\n if (this.container.ownerDocument.documentElement.classList.contains(REDUCED_MOTION_CLASS)) return true;\n return typeof matchMedia === \"function\" && matchMedia(\"(prefers-reduced-motion: reduce)\").matches;\n }\n\n private isOwnView(current: View): boolean {\n if (this.reference && sameView(current, this.reference)) return true;\n return (\n this.animation !== undefined && this.lastWritten !== undefined && sameView(current, this.lastWritten)\n );\n }\n\n // Not a flag around the own writes alone: with `canvas.deferUpdate` the event of an own change\n // arrives up to 300ms later, so it is recognised by its view.\n private readonly onViewboxChanged = (event?: { viewbox?: DiagramViewbox }): void => {\n if (this.writing === 0 && !this.importing) {\n const current = event?.viewbox ?? this.canvas.viewbox();\n if (!this.isOwnView(current)) {\n this.cancelAnimation();\n this.userMoved = true;\n this.autoFitDue = false;\n this.emptyViewDue = false;\n }\n }\n this.notify();\n };\n\n private readonly onImportStart = (): void => {\n this.importing = true;\n };\n\n private readonly onImportDone = (): void => {\n this.importing = false;\n this.fittable = undefined;\n this.notify();\n };\n\n private readonly onContentChanged = (): void => {\n this.fittable = undefined;\n };\n\n // Guide §5.9: an element the user creates on the canvas never fits. Once such content exists,\n // only imports that keep the view follow, so the first automatic fit is no longer due.\n private readonly onElementsChanged = (): void => {\n this.fittable = undefined;\n if (this.imported && !this.importing && this.firstFitPending && this.canFit()) {\n this.firstFitPending = false;\n }\n this.notify();\n };\n\n // User input also ends an import window a failed import never closed with `import.done`.\n private readonly onPointerInput = (): void => {\n this.importing = false;\n this.cancelAnimation();\n };\n\n private readonly onKeyInput = (event: KeyboardEvent): void => {\n this.importing = false;\n if (matchChromeKey(event)) this.cancelAnimation();\n };\n\n private readonly onResizeObserved = (): void => {\n if (this.destroyed || this.resizeFrame !== undefined) return;\n if (typeof requestAnimationFrame !== \"function\") {\n this.onResize();\n return;\n }\n this.resizeFrame = requestAnimationFrame(() => {\n this.resizeFrame = undefined;\n this.onResize();\n });\n };\n\n private onResize(): void {\n if (this.destroyed) return;\n const size = this.canvas.getSize();\n if (!hasSize(size)) return;\n const changed = this.lastSize?.width !== size.width || this.lastSize.height !== size.height;\n if (!changed && !this.pendingView && !this.autoFitDue && !this.emptyViewDue) return;\n this.lastSize = { width: size.width, height: size.height };\n this.resized();\n const restored = this.pendingView;\n if (restored) {\n this.pendingView = undefined;\n this.write(restored);\n }\n if (this.userMoved) return;\n if (this.emptyViewDue) {\n this.emptyViewDue = false;\n this.showActualSize();\n return;\n }\n // A view restored for this very moment is not refitted; a fit that was due still runs.\n if (this.autoFitDue || (!restored && !this.firstFitPending)) this.fitTo(false);\n }\n\n private subscribe(events: string | string[], listener: (event: never) => unknown): void {\n this.eventBus.on(events, listener);\n this.subscriptions.push([events, listener]);\n }\n\n private readonly notify = (): void => {\n for (const listener of [...this.listeners]) listener();\n };\n}\n\nfunction toView(box: ScaledViewbox): View {\n return { x: box.x, y: box.y, scale: box.scale };\n}\n\nfunction hasSize(size: Size): boolean {\n return size.width > 0 && size.height > 0;\n}\n\nfunction hasExtent(bounds: Bounds | undefined): boolean {\n return bounds !== undefined && (bounds.width > 0 || bounds.height > 0);\n}\n\n/** `ZoomScroll#_zoom` for a diagram without `zoomScroll`: snap to the raster, one step, cap. */\nfunction stepOnRaster(zoom: number, direction: 1 | -1): number {\n const level = Math.round(Math.log10(zoom) / ZOOM_STEP) * ZOOM_STEP + ZOOM_STEP * direction;\n return Math.min(ZOOM_MAX, Math.max(ZOOM_MIN, 10 ** level));\n}\n","import type { Bounds } from \"../core/index.js\";\nimport type { ShellHandle } from \"../dom/index.js\";\nimport { shellServiceOf } from \"./service.js\";\nimport type { DiagramEventListener, EventBusLike, InjectorLike } from \"./types.js\";\nimport { DiagramViewport, type Viewport } from \"./viewport.js\";\n\nexport interface ConnectOptions {\n /**\n * Content bounds in diagram coordinates, tight around the content: the fit insets are the only\n * margin (guide §5.9). `undefined` = nothing to fit (empty file).\n */\n contentBounds(): Bounds | undefined;\n /**\n * An empty file (guide §13.5: no element, only whitespace, comments or a title), which shows the\n * empty-file hint. Default: `contentBounds()` is empty. A notation whose bounds are never empty\n * (Wardley's plot) passes its own test.\n */\n isEmpty?: (() => boolean) | undefined;\n prefersReducedMotion?: (() => boolean) | undefined;\n}\n\nexport interface DiagramJsConnection {\n readonly viewport: Viewport;\n /**\n * Unbinds viewport, palette, keys and the hint sync from the shell; destroys neither the diagram\n * nor the shell. Runs by itself on `diagram.destroy`.\n */\n destroy(): void;\n}\n\n/** After the viewport's own listeners, so its content state is fresh when the hint reads it. */\nconst HINT_SYNC_PRIORITY = 500;\n\n/**\n * Connects a diagram built with `shellModule` to a mounted shell: creates the viewport with\n * `shell.fitInsets`, binds it with `shell.setZoomTarget`, registers the palette\n * (`shell.registerPalette`), keeps the empty-file hint in sync with the diagram (after every\n * `import.done` and `elements.changed`, so the first element created on the canvas hides it) and\n * calls `canvas.resized()`. Throws when `shellModule` is missing.\n */\nexport function connectDiagramJs(\n shell: ShellHandle,\n diagram: InjectorLike,\n options: ConnectOptions,\n): DiagramJsConnection {\n const service = shellServiceOf(diagram);\n const eventBus = diagram.get<EventBusLike>(\"eventBus\");\n const prefersReducedMotion = options.prefersReducedMotion?.bind(options);\n const viewport = new DiagramViewport(diagram, {\n contentBounds: () => options.contentBounds(),\n insets: () => shell.fitInsets(),\n ...(prefersReducedMotion ? { prefersReducedMotion } : {}),\n });\n shell.setZoomTarget(viewport);\n const detachPalette = service.attachShell(shell);\n // The shell has moved the canvas element into its slot; diagram-js caches the old size.\n viewport.resized();\n\n const isEmpty = options.isEmpty ? () => options.isEmpty?.() === true : () => !viewport.canFit();\n const syncHint: DiagramEventListener = () => shell.setEmptyHint(isEmpty());\n eventBus.on([\"import.done\", \"elements.changed\"], HINT_SYNC_PRIORITY, syncHint);\n\n let destroyed = false;\n const destroy = (): void => {\n if (destroyed) return;\n destroyed = true;\n eventBus.off([\"import.done\", \"elements.changed\"], syncHint);\n eventBus.off(\"diagram.destroy\", destroy);\n detachPalette();\n shell.setZoomTarget(null);\n viewport.destroy();\n };\n eventBus.on(\"diagram.destroy\", destroy);\n return { viewport, destroy };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { e as Size, S as ScaledViewbox, a as Bounds, c as Point, Z as ZoomTarget, V as ViewportState, I as Insets } from './toasts-Bp0REqdq.cjs';
|
|
2
|
+
import { a as ShellHandle } from './types-CnRrINRs.cjs';
|
|
3
|
+
import './appearance-XX3JC3pI.cjs';
|
|
4
|
+
import './appearance-D3FUcbu6.cjs';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* diagram-js services, typed structurally: the shell never imports diagram-js (not even its
|
|
8
|
+
* types), so it works with every diagram-js 15.28+ build the modeler bundles.
|
|
9
|
+
*/
|
|
10
|
+
/** Anything with diagram-js's `get`: a `Diagram`, a renderer's `Modeler`, a didi injector. */
|
|
11
|
+
interface InjectorLike {
|
|
12
|
+
get<T>(name: string): T;
|
|
13
|
+
}
|
|
14
|
+
/** A didi module declaration, assignable to didi's `ModuleDeclaration`. */
|
|
15
|
+
interface ModuleDeclarationLike {
|
|
16
|
+
__init__?: string[];
|
|
17
|
+
__depends__?: ModuleDeclarationLike[];
|
|
18
|
+
[name: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
type DiagramEventListener = (event: never) => unknown;
|
|
21
|
+
/** `eventBus` */
|
|
22
|
+
interface EventBusLike {
|
|
23
|
+
on(events: string | string[], priority: number, listener: DiagramEventListener): void;
|
|
24
|
+
on(events: string | string[], listener: DiagramEventListener): void;
|
|
25
|
+
off(events: string | string[], listener?: DiagramEventListener): void;
|
|
26
|
+
}
|
|
27
|
+
/** `canvas.viewbox()` */
|
|
28
|
+
interface DiagramViewbox extends ScaledViewbox {
|
|
29
|
+
inner: Bounds;
|
|
30
|
+
outer: Size;
|
|
31
|
+
}
|
|
32
|
+
/** `canvas` */
|
|
33
|
+
interface CanvasLike {
|
|
34
|
+
getContainer(): HTMLElement;
|
|
35
|
+
getSize(): Size;
|
|
36
|
+
viewbox(): DiagramViewbox;
|
|
37
|
+
viewbox(box: Bounds): void;
|
|
38
|
+
zoom(): number;
|
|
39
|
+
zoom(scale: number, center?: Point): number;
|
|
40
|
+
resized(): void;
|
|
41
|
+
}
|
|
42
|
+
/** `zoomScroll` */
|
|
43
|
+
interface ZoomScrollLike {
|
|
44
|
+
stepZoom(delta: number, position?: Point): void;
|
|
45
|
+
}
|
|
46
|
+
/** `keyboard` (listeners are removed through `eventBus.off("keyboard.keydown", …)`) */
|
|
47
|
+
interface KeyboardLike {
|
|
48
|
+
addListener(priority: number, listener: (context: {
|
|
49
|
+
keyEvent: KeyboardEvent;
|
|
50
|
+
}) => unknown): void;
|
|
51
|
+
}
|
|
52
|
+
/** `palette` */
|
|
53
|
+
interface PaletteLike {
|
|
54
|
+
getEntries(): Record<string, {
|
|
55
|
+
title?: string | undefined;
|
|
56
|
+
shortcut?: string | undefined;
|
|
57
|
+
ariaKeyShortcuts?: string | undefined;
|
|
58
|
+
group?: string | undefined;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Add to the modeler's modules (`additionalModules`, after the renderer's own modules):
|
|
64
|
+
* - `paletteTooltip: ["value", null]` removes diagram-js's (or the renderer's) palette tooltip; the
|
|
65
|
+
* chrome tooltip takes over;
|
|
66
|
+
* - the palette skin: on `palette.create` it sets `data-chrome="palette"` and moves the palette
|
|
67
|
+
* before the canvas `<svg>`; after every `palette.changed` it re-applies `role="toolbar"`,
|
|
68
|
+
* `aria-label`, roving `tabindex` and `aria-pressed` (from `tool-manager.update`);
|
|
69
|
+
* - `keyboard.addListener(2000, …)` returning `true` for every chrome key, so `KeyboardBindings` and
|
|
70
|
+
* the modeler's keyboard modules (priority 1000) never act on them.
|
|
71
|
+
* It buffers the palette until `connectDiagramJs` hands it to a shell, so the diagram may be
|
|
72
|
+
* constructed before `mountShell`.
|
|
73
|
+
*/
|
|
74
|
+
declare const shellModule: ModuleDeclarationLike;
|
|
75
|
+
|
|
76
|
+
interface ViewportOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Content bounds in diagram coordinates, tight around the content: the fit insets are the only
|
|
79
|
+
* margin (guide §5.9). `undefined` = nothing to fit (empty file).
|
|
80
|
+
*/
|
|
81
|
+
contentBounds(): Bounds | undefined;
|
|
82
|
+
/** Fit insets at the moment of a fit (`shell.fitInsets()`). */
|
|
83
|
+
insets(): Insets;
|
|
84
|
+
/**
|
|
85
|
+
* Default: `matchMedia("(prefers-reduced-motion: reduce)").matches`, or `<html>` carries
|
|
86
|
+
* `REDUCED_MOTION_CLASS` (VS Code's `workbench.reduceMotion`, mirrored by `mountVsCodeShell`).
|
|
87
|
+
*/
|
|
88
|
+
prefersReducedMotion?: (() => boolean) | undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The view of one diagram (guide §5.8, §5.9): zoom steps through `zoomScroll.stepZoom`, fit with
|
|
92
|
+
* insets and the 100 % cap, automatic refit on container resize until the user moves the view,
|
|
93
|
+
* user-move detection by comparison with the last box the viewport set itself.
|
|
94
|
+
*/
|
|
95
|
+
interface Viewport extends ZoomTarget {
|
|
96
|
+
/** Explicit fit; animated (250ms) unless `animate: false` or reduced motion. View becomes automatic. */
|
|
97
|
+
fit(options?: {
|
|
98
|
+
animate?: boolean;
|
|
99
|
+
}): void;
|
|
100
|
+
/**
|
|
101
|
+
* Call after every successful import: fits (jump) the first time the content bounds are
|
|
102
|
+
* non-empty, unless a persisted view was set (`setState`) or the user moved it. When the first
|
|
103
|
+
* import is empty, under the same conditions, it shows 100 % around the centre of the free
|
|
104
|
+
* rectangle instead, whatever zoom the renderer's import left (an empty file opens at 100 %);
|
|
105
|
+
* that view is the viewport's own, not a user move, so the first content still fits. Otherwise
|
|
106
|
+
* a no-op. A `restore` keeps the view of an empty file, so its first content still fits; content
|
|
107
|
+
* the user created on the canvas does not (guide §5.9), so the next import keeps the view.
|
|
108
|
+
*/
|
|
109
|
+
afterImport(): void;
|
|
110
|
+
/**
|
|
111
|
+
* The web app opens another document (New diagram, Open…, Import…, Show example, a shared link,
|
|
112
|
+
* a dropped file): the next `afterImport()` counts as the first again (guide §5.9). Forgets the
|
|
113
|
+
* user's moves, a stored view and a pending restore, and stops a running fit animation, so
|
|
114
|
+
* non-empty content fits (jump) and an empty document shows 100 %. Call it before the import.
|
|
115
|
+
*/
|
|
116
|
+
newDocument(): void;
|
|
117
|
+
/** Restores a viewbox after a re-import (external change): reference updated, user-move state kept. */
|
|
118
|
+
restore(viewbox: Bounds): void;
|
|
119
|
+
/** The current viewbox (x, y, width, height), `undefined` before the canvas has a size. */
|
|
120
|
+
getViewbox(): Bounds | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* The centre of the free rectangle (the container minus `insets()`, guide §5.9) in diagram
|
|
123
|
+
* coordinates, where keyboard creation from the palette places an element (guide §5.5).
|
|
124
|
+
* `undefined` while the container is 0 × 0 and after `destroy`.
|
|
125
|
+
*/
|
|
126
|
+
freeRectCenter(): Point | undefined;
|
|
127
|
+
/** True until the user zoomed or panned; a resize then refits. */
|
|
128
|
+
isAutomatic(): boolean;
|
|
129
|
+
/** For VS Code `setState`; `undefined` before the first import. */
|
|
130
|
+
getState(): ViewportState | undefined;
|
|
131
|
+
/** Restores a persisted view (it wins over the first automatic fit). */
|
|
132
|
+
setState(state: ViewportState): void;
|
|
133
|
+
/** Disconnects observers, listeners and a running animation; also runs on `diagram.destroy`. */
|
|
134
|
+
destroy(): void;
|
|
135
|
+
}
|
|
136
|
+
/** A viewport over `canvas` and `zoomScroll` of `diagram`, observing `canvas.getContainer()`. */
|
|
137
|
+
declare function createViewport(diagram: InjectorLike, options: ViewportOptions): Viewport;
|
|
138
|
+
|
|
139
|
+
interface ConnectOptions {
|
|
140
|
+
/**
|
|
141
|
+
* Content bounds in diagram coordinates, tight around the content: the fit insets are the only
|
|
142
|
+
* margin (guide §5.9). `undefined` = nothing to fit (empty file).
|
|
143
|
+
*/
|
|
144
|
+
contentBounds(): Bounds | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* An empty file (guide §13.5: no element, only whitespace, comments or a title), which shows the
|
|
147
|
+
* empty-file hint. Default: `contentBounds()` is empty. A notation whose bounds are never empty
|
|
148
|
+
* (Wardley's plot) passes its own test.
|
|
149
|
+
*/
|
|
150
|
+
isEmpty?: (() => boolean) | undefined;
|
|
151
|
+
prefersReducedMotion?: (() => boolean) | undefined;
|
|
152
|
+
}
|
|
153
|
+
interface DiagramJsConnection {
|
|
154
|
+
readonly viewport: Viewport;
|
|
155
|
+
/**
|
|
156
|
+
* Unbinds viewport, palette, keys and the hint sync from the shell; destroys neither the diagram
|
|
157
|
+
* nor the shell. Runs by itself on `diagram.destroy`.
|
|
158
|
+
*/
|
|
159
|
+
destroy(): void;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Connects a diagram built with `shellModule` to a mounted shell: creates the viewport with
|
|
163
|
+
* `shell.fitInsets`, binds it with `shell.setZoomTarget`, registers the palette
|
|
164
|
+
* (`shell.registerPalette`), keeps the empty-file hint in sync with the diagram (after every
|
|
165
|
+
* `import.done` and `elements.changed`, so the first element created on the canvas hides it) and
|
|
166
|
+
* calls `canvas.resized()`. Throws when `shellModule` is missing.
|
|
167
|
+
*/
|
|
168
|
+
declare function connectDiagramJs(shell: ShellHandle, diagram: InjectorLike, options: ConnectOptions): DiagramJsConnection;
|
|
169
|
+
|
|
170
|
+
export { type CanvasLike, type ConnectOptions, type DiagramEventListener, type DiagramJsConnection, type DiagramViewbox, type EventBusLike, type InjectorLike, type KeyboardLike, type ModuleDeclarationLike, type PaletteLike, type Viewport, type ViewportOptions, type ZoomScrollLike, connectDiagramJs, createViewport, shellModule };
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { e as Size, S as ScaledViewbox, a as Bounds, c as Point, Z as ZoomTarget, V as ViewportState, I as Insets } from './toasts-Bp0REqdq.js';
|
|
2
|
+
import { a as ShellHandle } from './types-PuDZczzy.js';
|
|
3
|
+
import './appearance-BBaM_4bD.js';
|
|
4
|
+
import './appearance-D3FUcbu6.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* diagram-js services, typed structurally: the shell never imports diagram-js (not even its
|
|
8
|
+
* types), so it works with every diagram-js 15.28+ build the modeler bundles.
|
|
9
|
+
*/
|
|
10
|
+
/** Anything with diagram-js's `get`: a `Diagram`, a renderer's `Modeler`, a didi injector. */
|
|
11
|
+
interface InjectorLike {
|
|
12
|
+
get<T>(name: string): T;
|
|
13
|
+
}
|
|
14
|
+
/** A didi module declaration, assignable to didi's `ModuleDeclaration`. */
|
|
15
|
+
interface ModuleDeclarationLike {
|
|
16
|
+
__init__?: string[];
|
|
17
|
+
__depends__?: ModuleDeclarationLike[];
|
|
18
|
+
[name: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
type DiagramEventListener = (event: never) => unknown;
|
|
21
|
+
/** `eventBus` */
|
|
22
|
+
interface EventBusLike {
|
|
23
|
+
on(events: string | string[], priority: number, listener: DiagramEventListener): void;
|
|
24
|
+
on(events: string | string[], listener: DiagramEventListener): void;
|
|
25
|
+
off(events: string | string[], listener?: DiagramEventListener): void;
|
|
26
|
+
}
|
|
27
|
+
/** `canvas.viewbox()` */
|
|
28
|
+
interface DiagramViewbox extends ScaledViewbox {
|
|
29
|
+
inner: Bounds;
|
|
30
|
+
outer: Size;
|
|
31
|
+
}
|
|
32
|
+
/** `canvas` */
|
|
33
|
+
interface CanvasLike {
|
|
34
|
+
getContainer(): HTMLElement;
|
|
35
|
+
getSize(): Size;
|
|
36
|
+
viewbox(): DiagramViewbox;
|
|
37
|
+
viewbox(box: Bounds): void;
|
|
38
|
+
zoom(): number;
|
|
39
|
+
zoom(scale: number, center?: Point): number;
|
|
40
|
+
resized(): void;
|
|
41
|
+
}
|
|
42
|
+
/** `zoomScroll` */
|
|
43
|
+
interface ZoomScrollLike {
|
|
44
|
+
stepZoom(delta: number, position?: Point): void;
|
|
45
|
+
}
|
|
46
|
+
/** `keyboard` (listeners are removed through `eventBus.off("keyboard.keydown", …)`) */
|
|
47
|
+
interface KeyboardLike {
|
|
48
|
+
addListener(priority: number, listener: (context: {
|
|
49
|
+
keyEvent: KeyboardEvent;
|
|
50
|
+
}) => unknown): void;
|
|
51
|
+
}
|
|
52
|
+
/** `palette` */
|
|
53
|
+
interface PaletteLike {
|
|
54
|
+
getEntries(): Record<string, {
|
|
55
|
+
title?: string | undefined;
|
|
56
|
+
shortcut?: string | undefined;
|
|
57
|
+
ariaKeyShortcuts?: string | undefined;
|
|
58
|
+
group?: string | undefined;
|
|
59
|
+
}>;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Add to the modeler's modules (`additionalModules`, after the renderer's own modules):
|
|
64
|
+
* - `paletteTooltip: ["value", null]` removes diagram-js's (or the renderer's) palette tooltip; the
|
|
65
|
+
* chrome tooltip takes over;
|
|
66
|
+
* - the palette skin: on `palette.create` it sets `data-chrome="palette"` and moves the palette
|
|
67
|
+
* before the canvas `<svg>`; after every `palette.changed` it re-applies `role="toolbar"`,
|
|
68
|
+
* `aria-label`, roving `tabindex` and `aria-pressed` (from `tool-manager.update`);
|
|
69
|
+
* - `keyboard.addListener(2000, …)` returning `true` for every chrome key, so `KeyboardBindings` and
|
|
70
|
+
* the modeler's keyboard modules (priority 1000) never act on them.
|
|
71
|
+
* It buffers the palette until `connectDiagramJs` hands it to a shell, so the diagram may be
|
|
72
|
+
* constructed before `mountShell`.
|
|
73
|
+
*/
|
|
74
|
+
declare const shellModule: ModuleDeclarationLike;
|
|
75
|
+
|
|
76
|
+
interface ViewportOptions {
|
|
77
|
+
/**
|
|
78
|
+
* Content bounds in diagram coordinates, tight around the content: the fit insets are the only
|
|
79
|
+
* margin (guide §5.9). `undefined` = nothing to fit (empty file).
|
|
80
|
+
*/
|
|
81
|
+
contentBounds(): Bounds | undefined;
|
|
82
|
+
/** Fit insets at the moment of a fit (`shell.fitInsets()`). */
|
|
83
|
+
insets(): Insets;
|
|
84
|
+
/**
|
|
85
|
+
* Default: `matchMedia("(prefers-reduced-motion: reduce)").matches`, or `<html>` carries
|
|
86
|
+
* `REDUCED_MOTION_CLASS` (VS Code's `workbench.reduceMotion`, mirrored by `mountVsCodeShell`).
|
|
87
|
+
*/
|
|
88
|
+
prefersReducedMotion?: (() => boolean) | undefined;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* The view of one diagram (guide §5.8, §5.9): zoom steps through `zoomScroll.stepZoom`, fit with
|
|
92
|
+
* insets and the 100 % cap, automatic refit on container resize until the user moves the view,
|
|
93
|
+
* user-move detection by comparison with the last box the viewport set itself.
|
|
94
|
+
*/
|
|
95
|
+
interface Viewport extends ZoomTarget {
|
|
96
|
+
/** Explicit fit; animated (250ms) unless `animate: false` or reduced motion. View becomes automatic. */
|
|
97
|
+
fit(options?: {
|
|
98
|
+
animate?: boolean;
|
|
99
|
+
}): void;
|
|
100
|
+
/**
|
|
101
|
+
* Call after every successful import: fits (jump) the first time the content bounds are
|
|
102
|
+
* non-empty, unless a persisted view was set (`setState`) or the user moved it. When the first
|
|
103
|
+
* import is empty, under the same conditions, it shows 100 % around the centre of the free
|
|
104
|
+
* rectangle instead, whatever zoom the renderer's import left (an empty file opens at 100 %);
|
|
105
|
+
* that view is the viewport's own, not a user move, so the first content still fits. Otherwise
|
|
106
|
+
* a no-op. A `restore` keeps the view of an empty file, so its first content still fits; content
|
|
107
|
+
* the user created on the canvas does not (guide §5.9), so the next import keeps the view.
|
|
108
|
+
*/
|
|
109
|
+
afterImport(): void;
|
|
110
|
+
/**
|
|
111
|
+
* The web app opens another document (New diagram, Open…, Import…, Show example, a shared link,
|
|
112
|
+
* a dropped file): the next `afterImport()` counts as the first again (guide §5.9). Forgets the
|
|
113
|
+
* user's moves, a stored view and a pending restore, and stops a running fit animation, so
|
|
114
|
+
* non-empty content fits (jump) and an empty document shows 100 %. Call it before the import.
|
|
115
|
+
*/
|
|
116
|
+
newDocument(): void;
|
|
117
|
+
/** Restores a viewbox after a re-import (external change): reference updated, user-move state kept. */
|
|
118
|
+
restore(viewbox: Bounds): void;
|
|
119
|
+
/** The current viewbox (x, y, width, height), `undefined` before the canvas has a size. */
|
|
120
|
+
getViewbox(): Bounds | undefined;
|
|
121
|
+
/**
|
|
122
|
+
* The centre of the free rectangle (the container minus `insets()`, guide §5.9) in diagram
|
|
123
|
+
* coordinates, where keyboard creation from the palette places an element (guide §5.5).
|
|
124
|
+
* `undefined` while the container is 0 × 0 and after `destroy`.
|
|
125
|
+
*/
|
|
126
|
+
freeRectCenter(): Point | undefined;
|
|
127
|
+
/** True until the user zoomed or panned; a resize then refits. */
|
|
128
|
+
isAutomatic(): boolean;
|
|
129
|
+
/** For VS Code `setState`; `undefined` before the first import. */
|
|
130
|
+
getState(): ViewportState | undefined;
|
|
131
|
+
/** Restores a persisted view (it wins over the first automatic fit). */
|
|
132
|
+
setState(state: ViewportState): void;
|
|
133
|
+
/** Disconnects observers, listeners and a running animation; also runs on `diagram.destroy`. */
|
|
134
|
+
destroy(): void;
|
|
135
|
+
}
|
|
136
|
+
/** A viewport over `canvas` and `zoomScroll` of `diagram`, observing `canvas.getContainer()`. */
|
|
137
|
+
declare function createViewport(diagram: InjectorLike, options: ViewportOptions): Viewport;
|
|
138
|
+
|
|
139
|
+
interface ConnectOptions {
|
|
140
|
+
/**
|
|
141
|
+
* Content bounds in diagram coordinates, tight around the content: the fit insets are the only
|
|
142
|
+
* margin (guide §5.9). `undefined` = nothing to fit (empty file).
|
|
143
|
+
*/
|
|
144
|
+
contentBounds(): Bounds | undefined;
|
|
145
|
+
/**
|
|
146
|
+
* An empty file (guide §13.5: no element, only whitespace, comments or a title), which shows the
|
|
147
|
+
* empty-file hint. Default: `contentBounds()` is empty. A notation whose bounds are never empty
|
|
148
|
+
* (Wardley's plot) passes its own test.
|
|
149
|
+
*/
|
|
150
|
+
isEmpty?: (() => boolean) | undefined;
|
|
151
|
+
prefersReducedMotion?: (() => boolean) | undefined;
|
|
152
|
+
}
|
|
153
|
+
interface DiagramJsConnection {
|
|
154
|
+
readonly viewport: Viewport;
|
|
155
|
+
/**
|
|
156
|
+
* Unbinds viewport, palette, keys and the hint sync from the shell; destroys neither the diagram
|
|
157
|
+
* nor the shell. Runs by itself on `diagram.destroy`.
|
|
158
|
+
*/
|
|
159
|
+
destroy(): void;
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Connects a diagram built with `shellModule` to a mounted shell: creates the viewport with
|
|
163
|
+
* `shell.fitInsets`, binds it with `shell.setZoomTarget`, registers the palette
|
|
164
|
+
* (`shell.registerPalette`), keeps the empty-file hint in sync with the diagram (after every
|
|
165
|
+
* `import.done` and `elements.changed`, so the first element created on the canvas hides it) and
|
|
166
|
+
* calls `canvas.resized()`. Throws when `shellModule` is missing.
|
|
167
|
+
*/
|
|
168
|
+
declare function connectDiagramJs(shell: ShellHandle, diagram: InjectorLike, options: ConnectOptions): DiagramJsConnection;
|
|
169
|
+
|
|
170
|
+
export { type CanvasLike, type ConnectOptions, type DiagramEventListener, type DiagramJsConnection, type DiagramViewbox, type EventBusLike, type InjectorLike, type KeyboardLike, type ModuleDeclarationLike, type PaletteLike, type Viewport, type ViewportOptions, type ZoomScrollLike, connectDiagramJs, createViewport, shellModule };
|