@unofficialbox/box-open-elements 0.31.2 → 0.32.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/README.md CHANGED
@@ -12,7 +12,7 @@
12
12
 
13
13
  Browse the full catalog on the **[live docs site](https://unofficialbox.github.io/box-open-elements)** — live previews, an events/properties inspector, per-framework code snippets (React / Angular / Vue / Svelte / HTML), and the foundations pages.
14
14
 
15
- The library is organized around three layers (see [taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/taxonomy.md) for the canonical model):
15
+ The library is organized around three layers (see [taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/taxonomy.md) for the canonical model):
16
16
 
17
17
  - **Foundations** — design decisions as data: tokens, color, typography, iconography, accessibility, theming
18
18
  - **Components** — accessible Web Components for single controls, organized by category
@@ -77,7 +77,7 @@ selection.toggleSelection("1");
77
77
  - optimized component entrypoints: `@unofficialbox/box-open-elements/<name>`
78
78
  - patterns: `@unofficialbox/box-open-elements/patterns/<area>` and `@unofficialbox/box-open-elements/patterns/<area>/<module>`
79
79
 
80
- See the versioned [API guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/api-guidelines.md).
80
+ See the versioned [API guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/api-guidelines.md).
81
81
 
82
82
  ## Developing
83
83
 
@@ -91,17 +91,17 @@ bun run docs # build + serve the docs site at http://localhost:4600
91
91
 
92
92
  ## Documentation
93
93
 
94
- The versioned docs index is at [docs/README.md](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/README.md). The most important entries:
95
-
96
- - [Taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/taxonomy.md)
97
- - [Architecture](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/architecture.md)
98
- - [API Guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/api-guidelines.md)
99
- - [Components catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/components/catalog.md) and [patterns catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/patterns/catalog.md)
100
- - [Using with React, Angular, Vue, and Svelte](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/integration/frameworks.md)
101
- - [Design Tokens](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/foundations/tokens.md)
102
- - [Content Explorer](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/patterns/content-explorer.md)
103
- - [Box Server Integration](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/integration/box-server.md)
104
- - [Agent UI guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/patterns/agent-ui-guidelines.md), [building blocks](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/patterns/agent-ui.md), and [motion](https://github.com/unofficialbox/box-open-elements/blob/v0.31.2/docs/foundations/motion.md)
94
+ The versioned docs index is at [docs/README.md](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/README.md). The most important entries:
95
+
96
+ - [Taxonomy](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/taxonomy.md)
97
+ - [Architecture](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/architecture.md)
98
+ - [API Guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/api-guidelines.md)
99
+ - [Components catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/components/catalog.md) and [patterns catalog](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/patterns/catalog.md)
100
+ - [Using with React, Angular, Vue, and Svelte](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/integration/frameworks.md)
101
+ - [Design Tokens](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/foundations/tokens.md)
102
+ - [Content Explorer](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/patterns/content-explorer.md)
103
+ - [Box Server Integration](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/integration/box-server.md)
104
+ - [Agent UI guidelines](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/patterns/agent-ui-guidelines.md), [building blocks](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/patterns/agent-ui.md), and [motion](https://github.com/unofficialbox/box-open-elements/blob/v0.32.0/docs/foundations/motion.md)
105
105
 
106
106
  ## Contributing
107
107
 
@@ -931,6 +931,7 @@ export interface BoxElementEventMap {
931
931
  positions: readonly import("./patterns/process-modeler/model.js").ProcessPositionSnapshot[];
932
932
  version: string | number;
933
933
  }>;
934
+ "process-copy-request": CustomEvent<import("./patterns/process-modeler/model.js").ProcessCopyRequest>;
934
935
  "process-edit-request": CustomEvent<import("./patterns/process-modeler/model.js").ProcessEditRequest>;
935
936
  "process-field-change-request": CustomEvent<{
936
937
  boxId: string;
@@ -1,2 +1,2 @@
1
1
  /** Riptide Diagram anatomy expressed through public Box Open Elements tokens. */
2
- export declare const processModelerDesign = "\n :host([hidden]), [hidden] { display: none !important; }\n *, *::before, *::after { box-sizing: border-box; }\n button, input, textarea, select { font: inherit; color: inherit; }\n button { min-height: 36px; padding: 8px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); cursor: pointer; }\n button:disabled { opacity: .5; cursor: default; }\n :is(button,input,textarea,select,[part=canvas],[part=minimap],[part=box],[part=frame]):focus-visible { outline: 2px solid var(--boe-token-surface-surface-brand, #0061d5); outline-offset: 2px; }\n :host {\n display: block;\n position: relative;\n isolation: isolate;\n min-width: 0;\n --boe-process-box-edge: color-mix(in srgb, var(--boe-token-text-text, #141413) 55%, var(--boe-token-surface-surface, #fff));\n color: var(--boe-token-text-text, #141413);\n background: var(--boe-token-surface-surface, #fff);\n font: 14px/1.5 var(--boe-token-font-family-base, Inter, sans-serif);\n }\n [part=sr-only], [part=status], [part=urgent-status] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n [part=toolbar] {\n min-height: 56px;\n margin: 0;\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8);\n background: var(--boe-token-surface-surface, #fff);\n }\n [part=toolbar] button, [part=controls] button, [part=selection-toolbar] button {\n min-height: 30px;\n padding: 4px 10px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n font-size: 13px;\n font-weight: 600;\n }\n [part=toolbar] svg, [part=controls] svg { width: 16px; height: 16px; display: block; }\n [part=toolbar] [data-command=undo], [part=toolbar] [data-command=redo], [part=controls] button:not([data-command=reset]) { width: 30px; height: 30px; padding: 6px; display: grid; place-items: center; }\n [part=controls] [data-command=snap][aria-pressed=true], [part=controls] [data-command=lock][aria-pressed=true] { background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 10%, var(--boe-token-surface-surface, #fff)); color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=toolbar] button:hover, [part=controls] button:hover, [part=selection-toolbar] button:hover {\n background: var(--boe-token-surface-surface-hover, #f4f4f4);\n }\n [part=toolbar] [data-command=tidy] { border-color: var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 18px; }\n [part=view-switch] { display: flex; gap: 2px; padding: 3px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 20px; }\n [part=view-switch] button { border-radius: 16px; }\n [part=view-switch] button[aria-pressed=true] { border-color: var(--boe-token-stroke-stroke, #e8e8e8); background: var(--boe-token-surface-surface, #fff); }\n [part=run-toggle] { display: flex; align-items: center; gap: 8px; white-space: nowrap; font-size: 13px; font-weight: 600; cursor: pointer; }\n [part=run-toggle] input { appearance: none; position: relative; width: 32px; height: 18px; margin: 0; border: 0; border-radius: 9px; background: var(--boe-token-surface-surface-hover, #f4f4f4); cursor: pointer; transition: background var(--boe-profile-motion-interactive, 200ms) ease-out; }\n [part=run-toggle] input::after { content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: var(--boe-token-surface-surface, #fff); box-shadow: 0 1px 2px rgb(0 0 0 / .25); transition: transform var(--boe-profile-motion-interactive, 200ms) ease-out; }\n [part=run-toggle] input:checked { background: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=run-toggle] input:checked::after { transform: translateX(14px); }\n [part=run-toggle] input:focus-visible { outline: 2px solid var(--boe-token-surface-surface-brand, #0061d5); outline-offset: 2px; }\n [part=toolbar] [data-command=checks-status] { white-space: nowrap; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=toolbar] [data-command=checks-status][data-state=bad] { color: var(--boe-token-text-status-text-error, #b92340); }\n [part=view-menu] { display: none; position: relative; }\n [part=view-menu] summary { list-style: none; padding: 4px 14px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 18px; font-size: 13px; font-weight: 600; cursor: pointer; }\n [part=view-menu][open] { z-index: 20; }\n [part=view-menu-options] { position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; width: 192px; padding: 6px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 10px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=view-menu] button { display: block; width: 100%; text-align: start; background: transparent; }\n [part=layout] {\n display: grid;\n grid-template-columns: 248px minmax(0, 1fr) 320px;\n gap: 0;\n min-height: 0;\n height: var(--boe-process-height, 660px);\n overflow: hidden;\n }\n [part=pane-drawer] { display: contents; color: inherit; }\n [part=canvas-stack] { display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0; min-height: 0; }\n /* The hidden hold leaves row 1 empty. Keep the canvas in the flexible row\n explicitly; auto-placement otherwise puts its absolute world in a 0px row. */\n [part=canvas-stack] > [part=hold] { grid-row: 1; }\n [part=canvas-stack] > [part=canvas] { grid-row: 2; }\n [part=insert-chooser]::backdrop { background: rgb(0 0 0 / .45); }\n [part=pane-scrim] { position: absolute; inset: 0; z-index: 30; background: rgb(0 0 0 / .45); }\n [part=pane-title], [part=pane-close], [data-command=palette], [data-command=details] { display: none; }\n [part=palette], [part=inspector] {\n min-width: 0;\n overflow: auto;\n padding: 12px;\n border: 0;\n border-radius: 0;\n background: var(--boe-token-surface-surface, #fff);\n }\n [part=palette] { display: flex; flex-direction: column; gap: 12px; border-right: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=palette-heading] { margin: 0 4px; font-size: 14px; font-weight: 650; line-height: 20px; }\n [part=skip-link] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }\n [part=skip-link]:focus-visible { position: static; width: auto; height: auto; clip-path: none; }\n [part=inspector] { border-left: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); padding: 16px 0; }\n [part=palette] label { font-size: 0; position: relative; }\n [part=search-icon] { position: absolute; left: 12px; top: 9px; width: 18px; height: 18px; color: var(--boe-token-text-text-secondary, #6f6f6f); pointer-events: none; }\n [part=palette] input {\n width: 100%; min-width: 0;\n height: 36px;\n padding: 0 12px 0 36px;\n border: 1px solid transparent;\n border-radius: 24px;\n background: var(--boe-token-surface-surface-hover, #f4f4f4);\n font-size: 13px;\n }\n [part=palette] input:focus-visible { border-color: var(--boe-token-surface-surface-brand, #0061d5); background: var(--boe-token-surface-surface, #fff); }\n [part=choices] { display: grid; align-content: start; gap: 4px; max-height: none; overflow: visible; }\n [part=group] { margin: 4px 4px 6px; font-size: 12px; font-weight: 650; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=choice] {\n display: grid; grid-template-columns: 20px minmax(0,1fr); gap: 0 10px;\n min-height: 44px; padding: 8px 10px; border: 1px solid transparent; border-radius: 10px;\n text-align: start; background: transparent; cursor: grab;\n }\n [part=choice]:hover { border-color: var(--boe-token-stroke-stroke, #e8e8e8); background: var(--boe-token-surface-surface-hover, #f4f4f4); }\n [part=choice-icon] { grid-row: span 2; width: 20px; height: 20px; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=choice][data-tone=accent] [part=choice-icon] { color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=choice-icon] svg { width: 20px; height: 20px; }\n [part=choice-name] { font-size: 13px; font-weight: 600; line-height: 1.3; }\n [part=choice-description] { grid-column: 2; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.35; }\n [part=palette-hint] { margin: 4px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.45; }\n [part=canvas] {\n position: relative; min-width: 0; height: 100%; overflow: hidden;\n border: 0; border-radius: 0;\n background-color: var(--boe-token-surface-surface-secondary, #fbfbfb);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 26%, transparent) 1px, transparent 1.2px);\n background-size: 12px 12px;\n touch-action: none; user-select: none; cursor: grab;\n }\n [part=canvas][data-panning] { cursor: grabbing; }\n [part=hold] { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 10px 16px; border-bottom: 1px solid color-mix(in srgb, var(--boe-token-text-status-text-warning, #9a6b00) 45%, var(--boe-token-stroke-stroke, #e8e8e8)); background: color-mix(in srgb, var(--boe-token-surface-status-surface-inprogress, #ffc447) 14%, var(--boe-token-surface-surface, #fff)); color: var(--boe-token-text-status-text-warning, #6b4a00); font-size: 13px; }\n [part=hold-text] { flex: 1 1 280px; min-width: 0; }\n [part=hold] button { min-height: 28px; padding: 3px 8px; border: 1px solid currentColor; border-radius: 6px; background: transparent; font-size: 12px; font-weight: 650; white-space: nowrap; }\n [part=world] { position: absolute; inset: 0; transform-origin: 0 0; }\n [part=lines] { position: absolute; inset: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; z-index: 3; }\n [part=line] { stroke: var(--boe-token-text-text-secondary, #6f6f6f); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; fill: none; }\n [part=line][data-selected=true] { stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 2; }\n [part=line][data-drop=true] { stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 2.5; stroke-dasharray: 6 4; }\n [part=line-hit] { fill: none; stroke: transparent; stroke-width: 24; pointer-events: stroke; }\n [part=connection-preview] { fill: none; stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 2.5; stroke-dasharray: 6 4; }\n [part=pinned-end] { fill: var(--boe-token-surface-surface-brand, #0061d5); pointer-events: none; }\n [part=box], [part=frame] {\n position: absolute; touch-action: none; text-align: start; z-index: 4; display: grid; grid-template-columns: 20px minmax(0,1fr); align-content: center;\n gap: 2px 10px; min-width: 0; min-height: 64px; padding: 11px 12px;\n border: 1px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585));\n border-radius: 8px; background: var(--boe-token-surface-surface, #fff);\n font-size: 13px; line-height: 1.3;\n transition: border-color var(--boe-profile-motion-interactive, 200ms) ease-out,\n background var(--boe-profile-motion-interactive, 200ms) ease-out;\n }\n [part=box][data-shape=task] { align-content: normal; font-size: 14px; line-height: 1.45; }\n [part=box][data-shape=task] > [part=icon] { grid-row: span 4; margin-top: 1px; }\n [part=box]:hover, [part=frame]:hover { border-color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [data-connect-target=true] { border-color: var(--boe-token-surface-surface-brand, #0061d5) !important; }\n [part=box] strong, [part=frame] strong { overflow-wrap: anywhere; }\n [part=box] strong, [part=frame] strong { font-size: 13.5px; font-weight: 650; line-height: 1.3; }\n [part=box] small, [part=frame] small { grid-column: 2; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; line-height: 1.35; }\n [part=technical-description] { font: 12px/1.4 ui-monospace, 'SF Mono', Menlo, Consolas, monospace !important; overflow-wrap: anywhere; }\n [part=metrics] { grid-column: 2; display: flex; gap: 6px; flex-wrap: wrap; margin-top: 3px; font-size: 12px; line-height: 1.45; font-variant-numeric: tabular-nums; }\n [part=metrics][data-state=unmeasured] { color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=metric-separator] { color: var(--boe-token-text-text-tertiary, #767676); }\n [part=technical-summary] { overflow-wrap: anywhere; }\n [part=technical-inline-code] { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; }\n [part=metric-warning] { color: var(--boe-token-text-status-text-warning, #8a5800); }\n [part=icon] { grid-row: span 3; width: 20px; height: 20px; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=icon] svg { display: block; width: 20px; height: 20px; }\n [data-tone=accent] > [part=icon] { color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=box][aria-current=true], [part=frame][aria-current=true] {\n border: 1px solid var(--boe-token-surface-surface-brand, #0061d5);\n background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 7%, var(--boe-token-surface-surface, #fff));\n }\n [data-invalid=true] { border-color: var(--boe-token-text-status-text-error, #b92340) !important; }\n [part=problem] { grid-column: 1 / -1; color: var(--boe-token-text-status-text-error, #b92340); font-size: 12px; }\n [part=frame] { z-index: 1; align-content: start; border-radius: 12px; background: color-mix(in srgb, var(--boe-token-surface-surface, #fff) 78%, transparent); }\n [part=frame] small { grid-column: 2; }\n [part=loop-mark] { position: absolute; right: 12px; bottom: 8px; font-size: 20px; line-height: 1; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=frame-resize] { position: absolute; right: -12px; bottom: -12px; z-index: 8; width: 28px; height: 28px; min-height: 28px; padding: 0; border: 0; border-radius: 8px; background: var(--boe-token-surface-surface, #fff); box-shadow: 0 0 0 1px var(--boe-process-box-edge, #858585); cursor: nwse-resize; }\n [part=frame-resize]::before { content: ''; position: absolute; right: 7px; bottom: 7px; width: 11px; height: 11px; border-right: 2px solid var(--boe-token-text-text-secondary, #6f6f6f); border-bottom: 2px solid var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=frame]:not([aria-current=true]) [part=frame-resize] { visibility: hidden; }\n [data-shape=gateway], [data-shape=event] { width: 56px !important; height: 56px !important; min-height: 0; padding: 0; border: 0 !important; background: transparent !important; display: block; overflow: visible; }\n [data-shape=gateway]::before { content: ''; position: absolute; left: 8px; top: 8px; width: 40px; height: 40px; rotate: 45deg; border: 1.5px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); }\n [data-shape=event]::before { content: ''; position: absolute; left: 8px; top: 8px; width: 40px; height: 40px; border: 1.5px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 50%; background: var(--boe-token-surface-surface, #fff); }\n [data-shape=event][data-kind=finish]::before { border-width: 4px; }\n [data-shape=event][data-kind=scheduled-start]::after { content: ''; position: absolute; left: 13px; top: 13px; width: 30px; height: 30px; border: 1px solid currentColor; border-radius: 50%; }\n [data-shape=gateway] > [part=icon], [data-shape=event] > [part=icon] { position: absolute; left: 18px; top: 18px; z-index: 1; }\n [data-shape=gateway] > strong, [data-shape=event] > strong { position: absolute; top: 58px; left: 50%; width: 150px; translate: -50% 0; text-align: center; font-size: 12px; }\n [data-shape=gateway] > small { display: none; }\n [part=caption] { position: absolute; left: 50%; translate: -50% 0; width: max-content; max-width: 168px; padding: 1px 4px; border-radius: 4px; text-align: center; background: color-mix(in srgb, var(--boe-token-surface-surface-hover, #f4f4f4) 88%, transparent); }\n [part=caption][data-side=below] { top: 60px; }\n [part=caption][data-side=above] { bottom: 60px; }\n [part=caption] strong { font-size: 12.5px; font-weight: 600; line-height: 1.3; }\n [part=caption] small { display: block; font-weight: 400; }\n [part=caption] [part=technical-description] { font: 10.5px ui-monospace, 'SF Mono', Menlo, Consolas, monospace !important; white-space: nowrap; }\n [data-shape=event] > small { position: absolute; top: 75px; left: 50%; width: 172px; translate: -50% 0; text-align: center; font-size: 11px; line-height: 1.25; }\n [part=event-details] { position: absolute; top: 75px; left: 50%; width: 172px; translate: -50% 0; text-align: center; }\n [part=event-details] small { display: block; font-size: 11px; line-height: 1.25; }\n [data-shape=gateway][aria-current=true]::before, [data-shape=event][aria-current=true]::before { border-color: var(--boe-token-surface-surface-brand, #0061d5); background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 7%, var(--boe-token-surface-surface, #fff)); }\n [part=port] { position: absolute; z-index: 5; width: 24px; height: 24px; min-height: 24px; padding: 0; border: 0; border-radius: 50%; background: transparent; opacity: 0; }\n [part=port][data-side=north] { left: calc(50% - 12px); top: -12px; }\n [part=port][data-side=south] { left: calc(50% - 12px); bottom: -12px; }\n [part=port][data-side=east] { right: -12px; top: calc(50% - 12px); }\n [part=port][data-side=west] { left: -12px; top: calc(50% - 12px); }\n [part=port]::before { content: ''; display: block; width: 10px; height: 10px; margin: 7px; border: 1.5px solid var(--boe-token-surface-surface-brand, #0061d5); border-radius: 50%; background: var(--boe-token-surface-surface, #fff); }\n [part=port]:hover::before, [part=port]:focus-visible::before { width: 22px; height: 22px; margin: 1px; background: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=port]::after { position: absolute; inset: 1px; display: none; place-items: center; color: #fff; font-size: 15px; font-weight: 700; line-height: 1; }\n [part=port][data-side=north]::after { content: '\u2191'; }\n [part=port][data-side=east]::after { content: '\u2192'; }\n [part=port][data-side=south]::after { content: '\u2193'; }\n [part=port][data-side=west]::after { content: '\u2190'; }\n [part=port]:hover::after, [part=port]:focus-visible::after, [part=port][data-hot=true]::after { display: grid; }\n [part=port][data-hot=true] { opacity: 1; }\n [part=port][data-hot=true]::before { width: 22px; height: 22px; margin: 1px; background: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=end-grip] { position: absolute; z-index: 8; width: 24px; height: 24px; min-height: 24px; padding: 0; border: 0; border-radius: 50%; background: transparent; }\n [part=end-grip]::before { content: ''; display: block; width: 10px; height: 10px; margin: 7px; border: 1.5px solid var(--boe-token-surface-surface-brand, #0061d5); border-radius: 50%; background: var(--boe-token-surface-surface, #fff); }\n [part=box]:hover [part=port], [part=box]:focus-within [part=port], [part=box][aria-current=true] [part=port], [part=frame]:hover [part=port] { opacity: 1; }\n [part=connection] { position: absolute; z-index: 5; display: flex; align-items: center; gap: 4px; transform: translate(-50%,-50%); min-width: 0; min-height: 0; padding: 2px 6px; border-radius: 6px; background: var(--boe-token-surface-surface-secondary, #fbfbfb); font-size: 12px; font-weight: 600; white-space: nowrap; }\n /* Unlabelled lines still need a reachable midpoint for pointer insert/selection. */\n [part=connection]:has(> span:empty):not(:has(> small)) { padding: 0; background: transparent; min-width: calc(24px * var(--boe-process-inverse-zoom, 1)); min-height: calc(24px * var(--boe-process-inverse-zoom, 1)); }\n [part=connection] > small { margin-left: 4px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 11px; font-weight: 400; font-variant-numeric: tabular-nums; }\n [part=connection-actions] { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); display: flex; gap: 4px; width: max-content; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); opacity: 0; pointer-events: none; }\n [part=connection]:hover [part=connection-actions], [part=connection]:focus-within [part=connection-actions] { opacity: 1; pointer-events: auto; }\n [part=connection-actions] button { min-height: 32px; padding: 4px; font-size: 12px; }\n [part=note] { width: 208px; border: 1px dashed var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 8px; }\n [part=section] { z-index: 0; border: 1px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 16px; background: color-mix(in srgb, var(--boe-token-surface-surface, #fff) 45%, transparent); }\n [part=section] strong { display: block; font-size: 12.5px; font-weight: 650; line-height: 1.3; }\n [part=section] small { display: block; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 11.5px; line-height: 1.35; }\n [part=note], [part=section] { position: absolute; padding: 8px; pointer-events: none; }\n [part=marquee] { position: absolute; border: 2px solid var(--boe-token-surface-surface-brand, #0061d5); background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 12%, transparent); pointer-events: none; }\n [part=connect-tooltip] { position: absolute; z-index: 20; max-width: 240px; padding: 5px 8px; border-radius: 7px; background: var(--boe-token-text-text, #141413); color: var(--boe-token-surface-surface, #fff); font-size: 12px; pointer-events: none; box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=connect-tooltip][data-invalid=true] { background: var(--boe-token-text-status-text-error, #b92340); color: #fff; }\n [part=ghost-box], [part=palette-ghost] { position: absolute; z-index: 5; display: flex; align-items: center; padding: 11px 12px; border: 1.5px dashed var(--boe-token-surface-surface-brand, #0061d5); border-radius: 8px; background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 10%, var(--boe-token-surface-surface, #fff)); font-size: 13px; font-weight: 650; pointer-events: none; }\n [part=guide] { position: absolute; background: var(--boe-token-surface-surface-brand, #0061d5); pointer-events: none; }\n [part=measure] { position: absolute; padding: 4px; background: var(--boe-token-surface-surface, #fff); font-size: 12px; pointer-events: none; }\n [part=insert-chooser] { max-width: calc(100vw - 32px); max-height: 65vh; overflow: auto; padding: 12px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); }\n [part=keyboard-chooser] { position: fixed; inset: auto; width: min(320px, calc(100vw - 24px)); max-height: min(420px, calc(100dvh - 24px)); margin: 0; overflow: auto; padding: 12px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #141413); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=chooser-title] { margin: 0 0 10px; font-size: 12px; font-weight: 650; line-height: 1.4; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=controls] { position: absolute; left: 16px; bottom: 16px; z-index: 10; display: flex; align-items: center; gap: 2px; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=controls] [data-command=reset] { min-width: 56px; font-variant-numeric: tabular-nums; }\n [part=minimap] { position: absolute; right: 16px; bottom: 16px; width: 208px; height: 136px; touch-action: none; background: var(--boe-token-surface-surface, #fff); border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=minimap] rect:not([data-viewport]) { fill: color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 35%, transparent); }\n [part=minimap] rect[data-selected=true] { fill: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=minimap] rect[data-section] { fill: color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 8%, transparent); }\n [part=minimap] [data-viewport] { fill: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 8%, transparent); stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 1.5; vector-effect: non-scaling-stroke; }\n [part=selection-toolbar] { position: absolute; bottom: auto; z-index: 11; display: flex; flex-wrap: nowrap; gap: 2px; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); white-space: nowrap; }\n [part=selection-toolbar] button { display: inline-flex; align-items: center; gap: 4px; }\n [part=selection-plus] { font-size: 18px; line-height: 12px; font-weight: 500; }\n [part=checks] { display: grid; gap: 6px; margin: 0; padding: 0; }\n [part=checks] button { width: 100%; display: grid; grid-template-columns: 16px 1fr; align-items: start; gap: 8px; padding: 8px 10px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 10px; text-align: start; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #222); line-height: 1.45; }\n [part=check-icon] { flex: 0 0 16px; width: 16px; height: 16px; margin-top: 1px; color: var(--boe-token-text-status-text-error, #b92340); }\n [part=checks] strong { display: block; font-size: 13px; font-weight: 600; line-height: 1.45; }\n [part=checks] small { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; }\n [part=check-icon][data-tone=success] { color: var(--boe-token-text-status-text-success, #138a58); }\n [part=problem] { display: flex; align-items: center; gap: 4px; }\n [part=problem] [part=check-icon] { flex-basis: 12px; width: 12px; height: 12px; margin: 0; }\n [part=checks-ready] { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--boe-token-text-text, #222); font-size: 13px; line-height: 1.45; }\n [part=process-heading] { display: grid; gap: 2px; padding: 0 16px 12px; }\n [part=process-title] { font-size: 15px; line-height: 1.4; }\n [part=process-summary] { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; }\n [part=pane-tabs] { display: flex; flex-wrap: nowrap; gap: 4px; margin: 0; padding: 0 16px; border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); overflow-x: auto; }\n [part=pane-tabs] button { flex: none; min-height: 40px; border: 0; border-bottom: 2px solid transparent; border-radius: 0; background: transparent; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 13px; font-weight: 600; }\n [part=pane-tabs] button[aria-selected=true] { color: var(--boe-token-text-text, #141413); border-bottom-color: currentColor; }\n [part=editor], [part=pane-content] { padding: 8px 16px; }\n [part=editor] { display: grid; align-content: start; gap: 12px; }\n [part=inspector-kind] { display: none; }\n [part=inspector-purpose] { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; }\n [part=field] { display: grid; gap: 4px; }\n [part=field] label { display: grid; gap: 5px; font-size: 12.5px; font-weight: 650; }\n [part=field] :is(input:not([type=checkbox]),textarea,select) { width: 100%; min-height: 36px; padding: 7px 10px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #141413); font-family: inherit; font-size: 13px; line-height: 1.4; }\n [part=field] textarea { min-height: 76px; resize: vertical; }\n [part=field]:has([part=action-options]) { position: relative; }\n [part=field]:has([part=action-options]) input { padding-right: 34px; }\n [part=action-caret] { position: absolute; z-index: 1; top: 25px; right: 2px; width: 32px; min-height: 32px; padding: 0; border: 0; background: transparent; font-size: 18px; }\n [part=action-options] { position: absolute; z-index: 30; top: calc(100% - 1px); left: 0; right: 0; max-height: 280px; overflow: auto; padding: 6px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 10px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=action-options][hidden] { display: none; }\n [part=action-options] button { display: block; width: 100%; min-height: 36px; padding: 7px 9px; border: 0; border-radius: 7px; text-align: start; background: transparent; font-size: 12.5px; }\n [part=action-options] button:hover, [part=action-options] [aria-selected=true] { background: var(--boe-token-surface-surface-hover, #f4f4f4); }\n [part=action-options] button strong { display: block; font-size: 13px; font-weight: 650; }\n [part=action-options] button small { display: block; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=action-options] [part=action-back] { color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=field] input[type=checkbox] { justify-self: start; width: 18px; height: 18px; accent-color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=field] small { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.4; }\n [part=field] [part=field-problem] { color: var(--boe-token-text-status-text-error, #b92340); }\n [part=variable-chips] { display: flex; flex-wrap: wrap; gap: 4px; }\n [part=variable-chips] button { min-height: 24px; padding: 2px 8px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface-secondary, #fbfbfb); font-size: 11.5px; }\n [part=leads-to] { display: grid; gap: 6px; padding-top: 10px; border-top: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=leads-to] h3 { margin: 0; font-size: 12.5px; }\n [part=lead-row] { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; }\n [part=lead-row] button, [part=leads-to] > button, [part=inspector-actions] button { min-height: 28px; padding: 4px 8px; font-size: 12px; }\n [part=inspector-metrics] { margin: 0; font-size: 12.5px; line-height: 1.5; font-variant-numeric: tabular-nums; }\n [part=inspector-metrics] h3 { margin: 0 0 6px; font-size: 12.5px; }\n [part=inspector-metrics] p { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=inspector-metrics] table { width: 100%; border-collapse: collapse; font-size: 13px; line-height: 1.45; }\n [part=inspector-metrics] :is(th,td) { padding: 5px 0; border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); font-weight: 400; }\n [part=inspector-metrics] th { text-align: start; font-variant-numeric: normal; }\n [part=inspector-metrics] td { text-align: right; font-variant-numeric: tabular-nums; }\n [part=inspector-actions] { display: flex; gap: 8px; padding-top: 10px; border-top: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=arrange-actions] { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 6px; }\n [part=arrange-actions] button { min-height: 32px; padding: 5px 7px; font-size: 12px; text-align: start; }\n [part=outline-intro] { margin: 4px 0 14px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; line-height: 1.5; }\n [part=copy-outline] { justify-self: start; min-height: 32px; margin-top: 4px; padding: 0 14px; border-radius: 20px; font-size: 13px; font-weight: 600; letter-spacing: .01em; }\n [part=outline-list] { margin: 0; padding-left: 22px; font-size: 13.5px; line-height: 1.5; }\n [part=outline-list] [part=outline-list] { margin-top: 2px; }\n [part=outline-list] li { margin: 3px 0; padding-left: 2px; }\n [part=outline-list] li::marker { color: var(--boe-token-text-text-secondary, #6f6f6f); font-variant-numeric: tabular-nums; font-size: 12.5px; }\n [part=outline-list] button, [part=outline-list] strong { border: 0; border-radius: 4px; background: transparent; padding: 0; min-height: 24px; display: inline-block; font-size: 13.5px; font-weight: 600; line-height: 24px; text-align: start; }\n [part=outline-list] button:hover { color: var(--boe-token-surface-surface-brand, #0061d5); text-decoration: underline; text-underline-offset: 2px; }\n [part=outline-list] button[aria-current=true] { color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=outline-list] p { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; line-height: 1.5; }\n [part=inspector-heading] { margin: 0 0 2px; font-size: 15px; font-weight: 650; }\n [part=inspector-heading][data-single=true]:not(:focus-visible) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n [part=variable-row], [part=connection-row] { display: grid; gap: 4px; padding: 10px 0; border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); font-size: 12.5px; }\n :host([data-variables-editable]) [part=variable-row] { grid-template-columns: minmax(0,1fr) minmax(0,1fr) 32px; gap: 6px; align-items: center; }\n :host([data-variables-editable]) [part=variable-row] label:has([data-variable-key=name]) { grid-column: 1; grid-row: 1; }\n :host([data-variables-editable]) [part=variable-row] label:has([data-variable-key=value]) { grid-column: 2; grid-row: 1; }\n :host([data-variables-editable]) [part=variable-row] label:has([data-variable-key=scope]) { grid-column: 2; grid-row: 2; }\n :host([data-variables-editable]) [part=variable-remove] { grid-column: 3; grid-row: 1; }\n :host([data-variables-editable]) [part=variable-row] :is(input,select) { width: 100%; min-width: 0; min-height: 32px; border-radius: 10px; font-size: 12.5px; }\n :host([data-variables-editable]) [part=variable-row] p { grid-column: 1 / -1; }\n :host([data-variables-editable]) [part=variable-row] [part=field-problem] { color: var(--boe-token-text-status-text-error, #b92340); }\n [part=variable-remove], [part=variable-add] { min-height: 32px; align-self: end; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); color: inherit; cursor: pointer; }\n [part=variable-row] p { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=variable-scope] { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 11.5px; }\n [part=variable-row] label { display: grid; gap: 4px; font-weight: 650; }\n [part=variable-row] :is(input,select) { min-height: 36px; padding: 7px 10px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); }\n [part=help] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n :host([data-narrow]) [part=layout] { grid-template-columns: minmax(0,1fr); }\n :host([data-narrow]) [part=layout] { overflow: visible; }\n :host([data-narrow]) [part=pane-drawer] { display: none; position: absolute; z-index: 40; margin: 0; max-width: none; max-height: none; padding: 0; overflow: auto; border: 0; border-radius: 0; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .24)); }\n :host([data-narrow]) [part=pane-drawer][open] { display: block; }\n :host([data-narrow]) [part=pane-title] { display: block; position: absolute; top: 18px; left: 16px; margin: 0; font-size: 14px; font-weight: 650; line-height: 20px; }\n :host([data-narrow]) [part=pane-close], :host([data-narrow]) [data-command=palette], :host([data-narrow]) [data-command=details] { display: inline-block; }\n :host([data-narrow]) [part=pane-close] { position: absolute; top: 12px; right: 12px; z-index: 1; width: 28px; height: 28px; padding: 0; border-radius: 50%; font-size: 16px; font-weight: 600; line-height: 1; }\n :host([data-narrow]) [part=pane-drawer][data-pane=palette] { left: 0; top: 0; bottom: auto; width: min(320px, 86%); height: 100%; max-height: 100%; }\n :host([data-narrow]) [part=pane-drawer][data-pane=inspector] { left: 0; right: 0; top: auto; bottom: 0; width: 100%; height: min(62%, 560px); border-radius: 24px 24px 0 0; }\n :host([data-narrow]) [part=pane-drawer][data-pane=inspector]::before { content: ''; display: block; width: 36px; height: 4px; margin: 6px auto; border-radius: 2px; background: var(--boe-token-text-text-secondary, #767676); opacity: .55; }\n :host([data-narrow]) [part=palette], :host([data-narrow]) [part=inspector] { height: 100%; }\n :host([data-narrow]) [part=inspector] { border-left: 0; }\n :host([data-narrow]) [part=palette] { padding-top: 54px; }\n :host([data-narrow]) [part=palette-heading] { display: none; }\n :host([data-narrow]) [part=minimap] { display: none; }\n :host([data-narrow]) [part=toolbar] { justify-content: flex-start; }\n :host([data-narrow]) [data-command=palette] { order: -2; }\n :host([data-narrow]) [part=view-menu] { order: -1; }\n :host([data-narrow]) [part=view-switch], :host([data-narrow]) [part=run-toggle] { display: none; }\n :host([data-narrow]) [data-command=checks-status] { display: none; }\n :host([data-narrow]) [part=view-menu] { display: block; }\n :host([data-narrow]) [part=controls] { left: 12px; bottom: 12px; }\n :host([data-narrow]) [part=selection-toolbar] [part=selection-plus] + span { display: none; }\n :host([data-phone]) [data-command=tidy] { display: none; }\n :host([data-phone]) [data-command=undo], :host([data-phone]) [data-command=redo] { display: none; }\n @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; scroll-behavior: auto !important; } }\n";
2
+ export declare const processModelerDesign = "\n :host([hidden]), [hidden] { display: none !important; }\n *, *::before, *::after { box-sizing: border-box; }\n button, input, textarea, select { font: inherit; color: inherit; }\n button { min-height: 36px; padding: 8px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); cursor: pointer; }\n button:disabled { opacity: .5; cursor: default; }\n :is(button,input,textarea,select,[part=canvas],[part=minimap],[part=box],[part=frame]):focus-visible { outline: 2px solid var(--boe-token-surface-surface-brand, #0061d5); outline-offset: 2px; }\n :host {\n display: block;\n position: relative;\n isolation: isolate;\n min-width: 0;\n --boe-process-box-edge: color-mix(in srgb, var(--boe-token-text-text, #141413) 55%, var(--boe-token-surface-surface, #fff));\n color: var(--boe-token-text-text, #141413);\n background: var(--boe-token-surface-surface, #fff);\n font: 14px/1.5 var(--boe-token-font-family-base, Inter, sans-serif);\n }\n [part=sr-only], [part=status], [part=urgent-status] { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n [part=toolbar] {\n min-height: 56px;\n margin: 0;\n padding: 8px 16px;\n display: flex;\n align-items: center;\n justify-content: flex-end;\n gap: 8px;\n flex-wrap: wrap;\n border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8);\n background: var(--boe-token-surface-surface, #fff);\n }\n [part=toolbar] button, [part=controls] button, [part=selection-toolbar] button {\n min-height: 30px;\n padding: 4px 10px;\n border: 1px solid transparent;\n border-radius: 8px;\n background: transparent;\n font-size: 13px;\n font-weight: 600;\n }\n [part=toolbar] svg, [part=controls] svg { width: 16px; height: 16px; display: block; }\n [part=toolbar] [data-command=undo], [part=toolbar] [data-command=redo], [part=controls] button:not([data-command=reset]) { width: 30px; height: 30px; padding: 6px; display: grid; place-items: center; }\n [part=controls] [data-command=snap][aria-pressed=true], [part=controls] [data-command=lock][aria-pressed=true] { background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 10%, var(--boe-token-surface-surface, #fff)); color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=toolbar] button:hover, [part=controls] button:hover, [part=selection-toolbar] button:hover {\n background: var(--boe-token-surface-surface-hover, #f4f4f4);\n }\n [part=toolbar] [data-command=tidy] { border-color: var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 18px; }\n [part=view-switch] { display: flex; gap: 2px; padding: 3px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 20px; }\n [part=view-switch] button { border-radius: 16px; }\n [part=view-switch] button[aria-pressed=true] { border-color: var(--boe-token-stroke-stroke, #e8e8e8); background: var(--boe-token-surface-surface, #fff); }\n [part=run-toggle] { display: flex; align-items: center; gap: 8px; white-space: nowrap; font-size: 13px; font-weight: 600; cursor: pointer; }\n [part=run-toggle] input { appearance: none; position: relative; width: 32px; height: 18px; margin: 0; border: 0; border-radius: 9px; background: var(--boe-token-surface-surface-hover, #f4f4f4); cursor: pointer; transition: background var(--boe-profile-motion-interactive, 200ms) ease-out; }\n [part=run-toggle] input::after { content: ''; position: absolute; top: 2px; left: 2px; width: 14px; height: 14px; border-radius: 50%; background: var(--boe-token-surface-surface, #fff); box-shadow: 0 1px 2px rgb(0 0 0 / .25); transition: transform var(--boe-profile-motion-interactive, 200ms) ease-out; }\n [part=run-toggle] input:checked { background: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=run-toggle] input:checked::after { transform: translateX(14px); }\n [part=run-toggle] input:focus-visible { outline: 2px solid var(--boe-token-surface-surface-brand, #0061d5); outline-offset: 2px; }\n [part=toolbar] [data-command=checks-status] { white-space: nowrap; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=toolbar] [data-command=checks-status][data-state=bad] { color: var(--boe-token-text-status-text-error, #b92340); }\n [part=view-menu] { display: none; position: relative; }\n [part=view-menu] summary { list-style: none; padding: 4px 14px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 18px; font-size: 13px; font-weight: 600; cursor: pointer; }\n [part=view-menu][open] { z-index: 20; }\n [part=view-menu-options] { position: absolute; z-index: 20; top: calc(100% + 4px); left: 0; width: 192px; padding: 6px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 10px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=view-menu] button { display: block; width: 100%; text-align: start; background: transparent; }\n [part=layout] {\n display: grid;\n grid-template-columns: 248px minmax(0, 1fr) 320px;\n gap: 0;\n min-height: 0;\n height: var(--boe-process-height, 660px);\n overflow: hidden;\n }\n [part=pane-drawer] { display: contents; color: inherit; }\n [part=canvas-stack] { display: grid; grid-template-rows: auto minmax(0, 1fr); min-width: 0; min-height: 0; }\n /* The hidden hold leaves row 1 empty. Keep the canvas in the flexible row\n explicitly; auto-placement otherwise puts its absolute world in a 0px row. */\n [part=canvas-stack] > [part=hold] { grid-row: 1; }\n [part=canvas-stack] > [part=canvas] { grid-row: 2; }\n [part=insert-chooser]::backdrop { background: rgb(0 0 0 / .45); }\n [part=pane-scrim] { position: absolute; inset: 0; z-index: 30; background: rgb(0 0 0 / .45); }\n [part=pane-title], [part=pane-close], [data-command=palette], [data-command=details] { display: none; }\n [part=palette], [part=inspector] {\n min-width: 0;\n overflow: auto;\n padding: 12px;\n border: 0;\n border-radius: 0;\n background: var(--boe-token-surface-surface, #fff);\n }\n [part=palette] { display: flex; flex-direction: column; gap: 12px; border-right: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=palette-heading] { margin: 0 4px; font-size: 14px; font-weight: 650; line-height: 20px; }\n [part=skip-link] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); }\n [part=skip-link]:focus-visible { position: static; width: auto; height: auto; clip-path: none; }\n [part=inspector] { border-left: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); padding: 16px 0; }\n [part=palette] label { font-size: 0; position: relative; }\n [part=search-icon] { position: absolute; left: 12px; top: 9px; width: 18px; height: 18px; color: var(--boe-token-text-text-secondary, #6f6f6f); pointer-events: none; }\n [part=palette] input {\n width: 100%; min-width: 0;\n height: 36px;\n padding: 0 12px 0 36px;\n border: 1px solid transparent;\n border-radius: 24px;\n background: var(--boe-token-surface-surface-hover, #f4f4f4);\n font-size: 13px;\n }\n [part=palette] input:focus-visible { border-color: var(--boe-token-surface-surface-brand, #0061d5); background: var(--boe-token-surface-surface, #fff); }\n [part=choices] { display: grid; align-content: start; gap: 4px; max-height: none; overflow: visible; }\n [part=group] { margin: 4px 4px 6px; font-size: 12px; line-height: 1.45; font-weight: 650; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=choice] {\n display: grid; grid-template-columns: 20px minmax(0,1fr); gap: 0 10px;\n min-height: 44px; padding: 8px 10px; border: 1px solid transparent; border-radius: 10px;\n text-align: start; background: transparent; cursor: grab; touch-action: none;\n }\n [part=choice]:hover { border-color: var(--boe-token-stroke-stroke, #e8e8e8); background: var(--boe-token-surface-surface-hover, #f4f4f4); }\n [part=choice-icon] { grid-row: span 2; width: 20px; height: 20px; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=choice][data-tone=accent] [part=choice-icon] { color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=choice-icon] svg { width: 20px; height: 20px; }\n [part=choice-name] { font-size: 13px; font-weight: 600; line-height: 1.45; }\n [part=choice-description] { grid-column: 2; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.35; }\n [part=palette-hint] { margin: 4px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.45; }\n [part=canvas] {\n position: relative; min-width: 0; height: 100%; overflow: hidden;\n border: 0; border-radius: 0;\n background-color: var(--boe-token-surface-surface-secondary, #fbfbfb);\n background-image: radial-gradient(circle, color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 26%, transparent) 1px, transparent 1.2px);\n background-size: 12px 12px;\n touch-action: none; user-select: none; cursor: grab;\n }\n [part=canvas][data-panning] { cursor: grabbing; }\n [part=hold] { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; padding: 10px 16px; border-bottom: 1px solid color-mix(in srgb, var(--boe-token-text-status-text-warning, #9a6b00) 45%, var(--boe-token-stroke-stroke, #e8e8e8)); background: color-mix(in srgb, var(--boe-token-surface-status-surface-inprogress, #ffc447) 14%, var(--boe-token-surface-surface, #fff)); color: var(--boe-token-text-status-text-warning, #6b4a00); font-size: 13px; }\n [part=hold-text] { flex: 1 1 280px; min-width: 0; }\n [part=hold] button { min-height: 28px; padding: 3px 8px; border: 1px solid currentColor; border-radius: 6px; background: transparent; font-size: 12px; font-weight: 650; white-space: nowrap; }\n [part=world] { position: absolute; inset: 0; transform-origin: 0 0; }\n [part=lines] { position: absolute; inset: 0; width: 1px; height: 1px; overflow: visible; pointer-events: none; z-index: 3; }\n [part=line] { stroke: var(--boe-token-text-text-secondary, #6f6f6f); stroke-width: 1.5; stroke-linecap: round; stroke-linejoin: round; fill: none; }\n [part=line][data-selected=true] { stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 2; }\n [part=line][data-drop=true] { stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 2.5; stroke-dasharray: 6 4; }\n [part=line-hit] { fill: none; stroke: transparent; stroke-width: 24; pointer-events: stroke; }\n [part=connection-preview] { fill: none; stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 2.5; stroke-dasharray: 6 4; }\n [part=pinned-end] { fill: var(--boe-token-surface-surface-brand, #0061d5); pointer-events: none; }\n [part=box], [part=frame] {\n position: absolute; touch-action: none; text-align: start; z-index: 4; display: grid; grid-template-columns: 20px minmax(0,1fr); align-content: center;\n gap: 2px 10px; min-width: 0; min-height: 64px; padding: 11px 12px;\n border: 1px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585));\n border-radius: 8px; background: var(--boe-token-surface-surface, #fff);\n font-size: 13px; line-height: 1.3;\n transition: border-color var(--boe-profile-motion-interactive, 200ms) ease-out,\n background var(--boe-profile-motion-interactive, 200ms) ease-out;\n }\n [part=box][data-shape=task] { align-content: normal; font-size: 14px; line-height: 1.45; }\n [part=box][data-shape=task] > [part=icon] { grid-row: span 4; margin-top: 1px; }\n [part=box]:hover, [part=frame]:hover { border-color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [data-connect-target=true][data-connect-invalid=true] { border-color: var(--boe-token-text-status-text-error, #b92340) !important; }\n [part=connection-preview][data-invalid=true] { stroke: var(--boe-token-text-status-text-error, #b92340); }\n [data-connect-target=true] { border-color: var(--boe-token-surface-surface-brand, #0061d5) !important; }\n [part=box] strong, [part=frame] strong { overflow-wrap: anywhere; }\n [part=box] strong, [part=frame] strong { font-size: 13.5px; font-weight: 650; line-height: 1.3; }\n [part=box] small, [part=frame] small { grid-column: 2; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; line-height: 1.35; }\n [part=technical-description] { font: 12px/1.4 ui-monospace, 'SF Mono', Menlo, Consolas, monospace !important; overflow-wrap: anywhere; }\n [part=metrics] { grid-column: 2; display: flex; gap: 6px; flex-wrap: wrap; margin-top: 3px; font-size: 12px; line-height: 1.45; font-variant-numeric: tabular-nums; }\n [part=metrics][data-state=unmeasured] { color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=metric-separator] { color: var(--boe-token-text-text-tertiary, #767676); }\n [part=technical-summary] { overflow-wrap: anywhere; }\n [part=technical-inline-code] { font-family: ui-monospace, 'SF Mono', Menlo, Consolas, monospace; }\n [part=metric-warning] { color: var(--boe-token-text-status-text-warning, #8a5800); }\n [part=icon] { grid-row: span 3; width: 20px; height: 20px; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=icon] svg { display: block; width: 20px; height: 20px; }\n [data-tone=accent] > [part=icon] { color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=box][aria-current=true], [part=frame][aria-current=true] {\n border: 1px solid var(--boe-token-surface-surface-brand, #0061d5);\n background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 7%, var(--boe-token-surface-surface, #fff));\n }\n [data-invalid=true] { border-color: var(--boe-token-text-status-text-error, #b92340) !important; }\n [part=problem] { grid-column: 1 / -1; color: var(--boe-token-text-status-text-error, #b92340); font-size: 12px; }\n [part=frame] { z-index: 1; align-content: start; border-radius: 12px; background: color-mix(in srgb, var(--boe-token-surface-surface, #fff) 78%, transparent); }\n [part=frame] small { grid-column: 2; }\n [part=loop-mark] { position: absolute; right: 12px; bottom: 8px; font-size: 20px; line-height: 1; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=frame][data-palette-drop=true] { border-color: var(--boe-token-surface-surface-brand, #0061d5); box-shadow: 0 0 0 2px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent); }\n [part=frame-resize] { position: absolute; right: -12px; bottom: -12px; z-index: 8; width: 28px; height: 28px; min-height: 28px; padding: 0; border: 0; border-radius: 8px; background: var(--boe-token-surface-surface, #fff); box-shadow: 0 0 0 1px var(--boe-process-box-edge, #858585); cursor: nwse-resize; }\n [part=frame-resize]::before { content: ''; position: absolute; right: 7px; bottom: 7px; width: 11px; height: 11px; border-right: 2px solid var(--boe-token-text-text-secondary, #6f6f6f); border-bottom: 2px solid var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=frame]:not([aria-current=true]) [part=frame-resize] { visibility: hidden; }\n [data-shape=gateway], [data-shape=event] { width: 56px !important; height: 56px !important; min-height: 0; padding: 0; border: 0 !important; background: transparent !important; display: block; overflow: visible; }\n [data-shape=gateway]::before { content: ''; position: absolute; left: 8px; top: 8px; width: 40px; height: 40px; rotate: 45deg; border: 1.5px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); }\n [data-shape=event]::before { content: ''; position: absolute; left: 8px; top: 8px; width: 40px; height: 40px; border: 1.5px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 50%; background: var(--boe-token-surface-surface, #fff); }\n [data-shape=event][data-kind=finish]::before { border-width: 4px; }\n [data-shape=event][data-kind=scheduled-start]::after { content: ''; position: absolute; left: 13px; top: 13px; width: 30px; height: 30px; border: 1px solid currentColor; border-radius: 50%; }\n [data-shape=gateway] > [part=icon], [data-shape=event] > [part=icon] { position: absolute; left: 18px; top: 18px; z-index: 1; }\n [data-shape=gateway] > strong, [data-shape=event] > strong { position: absolute; top: 58px; left: 50%; width: 150px; translate: -50% 0; text-align: center; font-size: 12px; }\n [data-shape=gateway] > small { display: none; }\n [part=caption] { position: absolute; left: 50%; translate: -50% 0; width: max-content; max-width: 168px; padding: 1px 4px; border-radius: 4px; text-align: center; background: color-mix(in srgb, var(--boe-token-surface-surface-hover, #f4f4f4) 88%, transparent); }\n [part=caption][data-side=below] { top: 60px; }\n [part=caption][data-side=above] { bottom: 60px; }\n [part=caption] strong { font-size: 12.5px; font-weight: 600; line-height: 1.3; }\n [part=caption] small { display: block; font-weight: 400; }\n [part=caption] [part=technical-description] { font: 10.5px ui-monospace, 'SF Mono', Menlo, Consolas, monospace !important; white-space: nowrap; }\n [data-shape=event] > small { position: absolute; top: 75px; left: 50%; width: 172px; translate: -50% 0; text-align: center; font-size: 11px; line-height: 1.25; }\n [part=event-details] { position: absolute; top: 75px; left: 50%; width: 172px; translate: -50% 0; text-align: center; }\n [part=event-details] small { display: block; font-size: 11px; line-height: 1.25; }\n [data-shape=gateway][aria-current=true]::before, [data-shape=event][aria-current=true]::before { border-color: var(--boe-token-surface-surface-brand, #0061d5); background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 7%, var(--boe-token-surface-surface, #fff)); }\n [part=port] { position: absolute; z-index: 5; width: 24px; height: 24px; min-height: 24px; padding: 0; border: 0; border-radius: 50%; background: transparent; opacity: 0; }\n [part=port][data-side=north] { left: calc(50% - 12px); top: -12px; }\n [part=port][data-side=south] { left: calc(50% - 12px); bottom: -12px; }\n [part=port][data-side=east] { right: -12px; top: calc(50% - 12px); }\n [part=port][data-side=west] { left: -12px; top: calc(50% - 12px); }\n [part=port]::before { content: ''; display: block; width: 10px; height: 10px; margin: 7px; border: 1.5px solid var(--boe-token-surface-surface-brand, #0061d5); border-radius: 50%; background: var(--boe-token-surface-surface, #fff); }\n [part=port]:hover::before, [part=port]:focus-visible::before { width: 22px; height: 22px; margin: 1px; background: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=port]::after { position: absolute; inset: 1px; display: none; place-items: center; color: #fff; font-size: 15px; font-weight: 700; line-height: 1; }\n [part=port][data-side=north]::after { content: '\u2191'; }\n [part=port][data-side=east]::after { content: '\u2192'; }\n [part=port][data-side=south]::after { content: '\u2193'; }\n [part=port][data-side=west]::after { content: '\u2190'; }\n [part=port]:hover::after, [part=port]:focus-visible::after, [part=port][data-hot=true]::after { display: grid; }\n [part=port][data-hot=true] { opacity: 1; }\n [part=port][data-hot=true]::before { width: 22px; height: 22px; margin: 1px; background: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=end-grip] { position: absolute; z-index: 8; width: 24px; height: 24px; min-height: 24px; padding: 0; border: 0; border-radius: 50%; background: transparent; }\n [part=end-grip]::before { content: ''; display: block; width: 10px; height: 10px; margin: 7px; border: 1.5px solid var(--boe-token-surface-surface-brand, #0061d5); border-radius: 50%; background: var(--boe-token-surface-surface, #fff); }\n [part=box]:hover [part=port], [part=box]:focus-within [part=port], [part=box][aria-current=true] [part=port], [part=frame]:hover [part=port] { opacity: 1; }\n [part=connection] { position: absolute; z-index: 5; display: flex; align-items: center; gap: 4px; transform: translate(-50%,-50%); min-width: 0; min-height: 0; padding: 2px 6px; border-radius: 6px; background: var(--boe-token-surface-surface-secondary, #fbfbfb); font-size: 12px; font-weight: 600; white-space: nowrap; }\n /* Unlabelled lines still need a reachable midpoint for pointer insert/selection. */\n [part=connection]:has(> span:empty):not(:has(> small)) { padding: 0; background: transparent; min-width: calc(24px * var(--boe-process-inverse-zoom, 1)); min-height: calc(24px * var(--boe-process-inverse-zoom, 1)); }\n [part=connection] > small { margin-left: 4px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 11px; font-weight: 400; font-variant-numeric: tabular-nums; }\n [part=connection-actions] { position: absolute; top: 100%; left: 50%; transform: translateX(-50%); display: flex; gap: 4px; width: max-content; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); opacity: 0; pointer-events: none; }\n [part=connection]:hover [part=connection-actions], [part=connection]:focus-within [part=connection-actions] { opacity: 1; pointer-events: auto; }\n [part=connection-actions] button { min-height: 32px; padding: 4px; font-size: 12px; }\n [part=note] { width: 208px; border: 1px dashed var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 8px; }\n [part=section] { z-index: 0; border: 1px solid var(--boe-process-box-edge, var(--boe-control-edge, #858585)); border-radius: 16px; background: color-mix(in srgb, var(--boe-token-surface-surface, #fff) 45%, transparent); }\n [part=section] strong { display: block; font-size: 12.5px; font-weight: 650; line-height: 1.3; }\n [part=section] small { display: block; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 11.5px; line-height: 1.35; }\n [part=note], [part=section] { position: absolute; padding: 8px; pointer-events: none; }\n [part=marquee] { position: absolute; border: 2px solid var(--boe-token-surface-surface-brand, #0061d5); background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 12%, transparent); pointer-events: none; }\n [part=connect-tooltip] { position: absolute; z-index: 20; max-width: 240px; padding: 5px 8px; border-radius: 7px; background: var(--boe-token-text-text, #141413); color: var(--boe-token-surface-surface, #fff); font-size: 12px; pointer-events: none; box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=connect-tooltip][data-invalid=true] { background: var(--boe-token-text-status-text-error, #b92340); color: #fff; }\n [part=ghost-box], [part=palette-ghost] { position: absolute; z-index: 5; display: flex; align-items: center; padding: 11px 12px; border: 1.5px dashed var(--boe-token-surface-surface-brand, #0061d5); border-radius: 8px; background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 10%, var(--boe-token-surface-surface, #fff)); font-size: 13px; font-weight: 650; pointer-events: none; }\n [part=palette-ghost][data-pointer] { position: fixed; z-index: 100; padding: 8px 12px; border: 1px solid var(--boe-token-surface-surface-brand, #0061d5); border-radius: 10px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-token-elevation-overlay, 0 8px 24px #0002); font-size: 13px; font-weight: 600; line-height: 1.45; gap: 8px; translate: -50% -50%; }\n [part=palette-ghost][data-pointer] svg { width: 20px; height: 20px; }\n [part=guide] { position: absolute; background: var(--boe-token-surface-surface-brand, #0061d5); pointer-events: none; }\n [part=measure] { position: absolute; padding: 4px; background: var(--boe-token-surface-surface, #fff); font-size: 12px; pointer-events: none; }\n [part=insert-chooser] { max-width: calc(100vw - 32px); max-height: 65vh; overflow: auto; padding: 12px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); }\n [part=keyboard-chooser] { position: fixed; inset: auto; width: min(320px, calc(100vw - 24px)); max-height: min(420px, calc(100dvh - 24px)); margin: 0; overflow: auto; padding: 12px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #141413); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=chooser-title] { margin: 0 0 10px; font-size: 12px; font-weight: 650; line-height: 1.4; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=controls] { position: absolute; left: 16px; bottom: 16px; z-index: 10; display: flex; align-items: center; gap: 2px; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=controls] button:not([data-command=reset]) { width: 32px; padding: 0; }\n [part=controls-divider] { width: 1px; height: 20px; margin: 0 4px; background: var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=controls] [data-command=reset] { min-width: 56px; padding: 0 6px; font-variant-numeric: tabular-nums; }\n [part=minimap] { position: absolute; right: 16px; bottom: 16px; width: 208px; height: 136px; touch-action: none; background: var(--boe-token-surface-surface, #fff); border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=minimap] rect:not([data-viewport]) { fill: color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 35%, transparent); }\n [part=minimap] rect[data-selected=true] { fill: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=minimap] rect[data-section] { fill: color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 8%, transparent); }\n [part=minimap] [data-viewport] { fill: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 8%, transparent); stroke: var(--boe-token-surface-surface-brand, #0061d5); stroke-width: 1.5; vector-effect: non-scaling-stroke; }\n [part=selection-toolbar] { position: absolute; bottom: auto; z-index: 11; display: flex; flex-wrap: nowrap; gap: 2px; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); white-space: nowrap; }\n [part=selection-toolbar] button { display: inline-flex; align-items: center; gap: 4px; }\n [part=selection-plus] { font-size: 18px; line-height: 12px; font-weight: 500; }\n [part=checks] { display: grid; gap: 6px; margin: 0; padding: 0; }\n [part=checks] button { width: 100%; display: grid; grid-template-columns: 16px 1fr; align-items: start; gap: 8px; padding: 8px 10px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 10px; text-align: start; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #222); line-height: 1.45; }\n [part=check-icon] { flex: 0 0 16px; width: 16px; height: 16px; margin-top: 1px; color: var(--boe-token-text-status-text-error, #b92340); }\n [part=checks] strong { display: block; font-size: 13px; font-weight: 600; line-height: 1.45; }\n [part=checks] small { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; }\n [part=check-icon][data-tone=success] { color: var(--boe-token-text-status-text-success, #138a58); }\n [part=problem] { display: flex; align-items: center; gap: 4px; }\n [part=problem] [part=check-icon] { flex-basis: 12px; width: 12px; height: 12px; margin: 0; }\n [part=checks-ready] { display: flex; align-items: center; gap: 8px; margin: 0; color: var(--boe-token-text-text, #222); font-size: 13px; line-height: 1.45; }\n [part=process-heading] { display: grid; gap: 2px; padding: 0 16px 12px; }\n [part=process-title] { font-size: 15px; line-height: 1.4; }\n [part=process-summary] { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; }\n [part=pane-tabs] { display: flex; flex-wrap: nowrap; gap: 4px; margin: 0; padding: 0 16px; border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); overflow-x: auto; }\n [part=pane-tabs] button { flex: none; min-height: 40px; border: 0; border-bottom: 2px solid transparent; border-radius: 0; background: transparent; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 13px; font-weight: 600; }\n [part=pane-tabs] button[aria-selected=true] { color: var(--boe-token-text-text, #141413); border-bottom-color: currentColor; }\n [part=editor], [part=pane-content] { padding: 8px 16px; }\n [part=editor] { display: grid; align-content: start; gap: 12px; }\n [part=inspector-kind] { display: none; }\n [part=inspector-purpose] { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; }\n [part=field] { display: grid; gap: 4px; }\n [part=field] label { display: grid; gap: 5px; font-size: 12.5px; font-weight: 650; }\n [part=field] :is(input:not([type=checkbox]),textarea,select) { width: 100%; min-height: 36px; padding: 7px 10px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #141413); font-family: inherit; font-size: 13px; line-height: 1.4; }\n [part=field] textarea { min-height: 76px; resize: vertical; }\n [part=field]:has([part=action-options]) { position: relative; }\n [part=field]:has([part=action-options]) input { padding-right: 34px; }\n [part=action-caret] { position: absolute; z-index: 1; top: 25px; right: 2px; width: 32px; min-height: 32px; padding: 0; border: 0; background: transparent; font-size: 18px; }\n [part=action-options] { position: absolute; z-index: 30; top: calc(100% - 1px); left: 0; right: 0; max-height: 280px; overflow: auto; padding: 6px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 10px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }\n [part=action-options][hidden] { display: none; }\n [part=action-options] button { display: block; width: 100%; min-height: 36px; padding: 7px 9px; border: 0; border-radius: 7px; text-align: start; background: transparent; font-size: 12.5px; }\n [part=action-options] button:hover, [part=action-options] [aria-selected=true] { background: var(--boe-token-surface-surface-hover, #f4f4f4); }\n [part=action-options] button strong { display: block; font-size: 13px; font-weight: 650; }\n [part=action-options] button small { display: block; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=action-options] [part=action-back] { color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=field] input[type=checkbox] { justify-self: start; width: 18px; height: 18px; accent-color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=field] small { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.4; }\n [part=field] [part=field-problem] { color: var(--boe-token-text-status-text-error, #b92340); }\n [part=variable-chips] { display: flex; flex-wrap: wrap; gap: 4px; }\n [part=variable-chips] button { min-height: 24px; padding: 2px 8px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface-secondary, #fbfbfb); font-size: 11.5px; }\n [part=leads-to] { display: grid; gap: 6px; padding-top: 10px; border-top: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=leads-to] h3 { margin: 0; font-size: 12.5px; }\n [part=lead-row] { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12.5px; }\n [part=lead-row] button, [part=leads-to] > button, [part=inspector-actions] button { min-height: 28px; padding: 4px 8px; font-size: 12px; }\n [part=inspector-metrics] { margin: 0; font-size: 12.5px; line-height: 1.5; font-variant-numeric: tabular-nums; }\n [part=inspector-metrics] h3 { margin: 0 0 6px; font-size: 12.5px; }\n [part=inspector-metrics] p { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=inspector-metrics] table { width: 100%; border-collapse: collapse; font-size: 13px; line-height: 1.45; }\n [part=inspector-metrics] :is(th,td) { padding: 5px 0; border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); font-weight: 400; }\n [part=inspector-metrics] th { text-align: start; font-variant-numeric: normal; }\n [part=inspector-metrics] td { text-align: right; font-variant-numeric: tabular-nums; }\n [part=inspector-actions] { display: flex; gap: 8px; padding-top: 10px; border-top: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); }\n [part=arrange-actions] { display: grid; grid-template-columns: repeat(2, minmax(0,1fr)); gap: 6px; }\n [part=arrange-actions] button { min-height: 32px; padding: 5px 7px; font-size: 12px; text-align: start; }\n [part=outline-intro] { margin: 4px 0 14px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; line-height: 1.5; }\n [part=copy-outline] { justify-self: start; min-height: 32px; margin-top: 4px; padding: 0 14px; border-radius: 20px; font-size: 13px; font-weight: 600; letter-spacing: .01em; }\n [part=outline-list] { margin: 0; padding-left: 22px; font-size: 13.5px; line-height: 1.5; }\n [part=outline-list] [part=outline-list] { margin-top: 2px; }\n [part=outline-list] li { margin: 3px 0; padding-left: 2px; }\n [part=outline-list] li::marker { color: var(--boe-token-text-text-secondary, #6f6f6f); font-variant-numeric: tabular-nums; font-size: 12.5px; }\n [part=outline-list] button, [part=outline-list] strong { border: 0; border-radius: 4px; background: transparent; padding: 0; min-height: 24px; display: inline-block; font-size: 13.5px; font-weight: 600; line-height: 24px; text-align: start; }\n [part=outline-list] button:hover { color: var(--boe-token-surface-surface-brand, #0061d5); text-decoration: underline; text-underline-offset: 2px; }\n [part=outline-list] button[aria-current=true] { color: var(--boe-token-surface-surface-brand, #0061d5); }\n [part=outline-list] p { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12.5px; line-height: 1.5; }\n [part=inspector-heading] { margin: 0 0 2px; font-size: 15px; font-weight: 650; }\n [part=inspector-heading][data-single=true]:not(:focus-visible) { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n [part=variable-row], [part=connection-row] { display: grid; gap: 4px; padding: 10px 0; border-bottom: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); font-size: 12.5px; }\n :host([data-variables-editable]) [part=variable-row] { grid-template-columns: minmax(0,1fr) minmax(0,1fr) 32px; gap: 6px; align-items: center; }\n :host([data-variables-editable]) [part=variable-row] label:has([data-variable-key=name]) { grid-column: 1; grid-row: 1; }\n :host([data-variables-editable]) [part=variable-row] label:has([data-variable-key=value]) { grid-column: 2; grid-row: 1; }\n :host([data-variables-editable]) [part=variable-row] label:has([data-variable-key=scope]) { grid-column: 2; grid-row: 2; }\n :host([data-variables-editable]) [part=variable-remove] { grid-column: 3; grid-row: 1; }\n :host([data-variables-editable]) [part=variable-row] :is(input,select) { width: 100%; min-width: 0; min-height: 32px; border-radius: 10px; font-size: 12.5px; }\n :host([data-variables-editable]) [part=variable-row] p { grid-column: 1 / -1; }\n :host([data-variables-editable]) [part=variable-row] [part=field-problem] { color: var(--boe-token-text-status-text-error, #b92340); }\n [part=variable-remove], [part=variable-add] { min-height: 32px; align-self: end; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); color: inherit; cursor: pointer; }\n [part=variable-row] p { margin: 0; color: var(--boe-token-text-text-secondary, #6f6f6f); }\n [part=variable-scope] { color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 11.5px; }\n [part=variable-row] label { display: grid; gap: 4px; font-weight: 650; }\n [part=variable-row] :is(input,select) { min-height: 36px; padding: 7px 10px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 8px; background: var(--boe-token-surface-surface, #fff); }\n [part=help] { position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap; }\n :host([data-narrow]) [part=layout] { grid-template-columns: minmax(0,1fr); }\n :host([data-narrow]) [part=layout] { overflow: visible; }\n :host([data-narrow]) [part=pane-drawer] { display: none; position: absolute; z-index: 40; margin: 0; max-width: none; max-height: none; padding: 0; overflow: auto; border: 0; border-radius: 0; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .24)); }\n :host([data-narrow]) [part=pane-drawer][open] { display: block; }\n :host([data-narrow]) [part=pane-title] { display: block; position: absolute; top: 18px; left: 16px; margin: 0; font-size: 14px; font-weight: 650; line-height: 20px; }\n :host([data-narrow]) [part=pane-close], :host([data-narrow]) [data-command=palette], :host([data-narrow]) [data-command=details] { display: inline-block; }\n :host([data-narrow]) [part=pane-close] { position: absolute; top: 12px; right: 12px; z-index: 1; width: 28px; height: 28px; padding: 0; border-radius: 50%; font-size: 16px; font-weight: 600; line-height: 1; }\n :host([data-narrow]) [part=pane-drawer][data-pane=palette] { left: 0; top: 0; bottom: auto; width: min(320px, 86%); height: 100%; max-height: 100%; }\n :host([data-narrow]) [part=pane-drawer][data-pane=inspector] { left: 0; right: 0; top: auto; bottom: 0; width: 100%; height: min(62%, 560px); border-radius: 24px 24px 0 0; }\n :host([data-narrow]) [part=pane-drawer][data-pane=inspector]::before { content: ''; display: block; width: 36px; height: 4px; margin: 6px auto; border-radius: 2px; background: var(--boe-token-text-text-secondary, #767676); opacity: .55; }\n :host([data-narrow]) [part=palette], :host([data-narrow]) [part=inspector] { height: 100%; }\n :host([data-narrow]) [part=inspector] { border-left: 0; }\n :host([data-narrow]) [part=palette] { padding-top: 54px; }\n :host([data-narrow]) [part=palette-heading] { display: none; }\n :host([data-narrow]) [part=minimap] { display: none; }\n :host([data-narrow]) [part=toolbar] { justify-content: flex-start; }\n :host([data-narrow]) [data-command=palette] { order: -2; }\n :host([data-narrow]) [part=view-menu] { order: -1; }\n :host([data-narrow]) [part=view-switch], :host([data-narrow]) [part=run-toggle] { display: none; }\n :host([data-narrow]) [data-command=checks-status] { display: none; }\n :host([data-narrow]) [part=view-menu] { display: block; }\n :host([data-hide-tidy]) [part=controls] { left: 12px; bottom: 12px; }\n :host([data-narrow]) [part=selection-toolbar] [part=selection-plus] + span { display: none; }\n :host([data-hide-tidy]) [data-command=tidy] { display: none; }\n :host([data-phone]) [data-command=undo], :host([data-phone]) [data-command=redo] { display: none; }\n @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; scroll-behavior: auto !important; } }\n";
@@ -104,17 +104,17 @@ export const processModelerDesign = `
104
104
  }
105
105
  [part=palette] input:focus-visible { border-color: var(--boe-token-surface-surface-brand, #0061d5); background: var(--boe-token-surface-surface, #fff); }
106
106
  [part=choices] { display: grid; align-content: start; gap: 4px; max-height: none; overflow: visible; }
107
- [part=group] { margin: 4px 4px 6px; font-size: 12px; font-weight: 650; color: var(--boe-token-text-text-secondary, #6f6f6f); }
107
+ [part=group] { margin: 4px 4px 6px; font-size: 12px; line-height: 1.45; font-weight: 650; color: var(--boe-token-text-text-secondary, #6f6f6f); }
108
108
  [part=choice] {
109
109
  display: grid; grid-template-columns: 20px minmax(0,1fr); gap: 0 10px;
110
110
  min-height: 44px; padding: 8px 10px; border: 1px solid transparent; border-radius: 10px;
111
- text-align: start; background: transparent; cursor: grab;
111
+ text-align: start; background: transparent; cursor: grab; touch-action: none;
112
112
  }
113
113
  [part=choice]:hover { border-color: var(--boe-token-stroke-stroke, #e8e8e8); background: var(--boe-token-surface-surface-hover, #f4f4f4); }
114
114
  [part=choice-icon] { grid-row: span 2; width: 20px; height: 20px; color: var(--boe-token-text-text-secondary, #6f6f6f); }
115
115
  [part=choice][data-tone=accent] [part=choice-icon] { color: var(--boe-token-surface-surface-brand, #0061d5); }
116
116
  [part=choice-icon] svg { width: 20px; height: 20px; }
117
- [part=choice-name] { font-size: 13px; font-weight: 600; line-height: 1.3; }
117
+ [part=choice-name] { font-size: 13px; font-weight: 600; line-height: 1.45; }
118
118
  [part=choice-description] { grid-column: 2; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.35; }
119
119
  [part=palette-hint] { margin: 4px; color: var(--boe-token-text-text-secondary, #6f6f6f); font-size: 12px; line-height: 1.45; }
120
120
  [part=canvas] {
@@ -149,6 +149,8 @@ export const processModelerDesign = `
149
149
  [part=box][data-shape=task] { align-content: normal; font-size: 14px; line-height: 1.45; }
150
150
  [part=box][data-shape=task] > [part=icon] { grid-row: span 4; margin-top: 1px; }
151
151
  [part=box]:hover, [part=frame]:hover { border-color: var(--boe-token-text-text-secondary, #6f6f6f); }
152
+ [data-connect-target=true][data-connect-invalid=true] { border-color: var(--boe-token-text-status-text-error, #b92340) !important; }
153
+ [part=connection-preview][data-invalid=true] { stroke: var(--boe-token-text-status-text-error, #b92340); }
152
154
  [data-connect-target=true] { border-color: var(--boe-token-surface-surface-brand, #0061d5) !important; }
153
155
  [part=box] strong, [part=frame] strong { overflow-wrap: anywhere; }
154
156
  [part=box] strong, [part=frame] strong { font-size: 13.5px; font-weight: 650; line-height: 1.3; }
@@ -172,6 +174,7 @@ export const processModelerDesign = `
172
174
  [part=frame] { z-index: 1; align-content: start; border-radius: 12px; background: color-mix(in srgb, var(--boe-token-surface-surface, #fff) 78%, transparent); }
173
175
  [part=frame] small { grid-column: 2; }
174
176
  [part=loop-mark] { position: absolute; right: 12px; bottom: 8px; font-size: 20px; line-height: 1; color: var(--boe-token-text-text-secondary, #6f6f6f); }
177
+ [part=frame][data-palette-drop=true] { border-color: var(--boe-token-surface-surface-brand, #0061d5); box-shadow: 0 0 0 2px color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 18%, transparent); }
175
178
  [part=frame-resize] { position: absolute; right: -12px; bottom: -12px; z-index: 8; width: 28px; height: 28px; min-height: 28px; padding: 0; border: 0; border-radius: 8px; background: var(--boe-token-surface-surface, #fff); box-shadow: 0 0 0 1px var(--boe-process-box-edge, #858585); cursor: nwse-resize; }
176
179
  [part=frame-resize]::before { content: ''; position: absolute; right: 7px; bottom: 7px; width: 11px; height: 11px; border-right: 2px solid var(--boe-token-text-text-secondary, #6f6f6f); border-bottom: 2px solid var(--boe-token-text-text-secondary, #6f6f6f); }
177
180
  [part=frame]:not([aria-current=true]) [part=frame-resize] { visibility: hidden; }
@@ -227,13 +230,17 @@ export const processModelerDesign = `
227
230
  [part=connect-tooltip] { position: absolute; z-index: 20; max-width: 240px; padding: 5px 8px; border-radius: 7px; background: var(--boe-token-text-text, #141413); color: var(--boe-token-surface-surface, #fff); font-size: 12px; pointer-events: none; box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }
228
231
  [part=connect-tooltip][data-invalid=true] { background: var(--boe-token-text-status-text-error, #b92340); color: #fff; }
229
232
  [part=ghost-box], [part=palette-ghost] { position: absolute; z-index: 5; display: flex; align-items: center; padding: 11px 12px; border: 1.5px dashed var(--boe-token-surface-surface-brand, #0061d5); border-radius: 8px; background: color-mix(in srgb, var(--boe-token-surface-surface-brand, #0061d5) 10%, var(--boe-token-surface-surface, #fff)); font-size: 13px; font-weight: 650; pointer-events: none; }
233
+ [part=palette-ghost][data-pointer] { position: fixed; z-index: 100; padding: 8px 12px; border: 1px solid var(--boe-token-surface-surface-brand, #0061d5); border-radius: 10px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-token-elevation-overlay, 0 8px 24px #0002); font-size: 13px; font-weight: 600; line-height: 1.45; gap: 8px; translate: -50% -50%; }
234
+ [part=palette-ghost][data-pointer] svg { width: 20px; height: 20px; }
230
235
  [part=guide] { position: absolute; background: var(--boe-token-surface-surface-brand, #0061d5); pointer-events: none; }
231
236
  [part=measure] { position: absolute; padding: 4px; background: var(--boe-token-surface-surface, #fff); font-size: 12px; pointer-events: none; }
232
237
  [part=insert-chooser] { max-width: calc(100vw - 32px); max-height: 65vh; overflow: auto; padding: 12px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); }
233
238
  [part=keyboard-chooser] { position: fixed; inset: auto; width: min(320px, calc(100vw - 24px)); max-height: min(420px, calc(100dvh - 24px)); margin: 0; overflow: auto; padding: 12px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); color: var(--boe-token-text-text, #141413); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }
234
239
  [part=chooser-title] { margin: 0 0 10px; font-size: 12px; font-weight: 650; line-height: 1.4; color: var(--boe-token-text-text-secondary, #6f6f6f); }
235
240
  [part=controls] { position: absolute; left: 16px; bottom: 16px; z-index: 10; display: flex; align-items: center; gap: 2px; padding: 4px; border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; background: var(--boe-token-surface-surface, #fff); box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }
236
- [part=controls] [data-command=reset] { min-width: 56px; font-variant-numeric: tabular-nums; }
241
+ [part=controls] button:not([data-command=reset]) { width: 32px; padding: 0; }
242
+ [part=controls-divider] { width: 1px; height: 20px; margin: 0 4px; background: var(--boe-token-stroke-stroke, #e8e8e8); }
243
+ [part=controls] [data-command=reset] { min-width: 56px; padding: 0 6px; font-variant-numeric: tabular-nums; }
237
244
  [part=minimap] { position: absolute; right: 16px; bottom: 16px; width: 208px; height: 136px; touch-action: none; background: var(--boe-token-surface-surface, #fff); border: 1px solid var(--boe-token-stroke-stroke, #e8e8e8); border-radius: 12px; box-shadow: var(--boe-shadow-overlay, 0 4px 12px rgb(0 0 0 / .16)); }
238
245
  [part=minimap] rect:not([data-viewport]) { fill: color-mix(in srgb, var(--boe-token-text-text-secondary, #6f6f6f) 35%, transparent); }
239
246
  [part=minimap] rect[data-selected=true] { fill: var(--boe-token-surface-surface-brand, #0061d5); }
@@ -342,9 +349,9 @@ export const processModelerDesign = `
342
349
  :host([data-narrow]) [part=view-switch], :host([data-narrow]) [part=run-toggle] { display: none; }
343
350
  :host([data-narrow]) [data-command=checks-status] { display: none; }
344
351
  :host([data-narrow]) [part=view-menu] { display: block; }
345
- :host([data-narrow]) [part=controls] { left: 12px; bottom: 12px; }
352
+ :host([data-hide-tidy]) [part=controls] { left: 12px; bottom: 12px; }
346
353
  :host([data-narrow]) [part=selection-toolbar] [part=selection-plus] + span { display: none; }
347
- :host([data-phone]) [data-command=tidy] { display: none; }
354
+ :host([data-hide-tidy]) [data-command=tidy] { display: none; }
348
355
  :host([data-phone]) [data-command=undo], :host([data-phone]) [data-command=redo] { display: none; }
349
356
  @media (prefers-reduced-motion: reduce) { *, *::before, *::after { transition-duration: 0s !important; animation-duration: 0s !important; scroll-behavior: auto !important; } }
350
357
  `;
@@ -141,8 +141,18 @@ export interface ProcessLastRun {
141
141
  /** Keys are projected box IDs, supplied by the host. */
142
142
  steps: Readonly<Record<string, ProcessStepMetrics>>;
143
143
  }
144
+ /** A proposed host-owned connection, also used when reattaching a line. */
145
+ export interface ProcessConnectionProposal {
146
+ readonly from: string;
147
+ readonly to: string;
148
+ /** Exclude this existing line from duplicate/outgoing-limit checks. */
149
+ readonly ignoreLineId?: string;
150
+ }
144
151
  export interface ProcessModel<D = unknown, N = unknown> {
145
152
  project(document: D): ProcessProjection<N>;
153
+ /** Pure synchronous host rule: a nonempty reason rejects preview and commit.
154
+ * Returning null permits an edit request; the host must still accept it. */
155
+ connectionProblem?(document: D, proposal: ProcessConnectionProposal, projection: ProcessProjection<N>): string | null;
146
156
  /** Optional host arrangement, used by Tidy up and for missing positions. */
147
157
  arrange?(projection: ProcessProjection<N>): ProcessLayout;
148
158
  /**
@@ -188,9 +198,17 @@ export interface ProcessCheck {
188
198
  path?: NodePath;
189
199
  }
190
200
  export interface ProcessEdit {
191
- type: "add" | "delete" | "duplicate" | "connect" | "disconnect" | "reattach" | "insert" | "reparent";
201
+ type: "add" | "delete" | "duplicate" | "connect" | "disconnect" | "reattach" | "insert" | "reparent" | "reset-line";
192
202
  boxId?: string;
193
203
  sourceId?: string;
204
+ /** Duplicate these roots and descendants/internal edges in one host transaction.
205
+ * Plural requests omit sourceId; unsupported hosts must refuse the whole group. */
206
+ sourceIds?: readonly string[];
207
+ /** One world-coordinate translation for every node in a duplicate group. */
208
+ offset?: Readonly<{
209
+ x: number;
210
+ y: number;
211
+ }>;
194
212
  from?: string;
195
213
  to?: string;
196
214
  lineId?: string;
@@ -208,11 +226,34 @@ export interface ReversibleProcessEdit {
208
226
  redo(): void;
209
227
  /** The already-applied edit's layout, recorded atomically with the document. */
210
228
  layout?: ProcessLayout;
229
+ /** Selection after acceptance; recorded with layout for undo and redo. */
230
+ selectionIds?: readonly string[];
211
231
  }
212
232
  /** Host applies the edit, then calls accept with its inverse and replay. */
213
233
  export interface ProcessEditRequest extends ProcessEdit {
214
234
  accept(command: ReversibleProcessEdit): void;
215
235
  }
236
+ /** Host-owned immutable capture; BOE never serializes the captured graph. */
237
+ export interface ProcessClipboard {
238
+ readonly itemCount: number;
239
+ paste(request: ProcessPasteRequest): void;
240
+ dispose?(): void;
241
+ }
242
+ /** Copy capture is synchronous and once-only. Unsupported hosts leave it unhandled. */
243
+ export interface ProcessCopyRequest {
244
+ readonly sourceIds: readonly string[];
245
+ capture(clipboard: ProcessClipboard): void;
246
+ refuse(message?: string): void;
247
+ }
248
+ /** Host applies one synchronous graph transaction, then accepts it once. */
249
+ export interface ProcessPasteRequest {
250
+ readonly offset: Readonly<{
251
+ x: number;
252
+ y: number;
253
+ }>;
254
+ accept(command: ReversibleProcessEdit): void;
255
+ refuse(message?: string): void;
256
+ }
216
257
  export declare function validateProjection<N>(projection: ProcessProjection<N>): void;
217
258
  /** Deterministic fallback, preserving host-authored positions. */
218
259
  export declare function completeLayout<N>(projection: ProcessProjection<N>, layout: ProcessLayout): ProcessLayout;
@@ -37,7 +37,9 @@ export declare class ProcessModeler<D = ProcessProjection, N = unknown> extends
37
37
  private activeDrawer?;
38
38
  private drawerReturn?;
39
39
  private selectedIds;
40
- private clipboardIds;
40
+ private clipboard;
41
+ private clipboardSession;
42
+ private restoringBoxFocus;
41
43
  private selectionToolbarKey;
42
44
  private sectionSequence;
43
45
  private versionValue;
@@ -62,6 +64,9 @@ export declare class ProcessModeler<D = ProcessProjection, N = unknown> extends
62
64
  private spacePressed;
63
65
  private dropLine?;
64
66
  private paletteDragKind;
67
+ private palettePointer?;
68
+ private suppressPaletteClick;
69
+ private readonly escapePalettePointer;
65
70
  private narrowValue;
66
71
  private suppressClick;
67
72
  private resizeObserver?;
@@ -148,7 +153,9 @@ export declare class ProcessModeler<D = ProcessProjection, N = unknown> extends
148
153
  selectLine(id: string | null): void;
149
154
  setValidation(checks: readonly ProcessCheck[]): void;
150
155
  setValidationAtPath(message: string, path: NodePath): void;
156
+ private connectionProblem;
151
157
  requestEdit(edit: ProcessEdit): void;
158
+ private editAcceptance;
152
159
  undo(): void;
153
160
  redo(): void;
154
161
  private commitLayout;
@@ -184,7 +191,13 @@ export declare class ProcessModeler<D = ProcessProjection, N = unknown> extends
184
191
  arrangeSelection(action: 'left' | 'center' | 'right' | 'top' | 'middle' | 'bottom' | 'distribute-horizontal' | 'distribute-vertical'): void;
185
192
  makeSection(title?: string): void;
186
193
  protected setupListeners(): void;
194
+ private clearClipboard;
195
+ private copySelection;
196
+ private pasteClipboard;
197
+ private duplicateBoxes;
187
198
  private onKey;
199
+ private navigateSelection;
200
+ private revealBox;
188
201
  private pointerDown;
189
202
  private distance;
190
203
  private pointerMove;
@@ -194,6 +207,7 @@ export declare class ProcessModeler<D = ProcessProjection, N = unknown> extends
194
207
  private sideAt;
195
208
  private lineAt;
196
209
  private markDropLine;
210
+ private paintDropLine;
197
211
  private alignedPoint;
198
212
  private openChooser;
199
213
  private nextEdit;
@@ -211,6 +225,14 @@ export declare class ProcessModeler<D = ProcessProjection, N = unknown> extends
211
225
  private collectChecks;
212
226
  private renderWorld;
213
227
  private place;
228
+ private activatePaletteKind;
229
+ private paletteLineAt;
230
+ private paletteFrameAt;
231
+ private markPaletteDrop;
232
+ private paletteCanvasPoint;
233
+ private movePalettePointer;
234
+ private cancelPalettePointer;
235
+ private finishPalettePointer;
214
236
  private renderPalette;
215
237
  private renderChecks;
216
238
  private renderField;
@@ -74,7 +74,9 @@ export class ProcessModeler extends BaseElement {
74
74
  activeDrawer;
75
75
  drawerReturn;
76
76
  selectedIds = new Set();
77
- clipboardIds = [];
77
+ clipboard = null;
78
+ clipboardSession = 0;
79
+ restoringBoxFocus = false;
78
80
  selectionToolbarKey = "";
79
81
  sectionSequence = 0;
80
82
  versionValue = 0;
@@ -99,6 +101,14 @@ export class ProcessModeler extends BaseElement {
99
101
  spacePressed = false;
100
102
  dropLine;
101
103
  paletteDragKind = null;
104
+ palettePointer;
105
+ suppressPaletteClick = false;
106
+ escapePalettePointer = (event) => {
107
+ if (event.key !== 'Escape' || !this.palettePointer)
108
+ return;
109
+ this.cancelPalettePointer();
110
+ event.preventDefault();
111
+ };
102
112
  narrowValue = false;
103
113
  suppressClick = false;
104
114
  resizeObserver;
@@ -157,6 +167,7 @@ export class ProcessModeler extends BaseElement {
157
167
  };
158
168
  }
159
169
  load(document, options = {}) {
170
+ this.cancelPalettePointer();
160
171
  const projection = this.model.project(document);
161
172
  validateProjection(projection);
162
173
  this.documentValue = document;
@@ -170,7 +181,7 @@ export class ProcessModeler extends BaseElement {
170
181
  this.selectedId = null;
171
182
  this.selectedLineId = null;
172
183
  this.selectedIds.clear();
173
- this.clipboardIds = [];
184
+ this.clearClipboard();
174
185
  this.refresh();
175
186
  if (options.selectedPath)
176
187
  this.selectedPath = options.selectedPath;
@@ -259,6 +270,8 @@ export class ProcessModeler extends BaseElement {
259
270
  }
260
271
  set locked(value) {
261
272
  this.toggleAttribute("locked", value);
273
+ if (value)
274
+ this.cancelPalettePointer();
262
275
  }
263
276
  get snapToGrid() {
264
277
  return this.hasAttribute("snap-to-grid");
@@ -281,6 +294,8 @@ export class ProcessModeler extends BaseElement {
281
294
  }
282
295
  disconnectedCallback() {
283
296
  this.resizeObserver?.disconnect();
297
+ this.clearClipboard();
298
+ this.cancelPalettePointer();
284
299
  this.closeDrawer();
285
300
  const chooser = this.shadowRoot?.querySelector("[part=insert-chooser]");
286
301
  if (chooser)
@@ -341,56 +356,85 @@ export class ProcessModeler extends BaseElement {
341
356
  setValidationAtPath(message, path) {
342
357
  this.setValidation(message ? [{ message, path }] : []);
343
358
  }
359
+ connectionProblem(edit) {
360
+ if (!this.model.connectionProblem || this.documentValue === undefined || !['connect', 'reattach'].includes(edit.type))
361
+ return null;
362
+ if (!this.isRendered) {
363
+ this.projection = this.model.project(this.documentValue);
364
+ validateProjection(this.projection);
365
+ }
366
+ const line = edit.type === 'reattach' ? this.projection.lines.find(line => line.id === edit.lineId) : undefined;
367
+ const from = edit.from ?? line?.from;
368
+ const to = edit.to ?? line?.to;
369
+ if (!from || !to)
370
+ return null;
371
+ return this.model.connectionProblem(this.documentValue, { from, to, ...(line ? { ignoreLineId: line.id } : {}) }, this.projection) || null;
372
+ }
344
373
  requestEdit(edit) {
345
374
  if (this.locked || (this.disableConnections && (edit.type === "connect" || edit.type === "disconnect" || edit.type === 'reattach')))
346
375
  return;
376
+ const problem = this.connectionProblem(edit);
377
+ if (problem) {
378
+ this.setStatus(problem, true);
379
+ return;
380
+ }
381
+ const request = { ...edit, accept: this.editAcceptance(edit) };
382
+ emit(this, "process-edit-request", request);
383
+ }
384
+ editAcceptance(edit) {
347
385
  const before = this.layout;
386
+ const beforeSelection = [...this.selectedIds];
348
387
  const previousIds = new Set(this.projection.boxes.map((box) => box.id));
349
388
  let accepted = false;
350
- const request = {
351
- ...edit,
352
- accept: (command) => {
353
- if (accepted)
354
- return;
355
- accepted = true;
356
- if (command.layout)
357
- this.layoutValue = structuredClone(command.layout);
358
- this.refresh();
359
- const inserted = edit.type === 'insert' && !edit.boxId && !command.layout
360
- ? this.projection.boxes.find(box => !previousIds.has(box.id)) : undefined;
361
- const reflowed = inserted ? this.reflowStraightInsert(edit, inserted.id, before) : false;
362
- if (!reflowed &&
363
- !command.layout &&
364
- edit.position &&
365
- Number.isFinite(edit.position.x) &&
366
- Number.isFinite(edit.position.y)) {
367
- const added = this.projection.boxes.find(box => box.id === edit.boxId) ?? this.projection.boxes.find((box) => !previousIds.has(box.id));
368
- if (added)
369
- this.layoutValue = this.translatedLayout(added.id, edit.position.x, edit.position.y);
370
- }
371
- const after = this.layout;
372
- const restore = (layout) => {
373
- this.layoutValue = structuredClone(layout);
374
- this.refresh();
375
- this.emitPositions();
376
- };
377
- this.history.record({
378
- undo: () => {
379
- command.undo();
380
- restore(before);
381
- },
382
- redo: () => {
383
- command.redo();
384
- restore(after);
385
- },
386
- });
389
+ return (command) => {
390
+ if (accepted)
391
+ return;
392
+ accepted = true;
393
+ if (command.layout)
394
+ this.layoutValue = structuredClone(command.layout);
395
+ if (edit.type === 'reset-line' && edit.lineId)
396
+ delete this.layoutValue.lines?.[edit.lineId];
397
+ this.refresh();
398
+ const inserted = edit.type === 'insert' && !edit.boxId && !command.layout
399
+ ? this.projection.boxes.find(box => !previousIds.has(box.id)) : undefined;
400
+ const reflowed = inserted ? this.reflowStraightInsert(edit, inserted.id, before) : false;
401
+ if (!reflowed &&
402
+ !command.layout &&
403
+ edit.position &&
404
+ Number.isFinite(edit.position.x) &&
405
+ Number.isFinite(edit.position.y)) {
406
+ const added = this.projection.boxes.find(box => box.id === edit.boxId) ?? this.projection.boxes.find((box) => !previousIds.has(box.id));
407
+ if (added)
408
+ this.layoutValue = this.translatedLayout(added.id, edit.position.x, edit.position.y);
409
+ }
410
+ const after = this.layout;
411
+ const acceptedSelection = command.selectionIds ? [...command.selectionIds] : undefined;
412
+ const restore = (layout) => {
413
+ this.layoutValue = structuredClone(layout);
387
414
  this.refresh();
388
- if (JSON.stringify(before) !== JSON.stringify(after))
389
- this.emitPositions();
390
- announce("Process updated", "polite", this.ownerDocument);
391
- },
415
+ this.emitPositions();
416
+ };
417
+ this.history.record({
418
+ undo: () => {
419
+ command.undo();
420
+ restore(before);
421
+ if (acceptedSelection)
422
+ this.selectMany(beforeSelection);
423
+ },
424
+ redo: () => {
425
+ command.redo();
426
+ restore(after);
427
+ if (acceptedSelection)
428
+ this.selectMany(acceptedSelection);
429
+ },
430
+ });
431
+ if (acceptedSelection)
432
+ this.selectMany(acceptedSelection);
433
+ this.refresh();
434
+ if (JSON.stringify(before) !== JSON.stringify(after))
435
+ this.emitPositions();
436
+ announce("Process updated", "polite", this.ownerDocument);
392
437
  };
393
- emit(this, "process-edit-request", request);
394
438
  }
395
439
  undo() {
396
440
  if (this.locked)
@@ -541,6 +585,13 @@ export class ProcessModeler extends BaseElement {
541
585
  });
542
586
  }
543
587
  resetLine(id) {
588
+ const line = this.projection.lines.find(line => line.id === id);
589
+ if (this.locked || !line)
590
+ return;
591
+ if (line.fromSide || line.toSide || line.points?.length) {
592
+ this.requestEdit({ type: 'reset-line', lineId: id });
593
+ return;
594
+ }
544
595
  if (!this.layoutValue.lines?.[id])
545
596
  return;
546
597
  const next = this.layout;
@@ -763,7 +814,7 @@ export class ProcessModeler extends BaseElement {
763
814
  palette.prepend(skip, paletteHeading);
764
815
  const hint = document.createElement("p");
765
816
  hint.setAttribute("part", "palette-hint");
766
- hint.textContent = "Drag onto the canvas, a connection, or a frame. Select a step first to add after it.";
817
+ hint.textContent = "Drag onto the canvas, onto a line to insert, or into a frame. Choosing one with a step selected adds it after that step. Or click a dot beside any step to add one that way.";
767
818
  palette.append(hint);
768
819
  for (const [part, label] of [["palette", "Building blocks"], ["inspector", "Process details"]]) {
769
820
  const pane = root.querySelector(`[part=${part}]`);
@@ -864,6 +915,12 @@ export class ProcessModeler extends BaseElement {
864
915
  controls.setAttribute("role", "toolbar");
865
916
  controls.setAttribute("aria-label", "Canvas view controls");
866
917
  for (const command of ["zoom-out", "reset", "zoom-in", "fit", "snap", "lock"]) {
918
+ if (command === 'snap') {
919
+ const divider = document.createElement('span');
920
+ divider.setAttribute('part', 'controls-divider');
921
+ divider.setAttribute('aria-hidden', 'true');
922
+ controls.append(divider);
923
+ }
867
924
  const button = root.querySelector(`[data-command=${command}]`);
868
925
  if (button)
869
926
  controls.append(button);
@@ -896,6 +953,7 @@ export class ProcessModeler extends BaseElement {
896
953
  this.narrowValue = narrow;
897
954
  this.toggleAttribute("data-narrow", narrow);
898
955
  this.toggleAttribute('data-phone', entries[0].contentRect.width < 560);
956
+ this.toggleAttribute('data-hide-tidy', entries[0].contentRect.width <= 560);
899
957
  this.renderSelectionToolbar();
900
958
  this.positionSelectionToolbar();
901
959
  if (!narrow)
@@ -1326,6 +1384,16 @@ export class ProcessModeler extends BaseElement {
1326
1384
  this.setSection({ id, title, x, y, width: right - x, height: bottom - y });
1327
1385
  }
1328
1386
  setupListeners() {
1387
+ this.addEventListener('pointermove', event => this.movePalettePointer(event));
1388
+ this.addEventListener('pointerup', event => this.finishPalettePointer(event));
1389
+ this.addEventListener('pointercancel', event => { if (event.pointerId === this.palettePointer?.id)
1390
+ this.cancelPalettePointer(); });
1391
+ this.addEventListener('lostpointercapture', event => { if (event.pointerId === this.palettePointer?.id)
1392
+ this.cancelPalettePointer(); });
1393
+ this.shadowRoot.addEventListener('keydown', event => { if (event.key === 'Escape' && this.palettePointer) {
1394
+ this.cancelPalettePointer();
1395
+ event.preventDefault();
1396
+ } }, true);
1329
1397
  this.shadowRoot.addEventListener("click", (event) => {
1330
1398
  const command = event.target.closest("button")?.dataset.command;
1331
1399
  if (command === "zoom-in")
@@ -1455,6 +1523,92 @@ export class ProcessModeler extends BaseElement {
1455
1523
  this.paintViewport();
1456
1524
  });
1457
1525
  }
1526
+ clearClipboard() {
1527
+ const previous = this.clipboard;
1528
+ this.clipboard = null;
1529
+ this.clipboardSession++;
1530
+ previous?.dispose?.();
1531
+ }
1532
+ copySelection() {
1533
+ this.clearClipboard();
1534
+ const session = this.clipboardSession;
1535
+ let active = true;
1536
+ let claimed = false;
1537
+ const request = {
1538
+ sourceIds: Object.freeze([...this.selectedIds]),
1539
+ capture: clipboard => {
1540
+ if (!active || claimed || session !== this.clipboardSession || !this.isConnected)
1541
+ return;
1542
+ claimed = true;
1543
+ if (!Number.isInteger(clipboard?.itemCount) || clipboard.itemCount < 1 || typeof clipboard.paste !== 'function' || (clipboard.dispose !== undefined && typeof clipboard.dispose !== 'function')) {
1544
+ this.setStatus('The host did not supply a valid clipboard');
1545
+ return;
1546
+ }
1547
+ this.clipboard = Object.freeze({ itemCount: clipboard.itemCount, paste: clipboard.paste.bind(clipboard), ...(clipboard.dispose ? { dispose: clipboard.dispose.bind(clipboard) } : {}) });
1548
+ this.setStatus(`${clipboard.itemCount} steps copied`);
1549
+ },
1550
+ refuse: message => { if (!active || claimed || session !== this.clipboardSession || !this.isConnected)
1551
+ return; claimed = true; this.setStatus(message ?? 'Copy is not supported by this host'); },
1552
+ };
1553
+ try {
1554
+ emit(this, 'process-copy-request', request);
1555
+ }
1556
+ finally {
1557
+ active = false;
1558
+ }
1559
+ if (!claimed && session === this.clipboardSession && this.isConnected)
1560
+ this.setStatus('Copy is not supported by this host');
1561
+ return claimed;
1562
+ }
1563
+ pasteClipboard() {
1564
+ const clipboard = this.clipboard;
1565
+ if (!clipboard || this.locked)
1566
+ return;
1567
+ const session = this.clipboardSession;
1568
+ const accept = this.editAcceptance({ type: 'duplicate' });
1569
+ let active = true;
1570
+ let settled = false;
1571
+ const request = {
1572
+ offset: Object.freeze({ x: 32, y: 32 }),
1573
+ accept: command => {
1574
+ if (!active || settled || session !== this.clipboardSession || this.locked)
1575
+ return;
1576
+ settled = true;
1577
+ accept(command);
1578
+ },
1579
+ refuse: message => { if (!active || settled || session !== this.clipboardSession || !this.isConnected)
1580
+ return; settled = true; this.setStatus(message ?? 'Paste was refused by this host'); },
1581
+ };
1582
+ try {
1583
+ clipboard.paste(request);
1584
+ }
1585
+ finally {
1586
+ active = false;
1587
+ }
1588
+ if (!settled && session === this.clipboardSession)
1589
+ this.setStatus('Paste was not accepted by this host');
1590
+ }
1591
+ duplicateBoxes(ids, distance) {
1592
+ const selected = new Set(ids);
1593
+ if (!selected.size || [...selected].some(id => !this.projection.boxes.some(box => box.id === id) || !this.layoutValue.boxes[id]))
1594
+ return;
1595
+ const sources = [...selected].filter(id => {
1596
+ let parent = this.projection.boxes.find(box => box.id === id)?.parentId;
1597
+ while (parent) {
1598
+ if (selected.has(parent))
1599
+ return false;
1600
+ parent = this.projection.boxes.find(box => box.id === parent)?.parentId;
1601
+ }
1602
+ return true;
1603
+ });
1604
+ if (sources.length === 1) {
1605
+ const position = this.layoutValue.boxes[sources[0]];
1606
+ this.requestEdit({ type: 'duplicate', sourceId: sources[0], position: { x: position.x + distance, y: position.y + distance } });
1607
+ }
1608
+ else {
1609
+ this.requestEdit({ type: 'duplicate', sourceIds: sources, offset: { x: distance, y: distance } });
1610
+ }
1611
+ }
1458
1612
  onKey(event) {
1459
1613
  if (event.defaultPrevented)
1460
1614
  return;
@@ -1483,20 +1637,18 @@ export class ProcessModeler extends BaseElement {
1483
1637
  return;
1484
1638
  }
1485
1639
  if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'c' && this.selectedIds.size) {
1486
- event.preventDefault();
1487
- this.clipboardIds = [...this.selectedIds];
1488
- this.setStatus(`${this.clipboardIds.length} steps copied`);
1640
+ if (this.copySelection())
1641
+ event.preventDefault();
1489
1642
  return;
1490
1643
  }
1491
- if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'v' && this.clipboardIds.length && !this.locked) {
1644
+ if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'v' && this.clipboard && !this.locked) {
1492
1645
  event.preventDefault();
1493
- this.clipboardIds.forEach((id, index) => { const position = this.layoutValue.boxes[id]; if (position)
1494
- this.requestEdit({ type: 'duplicate', sourceId: id, position: { x: position.x + 32 * (index + 1), y: position.y + 32 * (index + 1) } }); });
1646
+ this.pasteClipboard();
1495
1647
  return;
1496
1648
  }
1497
1649
  if ((event.ctrlKey || event.metaKey) && event.key.toLowerCase() === 'd' && this.selectedIds.size && !this.locked) {
1498
1650
  event.preventDefault();
1499
- [...this.selectedIds].forEach(id => { const position = this.layoutValue.boxes[id]; this.requestEdit({ type: 'duplicate', sourceId: id, position: { x: position.x + 32, y: position.y + 32 } }); });
1651
+ this.duplicateBoxes([...this.selectedIds], 48);
1500
1652
  return;
1501
1653
  }
1502
1654
  if (event.key === "Escape") {
@@ -1506,6 +1658,7 @@ export class ProcessModeler extends BaseElement {
1506
1658
  if (this.selectedLineId)
1507
1659
  this.selectLine(null);
1508
1660
  this.portDrag = undefined;
1661
+ this.endDrag = undefined;
1509
1662
  this.marquee = undefined;
1510
1663
  this.segmentDrag = undefined;
1511
1664
  this.drag = undefined;
@@ -1532,6 +1685,18 @@ export class ProcessModeler extends BaseElement {
1532
1685
  this.openKeyboardChooser(box);
1533
1686
  return;
1534
1687
  }
1688
+ const direction = {
1689
+ ArrowLeft: [-1, 0],
1690
+ ArrowRight: [1, 0],
1691
+ ArrowUp: [0, -1],
1692
+ ArrowDown: [0, 1],
1693
+ }[event.key];
1694
+ const canInsertDirection = event.ctrlKey && event.altKey && box && !this.locked && this.catalog.length && !this.disableConnections;
1695
+ if (direction && !canInsertDirection && (event.altKey || !box || this.locked)) {
1696
+ event.preventDefault();
1697
+ this.navigateSelection(direction);
1698
+ return;
1699
+ }
1535
1700
  if (!box || this.locked)
1536
1701
  return;
1537
1702
  if (event.key === "Enter") {
@@ -1560,12 +1725,6 @@ export class ProcessModeler extends BaseElement {
1560
1725
  event.preventDefault();
1561
1726
  this.selectionCommand("delete-many");
1562
1727
  }
1563
- const direction = {
1564
- ArrowLeft: [-1, 0],
1565
- ArrowRight: [1, 0],
1566
- ArrowUp: [0, -1],
1567
- ArrowDown: [0, 1],
1568
- }[event.key];
1569
1728
  if (direction) {
1570
1729
  event.preventDefault();
1571
1730
  if (event.ctrlKey && event.altKey && this.catalog.length && !this.disableConnections) {
@@ -1578,31 +1737,52 @@ export class ProcessModeler extends BaseElement {
1578
1737
  this.openAnchoredChooser(this.nextEdit(box, side, { x: pos.x + direction[0] * 320, y: pos.y + direction[1] * 170 }), this.nextChooserTitle(box, side), anchor, returnFocus);
1579
1738
  return;
1580
1739
  }
1581
- if (event.altKey) {
1582
- const origin = this.layoutValue.boxes[box.id];
1583
- const center = { x: origin.x + (origin.width ?? 224) / 2, y: origin.y + (origin.height ?? 64) / 2 };
1584
- const candidates = this.projection.boxes.filter(candidate => candidate.id !== box.id).map(candidate => {
1585
- const position = this.layoutValue.boxes[candidate.id];
1586
- const dx = position.x + (position.width ?? 224) / 2 - center.x;
1587
- const dy = position.y + (position.height ?? 64) / 2 - center.y;
1588
- const forward = dx * direction[0] + dy * direction[1];
1589
- const sideways = Math.abs(dx * direction[1] - dy * direction[0]);
1590
- return { candidate, forward, score: forward + sideways * 2 };
1591
- }).filter(item => item.forward > 0).sort((a, b) => a.score - b.score || a.candidate.id.localeCompare(b.candidate.id));
1592
- const next = candidates[0]?.candidate;
1593
- if (next) {
1594
- this.select(next.id);
1595
- this.focusBox(next.id);
1596
- this.setStatus(`${next.title} selected`);
1597
- }
1598
- return;
1599
- }
1600
1740
  const pos = this.layoutValue.boxes[box.id];
1601
1741
  const distance = event.shiftKey ? 64 : 16;
1602
1742
  this.move(box.id, pos.x + direction[0] * distance, pos.y + direction[1] * distance);
1603
1743
  this.focusBox(box.id);
1604
1744
  }
1605
1745
  }
1746
+ navigateSelection(direction) {
1747
+ const canvas = this.shadowRoot.querySelector('[part=canvas]');
1748
+ const box = this.selected;
1749
+ const origin = box ? this.layoutValue.boxes[box.id] : undefined;
1750
+ const originSize = box ? this.selectionDimensions(box) : undefined;
1751
+ const center = origin && originSize
1752
+ ? { x: origin.x + originSize.width / 2, y: origin.y + originSize.height / 2 }
1753
+ : { x: (canvas.clientWidth / 2 - this.viewport.x) / this.viewport.zoom, y: (canvas.clientHeight / 2 - this.viewport.y) / this.viewport.zoom };
1754
+ const candidates = this.projection.boxes.filter(candidate => candidate.id !== box?.id && candidate.kind !== 'section').map(candidate => {
1755
+ const position = this.layoutValue.boxes[candidate.id];
1756
+ const size = this.selectionDimensions(candidate);
1757
+ const dx = position.x + size.width / 2 - center.x;
1758
+ const dy = position.y + size.height / 2 - center.y;
1759
+ const forward = dx * direction[0] + dy * direction[1];
1760
+ const sideways = Math.abs(dx * direction[1] - dy * direction[0]);
1761
+ return { candidate, forward, score: forward + sideways * 2.5 };
1762
+ }).filter(item => item.forward > 4).sort((a, b) => a.score - b.score);
1763
+ const next = candidates[0]?.candidate;
1764
+ if (!next) {
1765
+ this.setStatus('Nothing further that way');
1766
+ return;
1767
+ }
1768
+ this.select(next.id);
1769
+ this.revealBox(next);
1770
+ this.focusBox(next.id);
1771
+ this.setStatus(`${next.title} selected`);
1772
+ }
1773
+ revealBox(box) {
1774
+ const canvas = this.shadowRoot.querySelector('[part=canvas]');
1775
+ if (!canvas.clientWidth || !canvas.clientHeight)
1776
+ return;
1777
+ const position = this.layoutValue.boxes[box.id];
1778
+ const size = this.selectionDimensions(box);
1779
+ const left = position.x * this.viewport.zoom + this.viewport.x;
1780
+ const top = position.y * this.viewport.zoom + this.viewport.y;
1781
+ if (left > 48 && top > 48 && left + size.width * this.viewport.zoom < canvas.clientWidth - 48 && top + size.height * this.viewport.zoom < canvas.clientHeight - 48)
1782
+ return;
1783
+ this.setView({ x: canvas.clientWidth / 2 - (position.x + size.width / 2) * this.viewport.zoom,
1784
+ y: canvas.clientHeight / 2 - (position.y + size.height / 2) * this.viewport.zoom, zoom: this.viewport.zoom });
1785
+ }
1606
1786
  pointerDown(event) {
1607
1787
  if (event.button !== 0)
1608
1788
  return;
@@ -1706,10 +1886,12 @@ export class ProcessModeler extends BaseElement {
1706
1886
  const start = this.portDrag.start;
1707
1887
  ghost.setAttribute("points", `${start.x},${start.y} ${point.x},${start.y} ${point.x},${point.y}`);
1708
1888
  const target = this.boxAt(point, this.portDrag.id);
1709
- const pinned = target ? this.sideAt(target, point) : undefined;
1710
- this.shadowRoot.querySelectorAll('[data-box-id]').forEach(box => { box.dataset.connectTarget = String(box.dataset.boxId === target?.id); });
1889
+ const problem = target ? this.connectionProblem({ type: 'connect', from: this.portDrag.id, to: target.id }) : null;
1890
+ const pinned = target && !problem ? this.sideAt(target, point) : undefined;
1891
+ ghost.dataset.invalid = String(Boolean(problem));
1892
+ this.shadowRoot.querySelectorAll('[data-box-id]').forEach(box => { box.dataset.connectTarget = String(box.dataset.boxId === target?.id); box.dataset.connectInvalid = String(box.dataset.boxId === target?.id && Boolean(problem)); });
1711
1893
  this.shadowRoot.querySelectorAll('[part=port]').forEach(port => { port.dataset.hot = String(port.dataset.owner === target?.id && port.dataset.side === pinned); });
1712
- this.showConnectTooltip(point, target ? pinned ? `Pin to the ${pinned} of ${target.title}` : `Connect to ${target.title}` : 'Drop here to add a connected step');
1894
+ this.showConnectTooltip(point, problem ?? (target ? pinned ? `Pin to the ${pinned} of ${target.title}` : `Connect to ${target.title}` : 'Drop here to add a connected step'), Boolean(problem));
1713
1895
  return;
1714
1896
  }
1715
1897
  if (this.endDrag) {
@@ -1725,11 +1907,13 @@ export class ProcessModeler extends BaseElement {
1725
1907
  if (fixed)
1726
1908
  ghost.setAttribute('points', `${fixed.x},${fixed.y} ${point.x},${fixed.y} ${point.x},${point.y}`);
1727
1909
  const target = this.boxAt(point);
1728
- const pinned = target ? this.sideAt(target, point) : undefined;
1729
- this.shadowRoot.querySelectorAll('[data-box-id]').forEach(box => { box.dataset.connectTarget = String(box.dataset.boxId === target?.id); });
1910
+ const problem = target ? this.connectionProblem({ type: 'reattach', lineId: this.endDrag.lineId, [this.endDrag.end]: target.id }) : null;
1911
+ const pinned = target && !problem ? this.sideAt(target, point) : undefined;
1912
+ ghost.dataset.invalid = String(Boolean(problem) || !target);
1913
+ this.shadowRoot.querySelectorAll('[data-box-id]').forEach(box => { box.dataset.connectTarget = String(box.dataset.boxId === target?.id); box.dataset.connectInvalid = String(box.dataset.boxId === target?.id && Boolean(problem)); });
1730
1914
  this.shadowRoot.querySelectorAll('[part=port]').forEach(port => { port.dataset.hot = String(port.dataset.owner === target?.id && port.dataset.side === pinned); });
1731
- this.showConnectTooltip(point, target ? pinned ? `Pin to the ${pinned} of ${target.title}` : `Attach to ${target.title}` : 'Drop on a step to reattach', !target);
1732
- this.setStatus(target ? `Release to attach the connection to ${target.title}` : 'Move over a step to attach this connection');
1915
+ this.showConnectTooltip(point, problem ?? (target ? pinned ? `Pin to the ${pinned} of ${target.title}` : `Attach to ${target.title}` : 'Drop on a step to reattach'), Boolean(problem) || !target);
1916
+ this.setStatus(problem ?? (target ? `Release to attach the connection to ${target.title}` : 'Move over a step to attach this connection'));
1733
1917
  return;
1734
1918
  }
1735
1919
  if (this.segmentDrag) {
@@ -1828,7 +2012,7 @@ export class ProcessModeler extends BaseElement {
1828
2012
  this.portDrag = undefined;
1829
2013
  this.shadowRoot.querySelector('[part=connection-preview]')?.remove();
1830
2014
  this.shadowRoot.querySelector('[part=connect-tooltip]')?.remove();
1831
- this.shadowRoot.querySelectorAll('[data-connect-target]').forEach(box => { delete box.dataset.connectTarget; });
2015
+ this.shadowRoot.querySelectorAll('[data-connect-target]').forEach(box => { delete box.dataset.connectTarget; delete box.dataset.connectInvalid; });
1832
2016
  this.shadowRoot.querySelectorAll('[part=port]').forEach(port => { delete port.dataset.hot; });
1833
2017
  if (commit) {
1834
2018
  if (Math.hypot(point.x - port.start.x, point.y - port.start.y) < 8) {
@@ -1860,7 +2044,7 @@ export class ProcessModeler extends BaseElement {
1860
2044
  this.endDrag = undefined;
1861
2045
  this.shadowRoot.querySelector('[part=connection-preview]')?.remove();
1862
2046
  this.shadowRoot.querySelector('[part=connect-tooltip]')?.remove();
1863
- this.shadowRoot.querySelectorAll('[data-connect-target]').forEach(box => { delete box.dataset.connectTarget; });
2047
+ this.shadowRoot.querySelectorAll('[data-connect-target]').forEach(box => { delete box.dataset.connectTarget; delete box.dataset.connectInvalid; });
1864
2048
  this.shadowRoot.querySelectorAll('[part=port]').forEach(port => { delete port.dataset.hot; });
1865
2049
  if (commit) {
1866
2050
  if (Math.hypot(point.x - drag.start.x, point.y - drag.start.y) < 8) {
@@ -1976,6 +2160,9 @@ export class ProcessModeler extends BaseElement {
1976
2160
  }
1977
2161
  markDropLine(point, except) {
1978
2162
  this.dropLine = point ? this.lineAt(point, except)?.id : undefined;
2163
+ this.paintDropLine();
2164
+ }
2165
+ paintDropLine() {
1979
2166
  this.shadowRoot.querySelectorAll('[part=line]').forEach(path => { const active = path.dataset.lineId === this.dropLine; path.dataset.drop = String(active); path.setAttribute('marker-end', `url(#${active || path.dataset.selected === 'true' ? 'boe-process-arrow-brand' : 'boe-process-arrow'})`); });
1980
2167
  }
1981
2168
  alignedPoint(id, x, y) {
@@ -2192,8 +2379,15 @@ export class ProcessModeler extends BaseElement {
2192
2379
  this.updateToolbar();
2193
2380
  if (selectionBefore !== JSON.stringify({ ids: [...this.selectedIds], path: this.selectedPath }))
2194
2381
  emit(this, "selection-changed", { box: this.selected, boxes: this.selectedBoxes, path: this.selectedPath });
2195
- if (focused)
2196
- this.focusBox(focused);
2382
+ if (focused) {
2383
+ this.restoringBoxFocus = true;
2384
+ try {
2385
+ this.focusBox(focused);
2386
+ }
2387
+ finally {
2388
+ this.restoringBoxFocus = false;
2389
+ }
2390
+ }
2197
2391
  }
2198
2392
  get allChecks() { return this.computedChecks; }
2199
2393
  collectChecks() {
@@ -2222,6 +2416,13 @@ export class ProcessModeler extends BaseElement {
2222
2416
  for (const box of [...this.projection.boxes].sort((a, b) => Number(Boolean(b.frame)) - Number(Boolean(a.frame)))) {
2223
2417
  const element = document.createElement("div");
2224
2418
  element.tabIndex = 0;
2419
+ element.addEventListener('focus', () => {
2420
+ if (!this.restoringBoxFocus && !this.pointers.size && !this.portDrag && !this.endDrag && !this.segmentDrag && !this.marquee && !this.frameResize) {
2421
+ this.select(box.id);
2422
+ if (box.kind !== 'section')
2423
+ this.revealBox(box);
2424
+ }
2425
+ });
2225
2426
  element.setAttribute("role", "group");
2226
2427
  element.setAttribute("part", box.frame ? "frame" : "box");
2227
2428
  element.dataset.boxId = box.id;
@@ -2394,6 +2595,7 @@ export class ProcessModeler extends BaseElement {
2394
2595
  for (const side of ["north", "east", "south", "west"]) {
2395
2596
  const port = document.createElement("button");
2396
2597
  port.type = "button";
2598
+ port.tabIndex = -1;
2397
2599
  port.setAttribute("part", "port");
2398
2600
  port.dataset.side = side;
2399
2601
  port.dataset.owner = box.id;
@@ -2599,6 +2801,145 @@ export class ProcessModeler extends BaseElement {
2599
2801
  height: `${position.height ?? 64}px`,
2600
2802
  });
2601
2803
  }
2804
+ activatePaletteKind(kind) {
2805
+ if (this.locked)
2806
+ return;
2807
+ if (this.narrowValue) {
2808
+ this.closeDrawer();
2809
+ this.shadowRoot.querySelector('[part=canvas]').focus({ preventScroll: true });
2810
+ }
2811
+ if (kind.placement) {
2812
+ this.addLayoutKind(kind);
2813
+ return;
2814
+ }
2815
+ const selected = this.selectedIds.size === 1 ? this.selected : null;
2816
+ const from = selected && !selected.frame && selected.kind !== 'section' && !['end', 'finish'].includes(selected.kind) ? selected.id : undefined;
2817
+ const outgoing = from ? this.projection.lines.filter(line => line.from === from) : [];
2818
+ const line = outgoing.length === 1 ? outgoing[0] : undefined;
2819
+ this.requestEdit(line ? { type: 'insert', kind, lineId: line.id, from: line.from, to: line.to } : { type: 'add', kind, ...(from ? { from } : {}) });
2820
+ }
2821
+ paletteLineAt(point) {
2822
+ let nearest;
2823
+ let best = 20 / this.viewport.zoom;
2824
+ for (const line of this.projection.lines) {
2825
+ const points = this.routedLines.get(line.id) ?? [];
2826
+ for (let i = 1; i < points.length; i++) {
2827
+ const a = points[i - 1], b = points[i], dx = b.x - a.x, dy = b.y - a.y, length = dx * dx + dy * dy;
2828
+ const t = length ? Math.max(0, Math.min(1, ((point.x - a.x) * dx + (point.y - a.y) * dy) / length)) : 0;
2829
+ const distance = Math.hypot(point.x - a.x - t * dx, point.y - a.y - t * dy);
2830
+ if (distance < best) {
2831
+ best = distance;
2832
+ nearest = line;
2833
+ }
2834
+ }
2835
+ }
2836
+ return nearest;
2837
+ }
2838
+ paletteFrameAt(point) {
2839
+ let nearest;
2840
+ let depth = -1;
2841
+ for (const box of this.projection.boxes) {
2842
+ if (!box.frame)
2843
+ continue;
2844
+ const p = this.layoutValue.boxes[box.id];
2845
+ if (!p)
2846
+ continue;
2847
+ const { width, height } = this.selectionDimensions(box);
2848
+ if (point.x < p.x || point.x > p.x + width || point.y < p.y || point.y > p.y + height)
2849
+ continue;
2850
+ let level = 0, parent = box.parentId;
2851
+ while (parent) {
2852
+ level++;
2853
+ parent = this.projection.boxes.find(box => box.id === parent)?.parentId;
2854
+ }
2855
+ if (level > depth) {
2856
+ depth = level;
2857
+ nearest = box;
2858
+ }
2859
+ }
2860
+ return nearest;
2861
+ }
2862
+ markPaletteDrop(point) {
2863
+ this.dropLine = point ? this.paletteLineAt(point)?.id : undefined;
2864
+ const frame = point && !this.dropLine ? this.paletteFrameAt(point) : undefined;
2865
+ this.shadowRoot.querySelectorAll('[part=frame]').forEach(element => { element.dataset.paletteDrop = String(element.dataset.boxId === frame?.id); });
2866
+ this.paintDropLine();
2867
+ }
2868
+ paletteCanvasPoint(event) {
2869
+ const rect = this.shadowRoot.querySelector('[part=canvas]').getBoundingClientRect();
2870
+ if (event.clientX <= rect.left || event.clientX >= rect.right || event.clientY <= rect.top || event.clientY >= rect.bottom)
2871
+ return;
2872
+ return this.canvasPoint(event);
2873
+ }
2874
+ movePalettePointer(event) {
2875
+ const drag = this.palettePointer;
2876
+ if (!drag || drag.id !== event.pointerId)
2877
+ return;
2878
+ if (this.locked) {
2879
+ this.cancelPalettePointer();
2880
+ return;
2881
+ }
2882
+ if (!drag.moved && Math.hypot(event.clientX - drag.x, event.clientY - drag.y) < 5)
2883
+ return;
2884
+ if (!drag.moved) {
2885
+ drag.moved = true;
2886
+ if (this.narrowValue)
2887
+ this.closeDrawer();
2888
+ }
2889
+ let ghost = this.shadowRoot.querySelector('[part=palette-ghost]');
2890
+ if (!ghost) {
2891
+ ghost = document.createElement('div');
2892
+ ghost.setAttribute('part', 'palette-ghost');
2893
+ ghost.dataset.pointer = 'true';
2894
+ ghost.setAttribute('aria-hidden', 'true');
2895
+ if (drag.kind.icon)
2896
+ ghost.append(drag.kind.icon());
2897
+ ghost.append(document.createTextNode(drag.kind.label));
2898
+ this.shadowRoot.append(ghost);
2899
+ }
2900
+ ghost.style.left = `${event.clientX}px`;
2901
+ ghost.style.top = `${event.clientY}px`;
2902
+ this.markPaletteDrop(!drag.kind.placement && drag.kind.kind !== 'end' ? this.paletteCanvasPoint(event) : undefined);
2903
+ event.preventDefault();
2904
+ }
2905
+ cancelPalettePointer() {
2906
+ const drag = this.palettePointer;
2907
+ this.palettePointer = undefined;
2908
+ this.ownerDocument.removeEventListener('keydown', this.escapePalettePointer, true);
2909
+ if (!drag)
2910
+ return;
2911
+ this.suppressPaletteClick = true;
2912
+ this.shadowRoot?.querySelector('[part=palette-ghost]')?.remove();
2913
+ this.markPaletteDrop(undefined);
2914
+ try {
2915
+ this.releasePointerCapture(drag.id);
2916
+ }
2917
+ catch { /* Capture may already have been lost. */ }
2918
+ }
2919
+ finishPalettePointer(event) {
2920
+ const drag = this.palettePointer;
2921
+ if (!drag || drag.id !== event.pointerId)
2922
+ return;
2923
+ const point = this.paletteCanvasPoint(event);
2924
+ this.cancelPalettePointer();
2925
+ if (this.locked)
2926
+ return;
2927
+ if (!drag.moved) {
2928
+ this.activatePaletteKind(drag.kind);
2929
+ if (this.narrowValue)
2930
+ this.closeDrawer();
2931
+ return;
2932
+ }
2933
+ if (!point)
2934
+ return;
2935
+ if (drag.kind.placement) {
2936
+ this.addLayoutKind(drag.kind, point);
2937
+ return;
2938
+ }
2939
+ const line = drag.kind.kind === 'end' ? undefined : this.paletteLineAt(point);
2940
+ const frame = line ? undefined : this.paletteFrameAt(point);
2941
+ this.requestEdit({ type: line ? 'insert' : 'add', kind: drag.kind, lineId: line?.id, from: line?.from, to: line?.to, parentId: frame?.id, position: point });
2942
+ }
2602
2943
  renderPalette() {
2603
2944
  const choices = this.shadowRoot.querySelector("[part=choices]");
2604
2945
  const query = this.shadowRoot.querySelector("[part=search]").value.toLowerCase();
@@ -2631,19 +2972,30 @@ export class ProcessModeler extends BaseElement {
2631
2972
  description.textContent = kind.description;
2632
2973
  button.append(icon, name, description);
2633
2974
  button.disabled = this.locked;
2634
- button.draggable = !this.locked;
2635
- button.addEventListener("click", () => {
2636
- if (kind.placement) {
2637
- this.addLayoutKind(kind);
2975
+ button.draggable = false;
2976
+ button.addEventListener('pointerdown', event => {
2977
+ if (this.locked || event.button !== 0 || event.isPrimary === false)
2638
2978
  return;
2979
+ this.cancelPalettePointer();
2980
+ this.suppressPaletteClick = false;
2981
+ this.palettePointer = { kind, id: event.pointerId, x: event.clientX, y: event.clientY, moved: false };
2982
+ this.ownerDocument.addEventListener('keydown', this.escapePalettePointer, true);
2983
+ try {
2984
+ this.setPointerCapture(event.pointerId);
2639
2985
  }
2640
- const from = this.selectedId;
2641
- const outgoing = from ? this.projection.lines.filter(line => line.from === from) : [];
2642
- const line = outgoing.length === 1 ? outgoing[0] : undefined;
2643
- this.requestEdit(line ? { type: "insert", kind, lineId: line.id, from: line.from, to: line.to } : { type: "add", kind, ...(from ? { from } : {}) });
2986
+ catch { /* Detached synthetic pointers cannot be captured. */ }
2987
+ event.preventDefault();
2988
+ event.stopPropagation();
2644
2989
  });
2645
- button.addEventListener("dragstart", event => { this.paletteDragKind = kind; event.dataTransfer?.setData("application/boe-process-kind", kind.kind); });
2646
- button.addEventListener("dragend", () => { this.paletteDragKind = null; this.shadowRoot?.querySelector('[part=palette-ghost]')?.remove(); this.markDropLine(undefined); });
2990
+ button.addEventListener('click', event => {
2991
+ if (event.detail > 0 && this.suppressPaletteClick) {
2992
+ this.suppressPaletteClick = false;
2993
+ return;
2994
+ }
2995
+ this.activatePaletteKind(kind);
2996
+ });
2997
+ button.addEventListener('dragstart', event => { this.paletteDragKind = kind; event.dataTransfer?.setData('application/boe-process-kind', kind.kind); });
2998
+ button.addEventListener('dragend', () => { this.paletteDragKind = null; this.shadowRoot?.querySelector('[part=palette-ghost]')?.remove(); this.markDropLine(undefined); });
2647
2999
  choices.append(button);
2648
3000
  }
2649
3001
  const canvas = this.shadowRoot.querySelector("[part=canvas]");
@@ -3106,7 +3458,7 @@ export class ProcessModeler extends BaseElement {
3106
3458
  duplicate.type = 'button';
3107
3459
  duplicate.textContent = 'Duplicate';
3108
3460
  duplicate.disabled = this.locked;
3109
- duplicate.onclick = () => this.requestEdit({ type: 'duplicate', sourceId: selected.id, position: { x: this.layoutValue.boxes[selected.id].x + 32, y: this.layoutValue.boxes[selected.id].y + 32 } });
3461
+ duplicate.onclick = () => this.duplicateBoxes([selected.id], 48);
3110
3462
  const remove = document.createElement('button');
3111
3463
  remove.type = 'button';
3112
3464
  remove.textContent = 'Delete';
@@ -3119,7 +3471,7 @@ export class ProcessModeler extends BaseElement {
3119
3471
  }
3120
3472
  renderSelectionToolbar() {
3121
3473
  const toolbar = this.shadowRoot.querySelector('[part=selection-toolbar]');
3122
- const key = `${[...this.selectedIds].join(',')}|${this.selected?.kind}|${this.selected?.shape}|${this.selectedLineId}|${Boolean(this.selectedLineId && this.layoutValue.lines?.[this.selectedLineId])}|${this.locked}|${this.narrowValue}|${this.projection.lines.filter(line => line.from === this.selectedId).map(line => `${line.id}:${line.label}:${line.dashed}`).join(',')}`;
3474
+ const key = `${[...this.selectedIds].join(',')}|${this.selected?.kind}|${this.selected?.shape}|${this.selectedLineId}|${Boolean(this.selectedLine && (this.layoutValue.lines?.[this.selectedLine.id] || this.selectedLine.fromSide || this.selectedLine.toSide || this.selectedLine.points?.length))}|${this.locked}|${this.narrowValue}|${this.projection.lines.filter(line => line.from === this.selectedId).map(line => `${line.id}:${line.label}:${line.dashed}`).join(',')}`;
3123
3475
  if (key === this.selectionToolbarKey)
3124
3476
  return;
3125
3477
  this.selectionToolbarKey = key;
@@ -3148,7 +3500,7 @@ export class ProcessModeler extends BaseElement {
3148
3500
  if (this.selectedLine) {
3149
3501
  const line = this.selectedLine;
3150
3502
  add('Insert a step', 'insert', () => this.openLineChooser(line, toolbar.querySelector('[data-selection-command=insert]') ?? undefined), true);
3151
- if (this.layoutValue.lines?.[line.id])
3503
+ if (this.layoutValue.lines?.[line.id] || line.fromSide || line.toSide || line.points?.length)
3152
3504
  add('Reset line', 'reset-line', () => this.resetLine(line.id));
3153
3505
  if (!this.disableConnections)
3154
3506
  add('Delete', 'delete-line', () => this.requestEdit({ type: 'disconnect', lineId: line.id }));
@@ -3163,7 +3515,7 @@ export class ProcessModeler extends BaseElement {
3163
3515
  if (failurePath)
3164
3516
  add('If it fails', 'add-failure', () => this.openAnchoredChooser({ type: 'add', from: box.id, routeLabel: 'If it fails', dashed: true }, `If a step in ${box.title} fails`, toolbar.querySelector('[data-selection-command=add-failure]') ?? undefined, Array.from(this.shadowRoot.querySelectorAll('[data-box-id]')).find(element => element.dataset.boxId === box.id)), true);
3165
3517
  if (!['start', 'timer', 'section'].includes(box.kind))
3166
- add('Duplicate', 'duplicate', () => { const position = this.layoutValue.boxes[box.id]; this.requestEdit({ type: 'duplicate', sourceId: box.id, position: { x: position.x + 32, y: position.y + 32 } }); });
3518
+ add('Duplicate', 'duplicate', () => this.duplicateBoxes([box.id], 48));
3167
3519
  if (!['start', 'timer'].includes(box.kind))
3168
3520
  add('Delete', 'delete-many', () => this.selectionCommand('delete-many'));
3169
3521
  }
@@ -3266,9 +3618,11 @@ export class ProcessModeler extends BaseElement {
3266
3618
  focusBox(id) {
3267
3619
  Array.from(this.shadowRoot.querySelectorAll("[data-box-id]"))
3268
3620
  .find((box) => box.dataset.boxId === id)
3269
- ?.focus();
3621
+ ?.focus({ preventScroll: true });
3270
3622
  }
3271
3623
  setStatus(message, urgent = false) {
3624
+ if (!this.isRendered)
3625
+ return;
3272
3626
  this.shadowRoot.querySelector(urgent ? "[part=status]" : "[part=urgent-status]").textContent = "";
3273
3627
  this.shadowRoot.querySelector(urgent ? "[part=urgent-status]" : "[part=status]").textContent = message;
3274
3628
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@unofficialbox/box-open-elements",
3
- "version": "0.31.2",
3
+ "version": "0.32.0",
4
4
  "description": "Box Open Elements: an open, framework-agnostic design system and web component library for Box-style experiences",
5
5
  "license": "MIT",
6
6
  "homepage": "https://unofficialbox.github.io/box-open-elements",