@bpmnkit/canvas 0.0.28 → 0.0.30

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/dist/css.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /** ID used to prevent duplicate style injection. */
2
2
  export declare const STYLE_ID = "bpmnkit-canvas-styles-v1";
3
3
  /** Complete CSS for the BpmnCanvas component, injected once into `<head>`. */
4
- export declare const CANVAS_CSS = "\n.bpmnkit-canvas-host {\n position: relative;\n overflow: hidden;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n touch-action: none;\n background: var(--bpmnkit-bg, #f8f9fa);\n}\n.bpmnkit-canvas-host *,\n.bpmnkit-canvas-host *::before,\n.bpmnkit-canvas-host *::after {\n box-sizing: border-box;\n}\n.bpmnkit-canvas-host > svg {\n display: block;\n width: 100%;\n height: 100%;\n cursor: default;\n}\n.bpmnkit-canvas-host.is-panning > svg {\n cursor: grabbing;\n}\n.bpmnkit-canvas-host > svg:focus {\n outline: none;\n}\n\n/* \u2500\u2500 Shapes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-shape {\n cursor: pointer;\n outline: none;\n}\n.bpmnkit-shape:focus .bpmnkit-shape-body,\n.bpmnkit-shape:focus .bpmnkit-event-body,\n.bpmnkit-shape:focus .bpmnkit-gw-body,\n.bpmnkit-shape:focus .bpmnkit-pool-body,\n.bpmnkit-shape:focus .bpmnkit-lane-body {\n stroke: var(--bpmnkit-focus, #0066cc);\n stroke-width: 2.5;\n}\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-shape-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-event-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-gw-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-pool-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-lane-body {\n stroke: var(--bpmnkit-highlight, #0066cc);\n stroke-width: 2.5;\n}\n\n/* \u2500\u2500 Highlight variants (AI improve preview) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-highlight--changed .bpmnkit-shape-body,\n.bpmnkit-highlight--changed .bpmnkit-event-body,\n.bpmnkit-highlight--changed .bpmnkit-gw-body,\n.bpmnkit-highlight--changed .bpmnkit-pool-body,\n.bpmnkit-highlight--changed .bpmnkit-lane-body {\n stroke: var(--bpmnkit-warn, #d97706);\n stroke-width: 2.5;\n}\n.bpmnkit-highlight--changed .bpmnkit-edge-path {\n stroke: var(--bpmnkit-warn, #d97706);\n stroke-width: 2.5;\n}\n.bpmnkit-highlight--new .bpmnkit-shape-body,\n.bpmnkit-highlight--new .bpmnkit-event-body,\n.bpmnkit-highlight--new .bpmnkit-gw-body,\n.bpmnkit-highlight--new .bpmnkit-pool-body,\n.bpmnkit-highlight--new .bpmnkit-lane-body {\n fill: var(--bpmnkit-success-subtle, rgba(22, 163, 74, 0.12));\n stroke: var(--bpmnkit-success, #16a34a);\n stroke-width: 2.5;\n}\n.bpmnkit-highlight--new .bpmnkit-edge-path {\n stroke: var(--bpmnkit-success, #16a34a);\n stroke-width: 2.5;\n}\n\n.bpmnkit-shape-body,\n.bpmnkit-event-body,\n.bpmnkit-gw-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-end-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 3;\n}\n.bpmnkit-event-inner {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-event-inner-dashed {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 4 2;\n}\n.bpmnkit-eventsubprocess-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 5 3;\n}\n.bpmnkit-callactivity-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 3;\n}\n\n/* \u2500\u2500 Icons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-icon {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n}\n.bpmnkit-icon-solid {\n fill: var(--bpmnkit-shape-stroke, #404040);\n stroke: none;\n pointer-events: none;\n}\n.bpmnkit-gw-marker {\n fill: var(--bpmnkit-shape-stroke, #404040);\n stroke: none;\n pointer-events: none;\n}\n.bpmnkit-gw-marker-stroke {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 2.5;\n stroke-linecap: round;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Pool / Lane \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-pool-body,\n.bpmnkit-lane-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-pool-header,\n.bpmnkit-lane-header {\n fill: var(--bpmnkit-pool-header, rgba(0,0,0,0.04));\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n[data-theme=\"dark\"] .bpmnkit-pool-header,\n[data-theme=\"dark\"] .bpmnkit-lane-header {\n fill: rgba(255,255,255,0.06);\n}\n\n/* \u2500\u2500 Message flow \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-msgflow-path {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 6 3;\n}\n\n/* \u2500\u2500 Edges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-edge {\n cursor: pointer;\n}\n.bpmnkit-edge-path {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-edge-assoc {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 5 3;\n}\n.bpmnkit-edge-default-slash {\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-arrow-fill {\n fill: var(--bpmnkit-flow-stroke, #404040);\n}\n\n/* \u2500\u2500 Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-label {\n fill: var(--bpmnkit-text, #333333);\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 11px;\n text-anchor: middle;\n dominant-baseline: central;\n pointer-events: none;\n /* White halo behind text so labels never visually merge with edge lines */\n paint-order: stroke;\n stroke: var(--bpmnkit-bg, #f8f9fa);\n stroke-width: 4px;\n stroke-linejoin: round;\n}\n\n/* \u2500\u2500 Light theme (default) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-canvas-host {\n --bpmnkit-bg: #f8f9fa;\n --bpmnkit-grid: rgba(0, 0, 0, 0.14);\n --bpmnkit-shape-fill: #ffffff;\n --bpmnkit-shape-stroke: #404040;\n --bpmnkit-flow-stroke: #404040;\n --bpmnkit-text: var(--bpmnkit-fg, #333333);\n --bpmnkit-highlight: var(--bpmnkit-accent, #1a56db);\n --bpmnkit-focus: #0066cc;\n --bpmnkit-overlay-bg: rgba(248, 249, 250, 0.92);\n --bpmnkit-overlay-border: rgba(0, 0, 0, 0.12);\n}\n\n/* \u2500\u2500 Dark theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-canvas-host[data-theme=\"dark\"] {\n --bpmnkit-bg: #0d0d16;\n --bpmnkit-grid: rgba(255, 255, 255, 0.07);\n --bpmnkit-shape-fill: #1e1e2e;\n --bpmnkit-shape-stroke: #8888bb;\n --bpmnkit-flow-stroke: #7777aa;\n --bpmnkit-text: var(--bpmnkit-fg, #cdd6f4);\n --bpmnkit-highlight: var(--bpmnkit-accent-bright, #89b4fa);\n --bpmnkit-focus: #89b4fa;\n --bpmnkit-overlay-bg: rgba(30, 30, 46, 0.92);\n --bpmnkit-overlay-border: rgba(255, 255, 255, 0.1);\n}\n.bpmnkit-canvas-host[data-theme=\"dark\"] .bpmnkit-edge-hover-dot { fill: var(--bpmnkit-accent, #6b9df7); }\n.bpmnkit-canvas-host[data-theme=\"dark\"] .bpmnkit-edge-waypoint-ball { fill: var(--bpmnkit-accent, #6b9df7); }\n\n/* \u2500\u2500 Neon theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-canvas-host[data-theme=\"neon\"] {\n --bpmnkit-bg: oklch(5% 0.025 270);\n --bpmnkit-grid: oklch(65% 0.28 280 / 0.22);\n --bpmnkit-shape-fill: oklch(7% 0.03 270 / 0.9);\n --bpmnkit-shape-stroke: oklch(65% 0.28 280);\n --bpmnkit-flow-stroke: oklch(72% 0.18 185);\n --bpmnkit-text: oklch(88% 0.02 270);\n --bpmnkit-highlight: oklch(72% 0.18 185);\n --bpmnkit-focus: oklch(65% 0.28 280);\n --bpmnkit-overlay-bg: oklch(8% 0.03 270 / 0.96);\n --bpmnkit-overlay-border: oklch(65% 0.28 280 / 0.25);\n}\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-pool-header,\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-lane-header {\n fill: oklch(65% 0.28 280 / 0.07);\n}\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-edge-hover-dot { fill: oklch(72% 0.18 185); }\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-edge-waypoint-ball { fill: oklch(72% 0.18 185); }\n";
4
+ export declare const CANVAS_CSS = "\n.bpmnkit-canvas-host {\n position: relative;\n overflow: hidden;\n width: 100%;\n height: 100%;\n box-sizing: border-box;\n -webkit-user-select: none;\n user-select: none;\n touch-action: none;\n background: var(--bpmnkit-bg, #f8f9fa);\n}\n.bpmnkit-canvas-host *,\n.bpmnkit-canvas-host *::before,\n.bpmnkit-canvas-host *::after {\n box-sizing: border-box;\n}\n.bpmnkit-canvas-host > svg {\n display: block;\n width: 100%;\n height: 100%;\n cursor: default;\n}\n.bpmnkit-canvas-host.is-panning > svg {\n cursor: grabbing;\n}\n.bpmnkit-canvas-host > svg:focus {\n outline: none;\n}\n\n/* \u2500\u2500 Drill-down breadcrumb \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-breadcrumb {\n position: absolute;\n top: 8px;\n left: 8px;\n z-index: 2;\n display: flex;\n align-items: center;\n gap: 2px;\n padding: 4px 8px;\n font-family: var(--bpmnkit-font, system-ui, -apple-system, sans-serif);\n font-size: 12px;\n color: var(--bpmnkit-text, #333);\n background: var(--bpmnkit-overlay-bg, rgba(248, 249, 250, 0.92));\n border: 1px solid var(--bpmnkit-overlay-border, rgba(0, 0, 0, 0.12));\n border-radius: 6px;\n -webkit-user-select: none;\n user-select: none;\n}\n.bpmnkit-breadcrumb-crumb {\n cursor: pointer;\n padding: 1px 4px;\n border-radius: 4px;\n background: none;\n border: none;\n color: inherit;\n font: inherit;\n}\n.bpmnkit-breadcrumb-crumb:hover {\n background: var(--bpmnkit-accent-subtle, rgba(26, 86, 219, 0.12));\n color: var(--bpmnkit-highlight, #1a56db);\n}\n.bpmnkit-breadcrumb-crumb:last-child {\n cursor: default;\n font-weight: 600;\n}\n.bpmnkit-breadcrumb-sep {\n opacity: 0.5;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Shapes \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-shape {\n cursor: pointer;\n outline: none;\n}\n.bpmnkit-shape:focus .bpmnkit-shape-body,\n.bpmnkit-shape:focus .bpmnkit-event-body,\n.bpmnkit-shape:focus .bpmnkit-gw-body,\n.bpmnkit-shape:focus .bpmnkit-pool-body,\n.bpmnkit-shape:focus .bpmnkit-lane-body {\n stroke: var(--bpmnkit-focus, #0066cc);\n stroke-width: 2.5;\n}\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-shape-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-event-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-gw-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-pool-body,\n.bpmnkit-shape.bpmnkit-selected .bpmnkit-lane-body {\n stroke: var(--bpmnkit-highlight, #0066cc);\n stroke-width: 2.5;\n}\n\n/* \u2500\u2500 Highlight variants (AI improve preview) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-highlight--changed .bpmnkit-shape-body,\n.bpmnkit-highlight--changed .bpmnkit-event-body,\n.bpmnkit-highlight--changed .bpmnkit-gw-body,\n.bpmnkit-highlight--changed .bpmnkit-pool-body,\n.bpmnkit-highlight--changed .bpmnkit-lane-body {\n stroke: var(--bpmnkit-warn, #d97706);\n stroke-width: 2.5;\n}\n.bpmnkit-highlight--changed .bpmnkit-edge-path {\n stroke: var(--bpmnkit-warn, #d97706);\n stroke-width: 2.5;\n}\n.bpmnkit-highlight--new .bpmnkit-shape-body,\n.bpmnkit-highlight--new .bpmnkit-event-body,\n.bpmnkit-highlight--new .bpmnkit-gw-body,\n.bpmnkit-highlight--new .bpmnkit-pool-body,\n.bpmnkit-highlight--new .bpmnkit-lane-body {\n fill: var(--bpmnkit-success-subtle, rgba(22, 163, 74, 0.12));\n stroke: var(--bpmnkit-success, #16a34a);\n stroke-width: 2.5;\n}\n.bpmnkit-highlight--new .bpmnkit-edge-path {\n stroke: var(--bpmnkit-success, #16a34a);\n stroke-width: 2.5;\n}\n\n.bpmnkit-shape-body,\n.bpmnkit-event-body,\n.bpmnkit-gw-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-end-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 3;\n}\n.bpmnkit-event-inner {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-event-inner-dashed {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 4 2;\n}\n.bpmnkit-eventsubprocess-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 5 3;\n}\n.bpmnkit-callactivity-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 3;\n}\n\n/* \u2500\u2500 Data objects / stores / groups \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-data-body,\n.bpmnkit-datastore-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-group-body {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 8 4 1 4;\n}\n\n/* \u2500\u2500 Icons \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-icon {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n}\n.bpmnkit-icon-solid {\n fill: var(--bpmnkit-shape-stroke, #404040);\n stroke: none;\n pointer-events: none;\n}\n.bpmnkit-gw-marker {\n fill: var(--bpmnkit-shape-stroke, #404040);\n stroke: none;\n pointer-events: none;\n}\n.bpmnkit-gw-marker-stroke {\n fill: none;\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 2.5;\n stroke-linecap: round;\n pointer-events: none;\n}\n\n/* \u2500\u2500 Drill-down button (collapsed sub-process) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-drilldown {\n cursor: pointer;\n pointer-events: all;\n}\n.bpmnkit-drilldown-box {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-drilldown:hover .bpmnkit-drilldown-box {\n fill: var(--bpmnkit-accent-subtle, rgba(26, 86, 219, 0.12));\n stroke: var(--bpmnkit-highlight, #1a56db);\n}\n\n/* \u2500\u2500 Pool / Lane \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-pool-body,\n.bpmnkit-lane-body {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-pool-header,\n.bpmnkit-lane-header {\n fill: var(--bpmnkit-pool-header, rgba(0,0,0,0.04));\n stroke: var(--bpmnkit-shape-stroke, #404040);\n stroke-width: 1.5;\n}\n[data-theme=\"dark\"] .bpmnkit-pool-header,\n[data-theme=\"dark\"] .bpmnkit-lane-header {\n fill: rgba(255,255,255,0.06);\n}\n\n/* \u2500\u2500 Message flow \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-msgflow-path {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 6 3;\n}\n\n/* \u2500\u2500 Edges \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-edge {\n cursor: pointer;\n}\n.bpmnkit-edge-path {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-edge-assoc {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n stroke-dasharray: 5 3;\n}\n.bpmnkit-edge-default-slash {\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n}\n.bpmnkit-arrow-fill {\n fill: var(--bpmnkit-flow-stroke, #404040);\n}\n.bpmnkit-open-arrow {\n fill: none;\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n stroke-linecap: round;\n stroke-linejoin: round;\n}\n.bpmnkit-conditional-marker,\n.bpmnkit-msg-marker {\n fill: var(--bpmnkit-shape-fill, #ffffff);\n stroke: var(--bpmnkit-flow-stroke, #404040);\n stroke-width: 1.5;\n}\n\n/* \u2500\u2500 Labels \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-label {\n fill: var(--bpmnkit-text, #333333);\n font-family: system-ui, -apple-system, sans-serif;\n font-size: 11px;\n text-anchor: middle;\n dominant-baseline: central;\n pointer-events: none;\n /* White halo behind text so labels never visually merge with edge lines */\n paint-order: stroke;\n stroke: var(--bpmnkit-bg, #f8f9fa);\n stroke-width: 4px;\n stroke-linejoin: round;\n}\n\n/* \u2500\u2500 Light theme (default) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-canvas-host {\n --bpmnkit-bg: #f8f9fa;\n --bpmnkit-grid: rgba(0, 0, 0, 0.14);\n --bpmnkit-shape-fill: #ffffff;\n --bpmnkit-shape-stroke: #404040;\n --bpmnkit-flow-stroke: #404040;\n --bpmnkit-text: var(--bpmnkit-fg, #333333);\n --bpmnkit-highlight: var(--bpmnkit-accent, #1a56db);\n --bpmnkit-focus: #0066cc;\n --bpmnkit-overlay-bg: rgba(248, 249, 250, 0.92);\n --bpmnkit-overlay-border: rgba(0, 0, 0, 0.12);\n}\n\n/* \u2500\u2500 Dark theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-canvas-host[data-theme=\"dark\"] {\n --bpmnkit-bg: #0d0d16;\n --bpmnkit-grid: rgba(255, 255, 255, 0.07);\n --bpmnkit-shape-fill: #1e1e2e;\n --bpmnkit-shape-stroke: #8888bb;\n --bpmnkit-flow-stroke: #7777aa;\n --bpmnkit-text: var(--bpmnkit-fg, #cdd6f4);\n --bpmnkit-highlight: var(--bpmnkit-accent-bright, #89b4fa);\n --bpmnkit-focus: #89b4fa;\n --bpmnkit-overlay-bg: rgba(30, 30, 46, 0.92);\n --bpmnkit-overlay-border: rgba(255, 255, 255, 0.1);\n}\n.bpmnkit-canvas-host[data-theme=\"dark\"] .bpmnkit-edge-hover-dot { fill: var(--bpmnkit-accent, #6b9df7); }\n.bpmnkit-canvas-host[data-theme=\"dark\"] .bpmnkit-edge-waypoint-ball { fill: var(--bpmnkit-accent, #6b9df7); }\n\n/* \u2500\u2500 Neon theme \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500 */\n.bpmnkit-canvas-host[data-theme=\"neon\"] {\n --bpmnkit-bg: oklch(5% 0.025 270);\n --bpmnkit-grid: oklch(65% 0.28 280 / 0.22);\n --bpmnkit-shape-fill: oklch(7% 0.03 270 / 0.9);\n --bpmnkit-shape-stroke: oklch(65% 0.28 280);\n --bpmnkit-flow-stroke: oklch(72% 0.18 185);\n --bpmnkit-text: oklch(88% 0.02 270);\n --bpmnkit-highlight: oklch(72% 0.18 185);\n --bpmnkit-focus: oklch(65% 0.28 280);\n --bpmnkit-overlay-bg: oklch(8% 0.03 270 / 0.96);\n --bpmnkit-overlay-border: oklch(65% 0.28 280 / 0.25);\n}\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-pool-header,\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-lane-header {\n fill: oklch(65% 0.28 280 / 0.07);\n}\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-edge-hover-dot { fill: oklch(72% 0.18 185); }\n.bpmnkit-canvas-host[data-theme=\"neon\"] .bpmnkit-edge-waypoint-ball { fill: oklch(72% 0.18 185); }\n";
5
5
  /**
6
6
  * Injects the canvas stylesheet into `<head>` if not already present.
7
7
  * Safe to call multiple times — only one `<style>` tag is ever inserted.
package/dist/css.js CHANGED
@@ -31,6 +31,47 @@ export const CANVAS_CSS = `
31
31
  outline: none;
32
32
  }
33
33
 
34
+ /* ── Drill-down breadcrumb ───────────────────────────────────────── */
35
+ .bpmnkit-breadcrumb {
36
+ position: absolute;
37
+ top: 8px;
38
+ left: 8px;
39
+ z-index: 2;
40
+ display: flex;
41
+ align-items: center;
42
+ gap: 2px;
43
+ padding: 4px 8px;
44
+ font-family: var(--bpmnkit-font, system-ui, -apple-system, sans-serif);
45
+ font-size: 12px;
46
+ color: var(--bpmnkit-text, #333);
47
+ background: var(--bpmnkit-overlay-bg, rgba(248, 249, 250, 0.92));
48
+ border: 1px solid var(--bpmnkit-overlay-border, rgba(0, 0, 0, 0.12));
49
+ border-radius: 6px;
50
+ -webkit-user-select: none;
51
+ user-select: none;
52
+ }
53
+ .bpmnkit-breadcrumb-crumb {
54
+ cursor: pointer;
55
+ padding: 1px 4px;
56
+ border-radius: 4px;
57
+ background: none;
58
+ border: none;
59
+ color: inherit;
60
+ font: inherit;
61
+ }
62
+ .bpmnkit-breadcrumb-crumb:hover {
63
+ background: var(--bpmnkit-accent-subtle, rgba(26, 86, 219, 0.12));
64
+ color: var(--bpmnkit-highlight, #1a56db);
65
+ }
66
+ .bpmnkit-breadcrumb-crumb:last-child {
67
+ cursor: default;
68
+ font-weight: 600;
69
+ }
70
+ .bpmnkit-breadcrumb-sep {
71
+ opacity: 0.5;
72
+ pointer-events: none;
73
+ }
74
+
34
75
  /* ── Shapes ─────────────────────────────────────────────────────── */
35
76
  .bpmnkit-shape {
36
77
  cursor: pointer;
@@ -115,6 +156,20 @@ export const CANVAS_CSS = `
115
156
  stroke-width: 3;
116
157
  }
117
158
 
159
+ /* ── Data objects / stores / groups ──────────────────────────────── */
160
+ .bpmnkit-data-body,
161
+ .bpmnkit-datastore-body {
162
+ fill: var(--bpmnkit-shape-fill, #ffffff);
163
+ stroke: var(--bpmnkit-shape-stroke, #404040);
164
+ stroke-width: 1.5;
165
+ }
166
+ .bpmnkit-group-body {
167
+ fill: none;
168
+ stroke: var(--bpmnkit-shape-stroke, #404040);
169
+ stroke-width: 1.5;
170
+ stroke-dasharray: 8 4 1 4;
171
+ }
172
+
118
173
  /* ── Icons ───────────────────────────────────────────────────────── */
119
174
  .bpmnkit-icon {
120
175
  fill: none;
@@ -142,6 +197,21 @@ export const CANVAS_CSS = `
142
197
  pointer-events: none;
143
198
  }
144
199
 
200
+ /* ── Drill-down button (collapsed sub-process) ───────────────────── */
201
+ .bpmnkit-drilldown {
202
+ cursor: pointer;
203
+ pointer-events: all;
204
+ }
205
+ .bpmnkit-drilldown-box {
206
+ fill: var(--bpmnkit-shape-fill, #ffffff);
207
+ stroke: var(--bpmnkit-shape-stroke, #404040);
208
+ stroke-width: 1.5;
209
+ }
210
+ .bpmnkit-drilldown:hover .bpmnkit-drilldown-box {
211
+ fill: var(--bpmnkit-accent-subtle, rgba(26, 86, 219, 0.12));
212
+ stroke: var(--bpmnkit-highlight, #1a56db);
213
+ }
214
+
145
215
  /* ── Pool / Lane ─────────────────────────────────────────────────── */
146
216
  .bpmnkit-pool-body,
147
217
  .bpmnkit-lane-body {
@@ -190,6 +260,19 @@ export const CANVAS_CSS = `
190
260
  .bpmnkit-arrow-fill {
191
261
  fill: var(--bpmnkit-flow-stroke, #404040);
192
262
  }
263
+ .bpmnkit-open-arrow {
264
+ fill: none;
265
+ stroke: var(--bpmnkit-flow-stroke, #404040);
266
+ stroke-width: 1.5;
267
+ stroke-linecap: round;
268
+ stroke-linejoin: round;
269
+ }
270
+ .bpmnkit-conditional-marker,
271
+ .bpmnkit-msg-marker {
272
+ fill: var(--bpmnkit-shape-fill, #ffffff);
273
+ stroke: var(--bpmnkit-flow-stroke, #404040);
274
+ stroke-width: 1.5;
275
+ }
193
276
 
194
277
  /* ── Labels ─────────────────────────────────────────────────────── */
195
278
  .bpmnkit-label {
package/dist/index.d.ts CHANGED
@@ -64,9 +64,14 @@
64
64
  * @packageDocumentation
65
65
  */
66
66
  export { BpmnCanvas } from "./canvas.js";
67
- export type { CanvasApi, CanvasEvents, CanvasOptions, CanvasPlugin, FitMode, RenderedEdge, RenderedShape, Theme, ViewportState, } from "./types.js";
67
+ export { Scene } from "./scene.js";
68
+ export type { SceneLayers } from "./scene.js";
69
+ export { OverlayManager } from "./overlays.js";
70
+ export type { Overlay, OverlayFilter, OverlayHost, OverlayOptions, OverlayPosition, } from "./overlays.js";
71
+ export type { CanvasApi, CanvasEvents, CanvasOptions, CanvasPlugin, FitMode, ImportWarnings, PlaneInfo, RenderedEdge, RenderedShape, ScreenBox, Theme, Viewbox, ViewportState, } from "./types.js";
68
72
  export { ViewportController } from "./viewport.js";
69
- export { render, computeDiagramBounds, createDefs, createGrid } from "./renderer.js";
73
+ export { render, computeDiagramBounds, createDefs, createGrid, buildRenderContext, renderEdgeGroup, renderShapeGroup, } from "./renderer.js";
74
+ export type { RenderContext, RenderedShapeGroup, ShapeLayer } from "./renderer.js";
70
75
  export { KeyboardHandler } from "./keyboard.js";
71
76
  export { injectStyles, CANVAS_CSS, STYLE_ID } from "./css.js";
72
77
  //# sourceMappingURL=index.d.ts.map
package/dist/index.js CHANGED
@@ -64,9 +64,11 @@
64
64
  * @packageDocumentation
65
65
  */
66
66
  export { BpmnCanvas } from "./canvas.js";
67
+ export { Scene } from "./scene.js";
68
+ export { OverlayManager } from "./overlays.js";
67
69
  // Internal exports for use by @bpmnkit/editor
68
70
  export { ViewportController } from "./viewport.js";
69
- export { render, computeDiagramBounds, createDefs, createGrid } from "./renderer.js";
71
+ export { render, computeDiagramBounds, createDefs, createGrid, buildRenderContext, renderEdgeGroup, renderShapeGroup, } from "./renderer.js";
70
72
  export { KeyboardHandler } from "./keyboard.js";
71
73
  export { injectStyles, CANVAS_CSS, STYLE_ID } from "./css.js";
72
74
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,8 @@
1
+ /** Measures the rendered width (px) of `text` at the label font. */
2
+ export declare function measureTextWidth(text: string): number;
3
+ /**
4
+ * Splits `text` into lines that fit within `maxPx`, measuring each candidate.
5
+ * A word wider than `maxPx` is broken mid-word with a hyphen (bpmn-js parity).
6
+ */
7
+ export declare function wrapText(text: string, maxPx: number): string[];
8
+ //# sourceMappingURL=measure.d.ts.map
@@ -0,0 +1,114 @@
1
+ // Real text measurement for label layout, with an SSR/test-safe fallback.
2
+ //
3
+ // In a browser we measure with an offscreen canvas 2D context (accurate and
4
+ // fast, matching what bpmn-js does); where that is unavailable or unreliable
5
+ // (SSR, jsdom/happy-dom) we fall back to an average character-width estimate.
6
+ /** Approximate glyph width at the label font — the no-canvas fallback. */
7
+ const AVG_CHAR_PX = 6.5;
8
+ /** Font used for internal/external labels (mirrors `.bpmnkit-label` in css.ts). */
9
+ const LABEL_FONT = "11px system-ui, -apple-system, sans-serif";
10
+ const estimate = (text) => text.length * AVG_CHAR_PX;
11
+ let _measure = null;
12
+ /** Lazily resolves a measurer once, memoizing per unique string (bounded). */
13
+ function resolveMeasurer() {
14
+ if (_measure)
15
+ return _measure;
16
+ if (typeof document === "undefined") {
17
+ _measure = estimate;
18
+ return _measure;
19
+ }
20
+ let ctx = null;
21
+ try {
22
+ ctx = document.createElement("canvas").getContext("2d");
23
+ }
24
+ catch {
25
+ ctx = null;
26
+ }
27
+ // Fall back when the environment has no working canvas text metrics
28
+ // (happy-dom/jsdom report 0-width) — keeps deterministic label wrapping.
29
+ if (!ctx) {
30
+ _measure = estimate;
31
+ return _measure;
32
+ }
33
+ ctx.font = LABEL_FONT;
34
+ const probe = ctx.measureText("MMMM").width;
35
+ if (!probe || !Number.isFinite(probe)) {
36
+ _measure = estimate;
37
+ return _measure;
38
+ }
39
+ const context = ctx;
40
+ const cache = new Map();
41
+ _measure = (text) => {
42
+ let w = cache.get(text);
43
+ if (w === undefined) {
44
+ w = context.measureText(text).width;
45
+ if (cache.size >= 5000)
46
+ cache.clear();
47
+ cache.set(text, w);
48
+ }
49
+ return w;
50
+ };
51
+ return _measure;
52
+ }
53
+ /** Measures the rendered width (px) of `text` at the label font. */
54
+ export function measureTextWidth(text) {
55
+ return resolveMeasurer()(text);
56
+ }
57
+ /** Breaks a single word wider than `maxPx` into hyphenated chunks. */
58
+ function hyphenate(word, maxPx) {
59
+ const out = [];
60
+ let chunk = "";
61
+ for (const ch of word) {
62
+ const tentative = chunk + ch;
63
+ // Reserve room for a trailing hyphen while a chunk is still growing.
64
+ if (chunk && measureTextWidth(`${tentative}-`) > maxPx) {
65
+ out.push(`${chunk}-`);
66
+ chunk = ch;
67
+ }
68
+ else {
69
+ chunk = tentative;
70
+ }
71
+ }
72
+ if (chunk)
73
+ out.push(chunk);
74
+ return out;
75
+ }
76
+ /**
77
+ * Splits `text` into lines that fit within `maxPx`, measuring each candidate.
78
+ * A word wider than `maxPx` is broken mid-word with a hyphen (bpmn-js parity).
79
+ */
80
+ export function wrapText(text, maxPx) {
81
+ if (!text.trim())
82
+ return [];
83
+ const words = text.split(/\s+/);
84
+ const lines = [];
85
+ let line = "";
86
+ for (const word of words) {
87
+ const candidate = line ? `${line} ${word}` : word;
88
+ if (measureTextWidth(candidate) <= maxPx) {
89
+ line = candidate;
90
+ continue;
91
+ }
92
+ // The candidate overflows: flush the current line first.
93
+ if (line) {
94
+ lines.push(line);
95
+ line = "";
96
+ }
97
+ if (measureTextWidth(word) <= maxPx) {
98
+ line = word;
99
+ }
100
+ else {
101
+ const chunks = hyphenate(word, maxPx);
102
+ for (let i = 0; i < chunks.length - 1; i++) {
103
+ const c = chunks[i];
104
+ if (c)
105
+ lines.push(c);
106
+ }
107
+ line = chunks[chunks.length - 1] ?? "";
108
+ }
109
+ }
110
+ if (line)
111
+ lines.push(line);
112
+ return lines.length > 0 ? lines : [text];
113
+ }
114
+ //# sourceMappingURL=measure.js.map
@@ -0,0 +1,86 @@
1
+ import type { ScreenBox } from "./types.js";
2
+ /** Placement of an overlay relative to its anchor element's bounding box. */
3
+ export interface OverlayPosition {
4
+ /** Distance (px) of the overlay's top edge below the element's top edge. */
5
+ top?: number;
6
+ /** Distance (px) of the overlay's bottom edge above the element's bottom edge. */
7
+ bottom?: number;
8
+ /** Distance (px) of the overlay's left edge right of the element's left edge. */
9
+ left?: number;
10
+ /** Distance (px) of the overlay's right edge left of the element's right edge. */
11
+ right?: number;
12
+ }
13
+ /** Options for {@link OverlayManager.add}. */
14
+ export interface OverlayOptions {
15
+ /** Where to anchor the overlay relative to the element. */
16
+ position: OverlayPosition;
17
+ /** Overlay content — an HTML string or a pre-built element. */
18
+ html: string | HTMLElement;
19
+ /** Zoom range in which the overlay is visible. */
20
+ show?: {
21
+ minZoom?: number;
22
+ maxZoom?: number;
23
+ };
24
+ /**
25
+ * Whether the overlay scales with zoom. `true` (default) scales 1:1 with the
26
+ * diagram; `false` keeps a constant pixel size; an object clamps the scale.
27
+ */
28
+ scale?: boolean | {
29
+ min?: number;
30
+ max?: number;
31
+ };
32
+ /** Optional tag for bulk queries and removal. */
33
+ type?: string;
34
+ }
35
+ /** A registered overlay. */
36
+ export interface Overlay {
37
+ readonly id: string;
38
+ readonly element: string;
39
+ readonly type?: string;
40
+ readonly node: HTMLElement;
41
+ }
42
+ /** Filter for {@link OverlayManager.get} / {@link OverlayManager.remove}. */
43
+ export interface OverlayFilter {
44
+ element?: string;
45
+ type?: string;
46
+ }
47
+ /** The host hooks an {@link OverlayManager} needs from a canvas or editor. */
48
+ export interface OverlayHost {
49
+ /** The (position: relative) host element to mount the overlay layer into. */
50
+ readonly hostEl: HTMLElement;
51
+ /** Current zoom scale. */
52
+ getScale(): number;
53
+ /** Element bounding box in screen pixels relative to the host, or `null`. */
54
+ getBBox(id: string): ScreenBox | null;
55
+ /** Subscribes to viewport changes; returns an unsubscribe function. */
56
+ onViewportChange(cb: () => void): () => void;
57
+ }
58
+ /**
59
+ * Manages HTML overlays anchored to diagram elements. Overlays live in a
60
+ * dedicated absolutely-positioned layer above the SVG and are repositioned on
61
+ * every viewport change from the element's screen bounding box.
62
+ */
63
+ export declare class OverlayManager {
64
+ private readonly _host;
65
+ private readonly _layer;
66
+ private readonly _overlays;
67
+ private readonly _unsub;
68
+ private _counter;
69
+ constructor(_host: OverlayHost);
70
+ /** Adds an overlay anchored to `elementId`. Returns its overlay id. */
71
+ add(elementId: string, opts: OverlayOptions): string;
72
+ /** Removes an overlay by id, or all overlays matching a filter. */
73
+ remove(idOrFilter: string | OverlayFilter): void;
74
+ /** Returns overlays matching a filter (all overlays when omitted). */
75
+ get(filter?: OverlayFilter): Overlay[];
76
+ /** Removes every overlay. */
77
+ clear(): void;
78
+ /** Repositions all overlays from their elements' current bounding boxes. */
79
+ reposition(): void;
80
+ /** Removes the overlay layer and unsubscribes from viewport changes. */
81
+ destroy(): void;
82
+ private _repositionAll;
83
+ private _position;
84
+ private _scaleFactor;
85
+ }
86
+ //# sourceMappingURL=overlays.d.ts.map
@@ -0,0 +1,140 @@
1
+ function clamp(v, min, max) {
2
+ return Math.max(min, Math.min(max, v));
3
+ }
4
+ /**
5
+ * Manages HTML overlays anchored to diagram elements. Overlays live in a
6
+ * dedicated absolutely-positioned layer above the SVG and are repositioned on
7
+ * every viewport change from the element's screen bounding box.
8
+ */
9
+ export class OverlayManager {
10
+ _host;
11
+ _layer;
12
+ _overlays = new Map();
13
+ _unsub;
14
+ _counter = 0;
15
+ constructor(_host) {
16
+ this._host = _host;
17
+ this._layer = document.createElement("div");
18
+ this._layer.className = "bpmnkit-overlays";
19
+ this._layer.style.cssText =
20
+ "position:absolute;top:0;left:0;width:0;height:0;overflow:visible;pointer-events:none;";
21
+ this._host.hostEl.appendChild(this._layer);
22
+ this._unsub = this._host.onViewportChange(() => this._repositionAll());
23
+ }
24
+ /** Adds an overlay anchored to `elementId`. Returns its overlay id. */
25
+ add(elementId, opts) {
26
+ const node = document.createElement("div");
27
+ node.style.position = "absolute";
28
+ node.style.pointerEvents = "auto";
29
+ if (typeof opts.html === "string")
30
+ node.innerHTML = opts.html;
31
+ else
32
+ node.appendChild(opts.html);
33
+ if (opts.type)
34
+ node.dataset.overlayType = opts.type;
35
+ this._layer.appendChild(node);
36
+ const id = String(this._counter++);
37
+ const overlay = { id, element: elementId, type: opts.type, node, opts };
38
+ this._overlays.set(id, overlay);
39
+ this._position(overlay);
40
+ return id;
41
+ }
42
+ /** Removes an overlay by id, or all overlays matching a filter. */
43
+ remove(idOrFilter) {
44
+ if (typeof idOrFilter === "string") {
45
+ const overlay = this._overlays.get(idOrFilter);
46
+ if (overlay) {
47
+ overlay.node.remove();
48
+ this._overlays.delete(idOrFilter);
49
+ }
50
+ return;
51
+ }
52
+ for (const overlay of this.get(idOrFilter)) {
53
+ overlay.node.remove();
54
+ this._overlays.delete(overlay.id);
55
+ }
56
+ }
57
+ /** Returns overlays matching a filter (all overlays when omitted). */
58
+ get(filter) {
59
+ const all = [...this._overlays.values()];
60
+ if (!filter)
61
+ return all;
62
+ return all.filter((o) => (filter.element === undefined || o.element === filter.element) &&
63
+ (filter.type === undefined || o.type === filter.type));
64
+ }
65
+ /** Removes every overlay. */
66
+ clear() {
67
+ for (const overlay of this._overlays.values())
68
+ overlay.node.remove();
69
+ this._overlays.clear();
70
+ }
71
+ /** Repositions all overlays from their elements' current bounding boxes. */
72
+ reposition() {
73
+ this._repositionAll();
74
+ }
75
+ /** Removes the overlay layer and unsubscribes from viewport changes. */
76
+ destroy() {
77
+ this._unsub();
78
+ this.clear();
79
+ this._layer.remove();
80
+ }
81
+ // ── Private ───────────────────────────────────────────────────────
82
+ _repositionAll() {
83
+ for (const overlay of this._overlays.values())
84
+ this._position(overlay);
85
+ }
86
+ _position(overlay) {
87
+ const { node, opts } = overlay;
88
+ const box = this._host.getBBox(overlay.element);
89
+ if (!box) {
90
+ node.style.display = "none";
91
+ return;
92
+ }
93
+ const scale = this._host.getScale();
94
+ const min = opts.show?.minZoom ?? Number.NEGATIVE_INFINITY;
95
+ const max = opts.show?.maxZoom ?? Number.POSITIVE_INFINITY;
96
+ if (scale < min || scale > max) {
97
+ node.style.display = "none";
98
+ return;
99
+ }
100
+ node.style.display = "";
101
+ const { position } = opts;
102
+ const transforms = [];
103
+ let originX = "left";
104
+ let originY = "top";
105
+ let x = box.x;
106
+ let y = box.y;
107
+ if (position.left !== undefined) {
108
+ x = box.x + position.left;
109
+ }
110
+ else if (position.right !== undefined) {
111
+ x = box.x + box.width - position.right;
112
+ transforms.push("translateX(-100%)");
113
+ originX = "right";
114
+ }
115
+ if (position.top !== undefined) {
116
+ y = box.y + position.top;
117
+ }
118
+ else if (position.bottom !== undefined) {
119
+ y = box.y + box.height - position.bottom;
120
+ transforms.push("translateY(-100%)");
121
+ originY = "bottom";
122
+ }
123
+ node.style.left = `${x}px`;
124
+ node.style.top = `${y}px`;
125
+ const scaleFactor = this._scaleFactor(opts.scale, scale);
126
+ if (scaleFactor !== 1)
127
+ transforms.push(`scale(${scaleFactor})`);
128
+ node.style.transformOrigin = `${originX} ${originY}`;
129
+ node.style.transform = transforms.join(" ");
130
+ }
131
+ _scaleFactor(scale, zoom) {
132
+ // Default: scale 1:1 with the diagram.
133
+ if (scale === undefined || scale === true)
134
+ return zoom;
135
+ if (scale === false)
136
+ return 1;
137
+ return clamp(zoom, scale.min ?? 0, scale.max ?? Number.POSITIVE_INFINITY);
138
+ }
139
+ }
140
+ //# sourceMappingURL=overlays.js.map
@@ -1,9 +1,40 @@
1
- import type { BpmnDefinitions } from "@bpmnkit/core";
1
+ import type { BpmnAssociation, BpmnDefinitions, BpmnDiEdge, BpmnDiPlane, BpmnDiShape, BpmnFlowElement, BpmnGroup, BpmnLane, BpmnMessageFlow, BpmnParticipant, BpmnSequenceFlow, BpmnTextAnnotation } from "@bpmnkit/core";
2
2
  import type { RenderedEdge, RenderedShape } from "./types.js";
3
+ /** The outline used to crop a connection to a shape. */
4
+ interface ShapeGeom {
5
+ x: number;
6
+ y: number;
7
+ width: number;
8
+ height: number;
9
+ kind: "circle" | "diamond" | "rect";
10
+ }
11
+ export interface ModelIndex {
12
+ /** id → BpmnFlowElement (all levels flattened) */
13
+ elements: Map<string, BpmnFlowElement>;
14
+ /** id → BpmnSequenceFlow */
15
+ flows: Map<string, BpmnSequenceFlow>;
16
+ /** id → BpmnTextAnnotation */
17
+ annotations: Map<string, BpmnTextAnnotation>;
18
+ /** id → BpmnParticipant */
19
+ participants: Map<string, BpmnParticipant>;
20
+ /** id → BpmnLane */
21
+ lanes: Map<string, BpmnLane>;
22
+ /** id → BpmnAssociation */
23
+ associations: Map<string, BpmnAssociation>;
24
+ /** id → BpmnGroup */
25
+ groups: Map<string, BpmnGroup>;
26
+ /** id → BpmnMessageFlow */
27
+ messageFlows: Map<string, BpmnMessageFlow>;
28
+ /** IDs of sequence flows that are the default flow of their source gateway */
29
+ defaultFlowIds: Set<string>;
30
+ }
3
31
  /**
4
32
  * Creates the SVG `<defs>` section for this canvas instance.
5
33
  * Uses `instanceId` to make marker IDs unique per canvas, avoiding
6
34
  * conflicts when multiple canvases are mounted on the same page.
35
+ *
36
+ * @returns the filled-arrowhead marker ID (sequence-flow terminus). Other
37
+ * marker IDs are derived from `instanceId` via {@link markerIds}.
7
38
  */
8
39
  export declare function createDefs(svg: SVGSVGElement, instanceId: string): string;
9
40
  /**
@@ -18,14 +49,45 @@ export interface RenderResult {
18
49
  edges: RenderedEdge[];
19
50
  }
20
51
  /**
21
- * Renders a `BpmnDefinitions` model into SVG element groups, appending them
22
- * to `edgesLayer` and `shapesLayer` respectively.
52
+ * Shared per-render state — a model index and docking geometry reused by the
53
+ * full {@link render} pass and by single-element updates in the {@link Scene}.
54
+ */
55
+ export interface RenderContext {
56
+ index: ModelIndex;
57
+ geomById: Map<string, ShapeGeom>;
58
+ drillableIds: ReadonlySet<string>;
59
+ instanceId: string;
60
+ }
61
+ /** Builds a {@link RenderContext} for a plane (index + docking geometry). */
62
+ export declare function buildRenderContext(defs: BpmnDefinitions, plane: BpmnDiPlane, drillableIds: ReadonlySet<string>, instanceId: string): RenderContext;
63
+ /** Renders a single edge's `<g>` (no DOM insertion). */
64
+ export declare function renderEdgeGroup(edge: BpmnDiEdge, ctx: RenderContext): SVGGElement;
65
+ /** Which layer a shape's `<g>` belongs to. */
66
+ export type ShapeLayer = "containers" | "shapes";
67
+ /** The result of rendering a single shape: its `<g>`, target layer, and external label. */
68
+ export interface RenderedShapeGroup {
69
+ group: SVGGElement;
70
+ layer: ShapeLayer;
71
+ label: SVGGElement | null;
72
+ rendered: RenderedShape;
73
+ }
74
+ /** Renders a single shape's `<g>` (+ optional external label), without DOM insertion. */
75
+ export declare function renderShapeGroup(shape: BpmnDiShape, ctx: RenderContext): RenderedShapeGroup;
76
+ /**
77
+ * Renders a `BpmnDefinitions` model into SVG element groups, appending them to
78
+ * the provided layers. Thin wrapper over {@link buildRenderContext} +
79
+ * {@link renderEdgeGroup}/{@link renderShapeGroup}, retained for existing
80
+ * callers (the editor). New code should prefer the {@link Scene} class.
23
81
  *
24
82
  * Edges are placed below shapes (rendered first) so connection lines don't
25
- * cover shape bodies. Shapes are rendered in DI order so container shapes
83
+ * cover shape bodies. Shapes are rendered in DI order so container shapes
26
84
  * (sub-processes) appear before their children.
27
85
  */
28
- export declare function render(defs: BpmnDefinitions, containersLayer: SVGGElement, edgesLayer: SVGGElement, shapesLayer: SVGGElement, labelsLayer: SVGGElement, markerId: string, instanceId: string): RenderResult;
86
+ export declare function render(defs: BpmnDefinitions, containersLayer: SVGGElement, edgesLayer: SVGGElement, shapesLayer: SVGGElement, labelsLayer: SVGGElement, _markerId: string, instanceId: string,
87
+ /** The DI plane to render. Defaults to the first diagram's plane. */
88
+ targetPlane?: BpmnDiPlane,
89
+ /** Element ids that own their own plane and can be drilled into. */
90
+ drillableIds?: ReadonlySet<string>): RenderResult;
29
91
  export interface DiagramBounds {
30
92
  minX: number;
31
93
  minY: number;
@@ -33,8 +95,9 @@ export interface DiagramBounds {
33
95
  maxY: number;
34
96
  }
35
97
  /**
36
- * Computes the bounding box of all shapes in the first DI diagram plane.
37
- * Returns `null` if the diagram has no shapes.
98
+ * Computes the bounding box of all shapes in a DI plane (the first diagram's
99
+ * plane by default). Returns `null` if the plane has no shapes.
38
100
  */
39
- export declare function computeDiagramBounds(defs: BpmnDefinitions): DiagramBounds | null;
101
+ export declare function computeDiagramBounds(defs: BpmnDefinitions, targetPlane?: BpmnDiPlane): DiagramBounds | null;
102
+ export {};
40
103
  //# sourceMappingURL=renderer.d.ts.map