@kekonic/diagrams-theme 1.0.0-rc.4

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Kekonic
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,36 @@
1
+ # @kekonic/diagrams-theme
2
+
3
+ CSS variable theme contract and dark/light token sets for KDiagram.
4
+
5
+ ## Scope
6
+
7
+ - Built-in `dark` / `light` modes
8
+ - `registerTheme` / `getThemeTokens` / `themeToCss`
9
+ - Kind class names, branch semantics, and style resolution helpers
10
+
11
+ ## Install
12
+
13
+ ```bash
14
+ pnpm add @kekonic/diagrams-theme
15
+ ```
16
+
17
+ ## Stylesheet
18
+
19
+ ```ts
20
+ import "@kekonic/diagrams-theme/theme.css";
21
+ // or via the meta package:
22
+ import "@kekonic/diagrams/theme.css";
23
+ ```
24
+
25
+ Live SVG prefers CSS variables; pass `snapshotTheme: true` on render/CLI to bake resolved tokens into static export.
26
+
27
+ Regenerate the stylesheet after token edits:
28
+
29
+ ```bash
30
+ pnpm --filter @kekonic/diagrams-theme generate:theme-css
31
+ ```
32
+
33
+ ## Related
34
+
35
+ - Meta package: `@kekonic/diagrams`
36
+ - Pipeline boundaries: [`docs/architecture/pipeline.md`](../../docs/architecture/pipeline.md)
@@ -0,0 +1,47 @@
1
+ import { BranchKind, GraphEdge, GraphNode, SequenceFragment, StyleDefinition } from "@kekonic/diagrams-core";
2
+
3
+ //#region src/theme/tokens.d.ts
4
+ type ThemeTokens = Record<string, string>;
5
+ declare function getThemeTokens(mode: string): ThemeTokens;
6
+ declare function registerTheme(name: string, tokens: ThemeTokens): void;
7
+ declare function themeToCss(mode: string, snapshot?: boolean): string;
8
+ declare function kindClassName(kind: string): string;
9
+ declare const THEME_CSS = "\n.k-diagram {\n font-family: \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n}\n.flow-node-shell { fill: var(--node-fill, var(--kd-node-fill)); stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }\n.flow-node-shell-rim { pointer-events: none; }\n.flow-node-title { fill: var(--kd-text); font-size: 15px; font-weight: 800; letter-spacing: -0.02em; }\n.flow-node-subtitle { fill: var(--kd-muted); font-size: 10.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; }\n.flow-node-technology { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; font-style: italic; }\n.flow-node-description { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; opacity: 0.92; }\n.flow-node-note-text { fill: var(--kd-muted); font-size: 9px; font-style: italic; }\n.flow-node-icon-glyph { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: 0.88; }\n.flow-port-marker { fill: var(--node-stroke, var(--kd-node-stroke)); stroke: none; }\n.flow-node-user, .flow-node-actor, .flow-node-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }\n.flow-node-participant { --node-fill: var(--kd-surface); --node-stroke: var(--kd-border); }\n.flow-node-system { --node-fill: var(--kd-system-fill); --node-stroke: var(--kd-system-stroke); }\n.flow-node-system .flow-node-shell { stroke-width: 2; }\n.flow-node-container { --node-fill: var(--kd-container-fill); --node-stroke: var(--kd-container-stroke); }\n.flow-node-component { --node-fill: var(--kd-component-fill); --node-stroke: var(--kd-component-stroke); }\n.flow-node-service, .flow-node-worker, .flow-node-application, .flow-node-module, .flow-node-process, .flow-node-task, .flow-node-subprocess, .flow-node-command, .flow-node-producer, .flow-node-consumer, .flow-node-job, .flow-node-aggregate, .flow-node-projection, .flow-node-interface, .flow-node-deployment, .flow-node-server, .flow-node-state { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-node-database, .flow-node-entity, .flow-node-cache, .flow-node-store, .flow-node-bucket { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }\n.flow-node-table, .flow-node-class { --node-fill: var(--kd-table-fill, var(--kd-service-fill)); --node-stroke: var(--kd-service-stroke); }\n.flow-node-gateway, .flow-node-broker, .flow-node-loadbalancer, .flow-node-firewall, .flow-node-preparation, .flow-node-policy { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-queue, .flow-node-dlq, .flow-node-topic, .flow-node-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-event, .flow-node-start, .flow-node-end, .flow-node-branch, .flow-node-tag { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }\n.flow-node-choice, .flow-node-decision { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }\n.flow-node-warning, .flow-node-failure, .flow-node-manual { --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke); }\n.flow-node-success { --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke); }\n.flow-node-external { --node-fill: var(--kd-external-fill); --node-stroke: var(--kd-external-stroke); --node-stroke-dash: 5 3.5; }\n.flow-node-note, .flow-node-document, .flow-node-message, .flow-node-package { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }\n.flow-node-cloud, .flow-node-zone, .flow-node-group, .flow-node-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-node-io { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-connector, .flow-node-initial, .flow-node-final, .flow-node-junction, .flow-node-commit { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }\n/* Icon-only kind (node shell) \u2014 do not put glyph color rules here. */\n.flow-node-icon {\n --node-fill: color-mix(in srgb, var(--kd-node-fill) 88%, var(--kd-accent, var(--kd-text)) 12%);\n --node-stroke: var(--kd-node-stroke);\n}\n/* Glyph wrapper \u2014 --icon-color for icon-only tint; else node stroke / theme. */\n.flow-node-icon-mark {\n color: var(--icon-color, var(--node-stroke, var(--kd-node-stroke)));\n}\n.flow-node-fork, .flow-node-join { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }\n.flow-node-rectangle, .flow-node-rounded, .flow-shape-rectangle, .flow-shape-rounded { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-node-pill, .flow-shape-pill { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }\n.flow-node-diamond, .flow-shape-diamond { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }\n.flow-node-hexagon, .flow-shape-hexagon { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-cylinder, .flow-shape-cylinder { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }\n.flow-node-ellipse, .flow-shape-ellipse, .flow-node-circle, .flow-shape-circle { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }\n.flow-node-triangle, .flow-shape-triangle, .flow-node-parallelogram, .flow-shape-parallelogram, .flow-node-trapezoid, .flow-shape-trapezoid { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-folded-document, .flow-shape-folded-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }\n.flow-shape-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }\n.flow-shape-cloud, .flow-shape-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-shape-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }\n.flow-shape-queue, .flow-shape-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-shell-partition { pointer-events: none; }\n/* Built-in semantic styles (is danger, is success, ...) \u2014 also applied via StyleDefinition. */\n.kd-style-danger, .kd-style-dangerous, .kd-style-error, .kd-style-fail, .kd-style-failure,\n.kd-style-critical {\n --node-fill: var(--kd-danger-fill); --node-stroke: var(--kd-danger);\n}\n.kd-style-warning, .kd-style-warn, .kd-style-caution {\n --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke);\n}\n.kd-style-success, .kd-style-ok, .kd-style-pass {\n --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke);\n}\n.kd-style-muted, .kd-style-deprecated, .kd-style-inactive {\n --node-fill: var(--kd-surface); --node-stroke: var(--kd-muted); --node-stroke-dash: 5 3.5;\n}\n.kd-style-info, .kd-style-accent {\n --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent);\n}\n.flow-table-shell { stroke: none; }\n.flow-table-border { fill: none; stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.5; }\n.flow-table-title { fill: var(--kd-table-title, var(--kd-text)); font-weight: 700; letter-spacing: -0.01em; }\n.flow-table-col-name { fill: var(--kd-text); font-weight: 600; letter-spacing: -0.01em; }\n.flow-table-col-pk { font-weight: 700; }\n.flow-table-col-type { fill: var(--kd-muted); font-weight: 500; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0; font-size: 10px; }\n.flow-table-col-nn { fill: var(--kd-muted); font-weight: 700; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.02em; font-size: 9px; opacity: 0.72; }\n.flow-table-col-note { fill: var(--kd-muted); font-weight: 500; opacity: 0.78; letter-spacing: 0; font-size: 9.5px; }\n.flow-table-badge-pk { fill: var(--kd-table-badge-pk-fill); }\n.flow-table-badge-fk { fill: var(--kd-table-badge-fk-fill); }\n.flow-table-badge-uk { fill: var(--kd-table-badge-uk-fill); }\n.flow-table-badge-text { fill: var(--kd-table-badge-text); font-weight: 800; letter-spacing: 0.02em; }\n.flow-table-zebra { pointer-events: none; }\n.flow-group-box { fill: var(--kd-group-fill); stroke: var(--kd-group-stroke); }\n.flow-group-header-bar { pointer-events: none; }\n.flow-group-header-accent { pointer-events: none; }\n.flow-group-header-rule { pointer-events: none; }\n.flow-group-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }\n.flow-group-label-icon { color: var(--icon-color, var(--kd-muted)); }\n.flow-group-chromeless { pointer-events: none; }\n.flow-edge-label-bg { fill: var(--kd-edge-label-bg); stroke: var(--kd-edge-label-stroke); stroke-width: 1; }\n.flow-edge-label-leader { stroke: var(--kd-edge-label-stroke); stroke-width: 1; fill: none; opacity: 0.85; }\n.flow-edge-label-text { fill: var(--kd-edge-label-text); font-size: 11px; font-weight: 700; letter-spacing: 0.02em; }\n.flow-edge-label-icon { color: var(--icon-color, var(--kd-edge-label-text)); }\n.flow-debug-port { fill: var(--kd-accent); stroke: var(--kd-bg); stroke-width: 1.5; opacity: 0.95; }\n.flow-debug-port-source { fill: var(--kd-edge-yes, var(--kd-accent)); }\n.flow-debug-port-target { fill: var(--kd-edge-no, var(--kd-danger, var(--kd-accent))); }\n.flow-edge-endpoint { stroke: var(--kd-bg); stroke-width: 1.25; opacity: 0.95; }\n.flow-canvas-title-text { fill: var(--kd-text); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }\n.flow-canvas-subtitle-text { fill: var(--kd-muted); font-size: 13px; font-weight: 500; letter-spacing: 0.01em; }\n.flow-canvas-legend-text { fill: var(--kd-muted); font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }\n.flow-group-label-bar { fill: var(--kd-text); font-weight: 700; }\n.flow-edge-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }\n.flow-sequence-message-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }\n.flow-sequence-message-label { fill: var(--kd-text); font-size: 11px; font-weight: 700; letter-spacing: 0.01em; }\n.flow-sequence-fragment-box { fill: var(--kd-sequence-fragment-fill); stroke: var(--kd-sequence-fragment-stroke); }\n.flow-sequence-fragment-operand {\n fill: var(--kd-sequence-fragment-fill, transparent);\n stroke: none;\n pointer-events: none;\n}\n.flow-sequence-fragment-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }\n.flow-sequence-divider-label { fill: var(--kd-muted); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; }\n.flow-sequence-note-text { fill: var(--kd-text); font-size: 11px; font-weight: 500; }\n.flow-edge-label-yes .flow-edge-label-bg { fill: var(--kd-edge-yes-label-bg); stroke: var(--kd-edge-yes); }\n.flow-edge-label-yes .flow-edge-label-text { fill: var(--kd-edge-yes-label-text); }\n.flow-edge-label-no .flow-edge-label-bg { fill: var(--kd-edge-no-label-bg); stroke: var(--kd-edge-no); }\n.flow-edge-label-no .flow-edge-label-text { fill: var(--kd-edge-no-label-text); }\n.flow-edge-branch-yes { stroke: var(--kd-edge-yes); }\n.flow-edge-branch-no { stroke: var(--kd-edge-no); }\n.flow-edge-async { stroke-dasharray: 6 4; }\n.flow-edge-eventual { stroke-dasharray: 2 6; opacity: 0.85; }\n.flow-edge-association { stroke-dasharray: 1 0; opacity: 0.75; }\n.flow-edge-dependency { stroke-dasharray: 2 4; opacity: 0.82; }\n.flow-edge-failure { stroke: var(--kd-danger); }\n\n/* Diagram animation emphasis (interactive player)\n * Resolve --kd-* on .k-diagram (SVG root), not on .kdiagram-viewport \u2014\n * intermediate vars on the viewport can't see tokens defined on the SVG child.\n * Active emphasis must NOT replace semantic strokes (yes/no, kind, danger, \u2026).\n */\n.k-diagram {\n --kd-anim-dim-opacity: 0.62;\n /* Soft haze \u2014 keep presence via wide blur, not a tight bright ring. */\n --kd-anim-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 18%, transparent);\n --kd-anim-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 9%, transparent);\n /* Pulse peak \u2014 stronger than steady active so the beat reads clearly. */\n --kd-anim-pulse-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 36%, transparent);\n --kd-anim-pulse-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 16%, transparent);\n /* Fallback only when an edge has no resolvable stroke (flow overlays set inline). */\n --kd-anim-flow-stroke: color-mix(in srgb, var(--kd-accent, #a195f7) 55%, white);\n --kd-anim-flow-core: color-mix(in srgb, var(--kd-accent, #a195f7) 30%, white);\n}\n.k-diagram .flow-anim-dim {\n opacity: var(--kd-anim-dim-opacity);\n}\n.k-diagram .flow-anim-active {\n opacity: 1 !important;\n}\n.k-diagram .flow-anim-active.flow-node {\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 22px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-active.flow-node :is(.flow-node-shell, rect.flow-node-shell) {\n stroke-width: 2.5px;\n}\n.k-diagram .flow-anim-active.flow-edge .flow-edge-path,\n.k-diagram .flow-anim-active.flow-edge .flow-edge-jump,\n.k-diagram .flow-anim-active.flow-edge .flow-sequence-message-path {\n stroke-width: 2.6px;\n stroke-opacity: 1;\n filter: drop-shadow(0 0 6px var(--kd-anim-glow))\n drop-shadow(0 0 16px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-active.flow-sequence-fragment .flow-sequence-fragment-box {\n stroke-width: 2.2px;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-active.flow-sequence-fragment-operand {\n filter: drop-shadow(0 0 6px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-pulse {\n animation: flow-anim-pulse 0.65s ease-in-out infinite;\n}\n@keyframes flow-anim-pulse {\n 0%, 100% {\n filter: drop-shadow(0 0 6px var(--kd-anim-glow))\n drop-shadow(0 0 14px var(--kd-anim-glow-soft));\n }\n 50% {\n filter: drop-shadow(0 0 14px var(--kd-anim-pulse-glow))\n drop-shadow(0 0 36px var(--kd-anim-pulse-glow-soft));\n }\n}\n.k-diagram .flow-anim-flow-track {\n fill: none;\n stroke: color-mix(in srgb, var(--kd-anim-flow-stroke) 40%, transparent);\n stroke-width: 5;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n}\n.k-diagram .flow-anim-flow-overlay {\n fill: none;\n stroke: var(--kd-anim-flow-stroke);\n stroke-width: 3.75;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-flow-core {\n fill: none;\n stroke: var(--kd-anim-flow-core);\n stroke-width: 1.75;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n}\n.k-diagram .flow-anim-flow-head {\n fill: var(--kd-anim-flow-core);\n stroke: var(--kd-anim-flow-stroke);\n stroke-width: 1.5;\n pointer-events: none;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n}\n@media (prefers-reduced-motion: reduce) {\n .k-diagram .flow-anim-pulse {\n animation: none;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n }\n}\n";
10
+ //#endregion
11
+ //#region src/theme/kind-labels.d.ts
12
+ /** Human-readable kind subtitles for node chrome (not debug identifiers). */
13
+ declare function kindSubtitle(kind: string): string;
14
+ //#endregion
15
+ //#region src/theme/edge-semantics.d.ts
16
+ /** Semantic branch styling for edge labels and strokes. */
17
+ type BranchSemantics = BranchKind;
18
+ /** Prefer compiled `GraphEdge.branch`; fall back to label classification. */
19
+ declare function branchSemantics(label?: string, branch?: BranchKind): BranchSemantics;
20
+ declare function branchEdgeClass(semantics: BranchSemantics): string;
21
+ declare function branchLabelClass(semantics: BranchSemantics): string;
22
+ declare function branchStrokeColor(semantics: BranchSemantics, fallback: string): string;
23
+ //#endregion
24
+ //#region src/theme/builtin-styles.d.ts
25
+ /** Canonical names users should prefer in docs (`is danger`, not only aliases). */
26
+ declare const BUILTIN_SEMANTIC_STYLE_NAMES: readonly ["danger", "warning", "success", "critical", "muted", "info"];
27
+ type BuiltinSemanticStyleName = (typeof BUILTIN_SEMANTIC_STYLE_NAMES)[number];
28
+ declare const BUILTIN_STYLE_DEFINITIONS: readonly StyleDefinition[];
29
+ /** Merge authored styles over builtins (same name + target wins for authored). */
30
+ declare function withBuiltinStyles(authored: StyleDefinition[]): StyleDefinition[];
31
+ //#endregion
32
+ //#region src/theme/resolve-styles.d.ts
33
+ type ResolvedStyles = {
34
+ cssVars: Record<string, string>;
35
+ classes: string[];
36
+ badge?: string;
37
+ strokeWidth?: number;
38
+ strokeDash?: string;
39
+ };
40
+ declare function resolveNodeStyles(node: GraphNode, styles: StyleDefinition[]): ResolvedStyles;
41
+ declare function resolveEdgeStyles(edge: GraphEdge, styles: StyleDefinition[]): ResolvedStyles;
42
+ /** Resolve semantic / authored styles onto a sequence fragment frame. */
43
+ declare function resolveFragmentStyles(fragment: Pick<SequenceFragment, "styleRefs" | "unresolvedVars">, styles: StyleDefinition[]): ResolvedStyles;
44
+ declare function stylesToInlineCss(vars: Record<string, string>): string;
45
+ //#endregion
46
+ export { BUILTIN_SEMANTIC_STYLE_NAMES, BUILTIN_STYLE_DEFINITIONS, type BranchSemantics, type BuiltinSemanticStyleName, type ResolvedStyles, THEME_CSS, type ThemeTokens, branchEdgeClass, branchLabelClass, branchSemantics, branchStrokeColor, getThemeTokens, kindClassName, kindSubtitle, registerTheme, resolveEdgeStyles, resolveFragmentStyles, resolveNodeStyles, stylesToInlineCss, themeToCss, withBuiltinStyles };
47
+ //# sourceMappingURL=index.d.mts.map
package/dist/index.mjs ADDED
@@ -0,0 +1,632 @@
1
+ import { classifyBranch, getKindDefaults, kindSubtitle as kindSubtitle$1 } from "@kekonic/diagrams-core";
2
+ //#region src/theme/tokens.ts
3
+ const DARK_TOKENS = {
4
+ "--kd-bg": "#070b12",
5
+ "--kd-surface": "#10161f",
6
+ "--kd-surface-2": "#171e29",
7
+ "--kd-surface-3": "#222a36",
8
+ "--kd-text": "#e8ebf1",
9
+ "--kd-muted": "#9099a5",
10
+ "--kd-border": "#343e4a",
11
+ "--kd-edge": "#7e8792",
12
+ "--kd-edge-label-bg": "#151c26",
13
+ "--kd-edge-label-text": "#e8ebf1",
14
+ "--kd-edge-label-stroke": "#3d4754",
15
+ "--kd-accent": "#a195f7",
16
+ "--kd-danger": "#f07f77",
17
+ "--kd-danger-fill": "#2a1214",
18
+ "--kd-warning": "#ebbd57",
19
+ "--kd-success": "#61c594",
20
+ "--kd-choice": "#a3a0d8",
21
+ "--kd-user-fill": "#16122a",
22
+ "--kd-user-stroke": "#a195f7",
23
+ "--kd-service-fill": "#141222",
24
+ "--kd-service-stroke": "#a195f7",
25
+ "--kd-system-fill": "#17102e",
26
+ "--kd-system-stroke": "#c1bbfc",
27
+ "--kd-container-fill": "#141222",
28
+ "--kd-container-stroke": "#a195f7",
29
+ "--kd-component-fill": "#161822",
30
+ "--kd-component-stroke": "#9a8fb8",
31
+ "--kd-data-fill": "#0a1a20",
32
+ "--kd-data-stroke": "#5bbdda",
33
+ "--kd-table-fill": "#10161f",
34
+ "--kd-table-header-fill": "#171e29",
35
+ "--kd-table-title": "#e8ebf1",
36
+ "--kd-table-zebra": "rgba(144, 153, 165, 0.06)",
37
+ "--kd-table-badge-pk-fill": "#7c5cbf",
38
+ "--kd-table-badge-fk-fill": "#6b5f9a",
39
+ "--kd-table-badge-uk-fill": "#2f7a62",
40
+ "--kd-table-badge-text": "#e8ebf1",
41
+ "--kd-choice-fill": "#1a1b2c",
42
+ "--kd-choice-stroke": "#a3a0d8",
43
+ "--kd-success-fill": "#0a2015",
44
+ "--kd-success-stroke": "#61c594",
45
+ "--kd-warning-fill": "#271e0a",
46
+ "--kd-warning-stroke": "#ebbd57",
47
+ "--kd-event-fill": "#101e17",
48
+ "--kd-async-stroke": "#61c594",
49
+ "--kd-external-fill": "#111418",
50
+ "--kd-external-stroke": "#9099a5",
51
+ "--kd-group-fill": "rgba(161, 149, 247, 0.1)",
52
+ "--kd-group-stroke": "rgba(161, 149, 247, 0.42)",
53
+ "--kd-node-fill": "var(--kd-surface)",
54
+ "--kd-node-stroke": "var(--kd-border)",
55
+ "--kd-node-shadow": "rgba(2, 6, 12, 0.45)",
56
+ "--kd-node-glow": "rgba(161, 149, 247, 0.14)",
57
+ "--kd-edge-yes": "var(--kd-success)",
58
+ "--kd-edge-no": "var(--kd-danger)",
59
+ "--kd-edge-yes-label-bg": "#0f5138",
60
+ "--kd-edge-yes-label-text": "#ecfdf5",
61
+ "--kd-edge-no-label-bg": "#8f2a2a",
62
+ "--kd-edge-no-label-text": "#fef2f2",
63
+ "--kd-port-fill": "var(--kd-bg)",
64
+ "--kd-port-stroke": "var(--kd-accent)",
65
+ "--kd-sequence-lifeline": "var(--kd-edge)",
66
+ "--kd-sequence-activation-fill": "var(--kd-surface-2)",
67
+ "--kd-sequence-activation-stroke": "var(--kd-edge)",
68
+ "--kd-sequence-fragment-fill": "rgba(161, 149, 247, 0.04)",
69
+ "--kd-sequence-fragment-stroke": "rgba(161, 149, 247, 0.28)",
70
+ "--kd-sequence-note-fill": "var(--kd-surface-2)",
71
+ "--kd-sequence-note-stroke": "var(--kd-border)"
72
+ };
73
+ const LIGHT_TOKENS = {
74
+ "--kd-bg": "#f2f5f9",
75
+ "--kd-surface": "#ffffff",
76
+ "--kd-surface-2": "#eef2f7",
77
+ "--kd-surface-3": "#e0e5eb",
78
+ "--kd-text": "#141b24",
79
+ "--kd-muted": "#565e69",
80
+ "--kd-border": "#c4cbd4",
81
+ "--kd-edge": "#616a75",
82
+ "--kd-edge-label-bg": "#ffffff",
83
+ "--kd-edge-label-text": "#141b24",
84
+ "--kd-edge-label-stroke": "#c4cbd4",
85
+ "--kd-accent": "#5f38ca",
86
+ "--kd-danger": "#c53637",
87
+ "--kd-danger-fill": "#fef2f2",
88
+ "--kd-warning": "#b67700",
89
+ "--kd-success": "#007d50",
90
+ "--kd-choice": "#6360a0",
91
+ "--kd-user-fill": "#f3eefc",
92
+ "--kd-user-stroke": "#5f38ca",
93
+ "--kd-service-fill": "#f5f0fc",
94
+ "--kd-service-stroke": "#5f38ca",
95
+ "--kd-system-fill": "#ede5fa",
96
+ "--kd-system-stroke": "#432990",
97
+ "--kd-container-fill": "#f5f0fc",
98
+ "--kd-container-stroke": "#5f38ca",
99
+ "--kd-component-fill": "#f7f5fb",
100
+ "--kd-component-stroke": "#6b5f8a",
101
+ "--kd-data-fill": "#e8f7fb",
102
+ "--kd-data-stroke": "#0e7490",
103
+ "--kd-table-fill": "#ffffff",
104
+ "--kd-table-header-fill": "#eef2f7",
105
+ "--kd-table-title": "#141b24",
106
+ "--kd-table-zebra": "rgba(86, 94, 105, 0.07)",
107
+ "--kd-table-badge-pk-fill": "#5f38ca",
108
+ "--kd-table-badge-fk-fill": "#6b5f9a",
109
+ "--kd-table-badge-uk-fill": "#2f7a62",
110
+ "--kd-table-badge-text": "#ffffff",
111
+ "--kd-choice-fill": "#f3f2fa",
112
+ "--kd-choice-stroke": "#6360a0",
113
+ "--kd-success-fill": "#ecfdf5",
114
+ "--kd-success-stroke": "#007d50",
115
+ "--kd-warning-fill": "#fffbeb",
116
+ "--kd-warning-stroke": "#b67700",
117
+ "--kd-event-fill": "#f0fdf4",
118
+ "--kd-async-stroke": "#007d50",
119
+ "--kd-external-fill": "#f8fafc",
120
+ "--kd-external-stroke": "#565e69",
121
+ "--kd-group-fill": "rgba(95, 56, 202, 0.07)",
122
+ "--kd-group-stroke": "rgba(95, 56, 202, 0.32)",
123
+ "--kd-node-fill": "var(--kd-surface)",
124
+ "--kd-node-stroke": "var(--kd-border)",
125
+ "--kd-node-shadow": "rgba(20, 27, 36, 0.12)",
126
+ "--kd-node-glow": "rgba(95, 56, 202, 0.08)",
127
+ "--kd-edge-yes": "var(--kd-success)",
128
+ "--kd-edge-no": "var(--kd-danger)",
129
+ "--kd-edge-yes-label-bg": "#007d50",
130
+ "--kd-edge-yes-label-text": "#ffffff",
131
+ "--kd-edge-no-label-bg": "#c53637",
132
+ "--kd-edge-no-label-text": "#ffffff",
133
+ "--kd-port-fill": "var(--kd-bg)",
134
+ "--kd-port-stroke": "var(--kd-accent)",
135
+ "--kd-sequence-lifeline": "var(--kd-edge)",
136
+ "--kd-sequence-activation-fill": "var(--kd-surface-2)",
137
+ "--kd-sequence-activation-stroke": "var(--kd-edge)",
138
+ "--kd-sequence-fragment-fill": "rgba(95, 56, 202, 0.03)",
139
+ "--kd-sequence-fragment-stroke": "rgba(95, 56, 202, 0.22)",
140
+ "--kd-sequence-note-fill": "var(--kd-surface-2)",
141
+ "--kd-sequence-note-stroke": "var(--kd-border)"
142
+ };
143
+ const registeredThemes = /* @__PURE__ */ new Map();
144
+ function getThemeTokens(mode) {
145
+ const registered = registeredThemes.get(mode);
146
+ if (registered) return { ...registered };
147
+ return mode === "light" ? { ...LIGHT_TOKENS } : { ...DARK_TOKENS };
148
+ }
149
+ function registerTheme(name, tokens) {
150
+ registeredThemes.set(name, tokens);
151
+ }
152
+ function themeToCss(mode, snapshot = false) {
153
+ if (!snapshot) return THEME_CSS;
154
+ const tokens = getThemeTokens(mode);
155
+ return `.k-diagram {\n${Object.entries(tokens).map(([k, v]) => ` ${k}: ${v};`).join("\n")}\n}\n${THEME_CSS}`;
156
+ }
157
+ function kindClassName(kind) {
158
+ return `flow-node-${kind.replace(/[^a-z0-9-]/gi, "")}`;
159
+ }
160
+ const THEME_CSS = `
161
+ .k-diagram {
162
+ font-family: "Inter", "Segoe UI", system-ui, sans-serif;
163
+ }
164
+ .flow-node-shell { fill: var(--node-fill, var(--kd-node-fill)); stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
165
+ .flow-node-shell-rim { pointer-events: none; }
166
+ .flow-node-title { fill: var(--kd-text); font-size: 15px; font-weight: 800; letter-spacing: -0.02em; }
167
+ .flow-node-subtitle { fill: var(--kd-muted); font-size: 10.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; }
168
+ .flow-node-technology { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; font-style: italic; }
169
+ .flow-node-description { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; opacity: 0.92; }
170
+ .flow-node-note-text { fill: var(--kd-muted); font-size: 9px; font-style: italic; }
171
+ .flow-node-icon-glyph { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: 0.88; }
172
+ .flow-port-marker { fill: var(--node-stroke, var(--kd-node-stroke)); stroke: none; }
173
+ .flow-node-user, .flow-node-actor, .flow-node-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }
174
+ .flow-node-participant { --node-fill: var(--kd-surface); --node-stroke: var(--kd-border); }
175
+ .flow-node-system { --node-fill: var(--kd-system-fill); --node-stroke: var(--kd-system-stroke); }
176
+ .flow-node-system .flow-node-shell { stroke-width: 2; }
177
+ .flow-node-container { --node-fill: var(--kd-container-fill); --node-stroke: var(--kd-container-stroke); }
178
+ .flow-node-component { --node-fill: var(--kd-component-fill); --node-stroke: var(--kd-component-stroke); }
179
+ .flow-node-service, .flow-node-worker, .flow-node-application, .flow-node-module, .flow-node-process, .flow-node-task, .flow-node-subprocess, .flow-node-command, .flow-node-producer, .flow-node-consumer, .flow-node-job, .flow-node-aggregate, .flow-node-projection, .flow-node-interface, .flow-node-deployment, .flow-node-server, .flow-node-state { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
180
+ .flow-node-database, .flow-node-entity, .flow-node-cache, .flow-node-store, .flow-node-bucket { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }
181
+ .flow-node-table, .flow-node-class { --node-fill: var(--kd-table-fill, var(--kd-service-fill)); --node-stroke: var(--kd-service-stroke); }
182
+ .flow-node-gateway, .flow-node-broker, .flow-node-loadbalancer, .flow-node-firewall, .flow-node-preparation, .flow-node-policy { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
183
+ .flow-node-queue, .flow-node-dlq, .flow-node-topic, .flow-node-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
184
+ .flow-node-event, .flow-node-start, .flow-node-end, .flow-node-branch, .flow-node-tag { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }
185
+ .flow-node-choice, .flow-node-decision { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }
186
+ .flow-node-warning, .flow-node-failure, .flow-node-manual { --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke); }
187
+ .flow-node-success { --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke); }
188
+ .flow-node-external { --node-fill: var(--kd-external-fill); --node-stroke: var(--kd-external-stroke); --node-stroke-dash: 5 3.5; }
189
+ .flow-node-note, .flow-node-document, .flow-node-message, .flow-node-package { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }
190
+ .flow-node-cloud, .flow-node-zone, .flow-node-group, .flow-node-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
191
+ .flow-node-io { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
192
+ .flow-node-connector, .flow-node-initial, .flow-node-final, .flow-node-junction, .flow-node-commit { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }
193
+ /* Icon-only kind (node shell) — do not put glyph color rules here. */
194
+ .flow-node-icon {
195
+ --node-fill: color-mix(in srgb, var(--kd-node-fill) 88%, var(--kd-accent, var(--kd-text)) 12%);
196
+ --node-stroke: var(--kd-node-stroke);
197
+ }
198
+ /* Glyph wrapper — --icon-color for icon-only tint; else node stroke / theme. */
199
+ .flow-node-icon-mark {
200
+ color: var(--icon-color, var(--node-stroke, var(--kd-node-stroke)));
201
+ }
202
+ .flow-node-fork, .flow-node-join { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }
203
+ .flow-node-rectangle, .flow-node-rounded, .flow-shape-rectangle, .flow-shape-rounded { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
204
+ .flow-node-pill, .flow-shape-pill { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }
205
+ .flow-node-diamond, .flow-shape-diamond { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }
206
+ .flow-node-hexagon, .flow-shape-hexagon { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
207
+ .flow-node-cylinder, .flow-shape-cylinder { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }
208
+ .flow-node-ellipse, .flow-shape-ellipse, .flow-node-circle, .flow-shape-circle { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }
209
+ .flow-node-triangle, .flow-shape-triangle, .flow-node-parallelogram, .flow-shape-parallelogram, .flow-node-trapezoid, .flow-shape-trapezoid { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
210
+ .flow-node-folded-document, .flow-shape-folded-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }
211
+ .flow-shape-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }
212
+ .flow-shape-cloud, .flow-shape-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
213
+ .flow-shape-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }
214
+ .flow-shape-queue, .flow-shape-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
215
+ .flow-node-shell-partition { pointer-events: none; }
216
+ /* Built-in semantic styles (is danger, is success, ...) — also applied via StyleDefinition. */
217
+ .kd-style-danger, .kd-style-dangerous, .kd-style-error, .kd-style-fail, .kd-style-failure,
218
+ .kd-style-critical {
219
+ --node-fill: var(--kd-danger-fill); --node-stroke: var(--kd-danger);
220
+ }
221
+ .kd-style-warning, .kd-style-warn, .kd-style-caution {
222
+ --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke);
223
+ }
224
+ .kd-style-success, .kd-style-ok, .kd-style-pass {
225
+ --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke);
226
+ }
227
+ .kd-style-muted, .kd-style-deprecated, .kd-style-inactive {
228
+ --node-fill: var(--kd-surface); --node-stroke: var(--kd-muted); --node-stroke-dash: 5 3.5;
229
+ }
230
+ .kd-style-info, .kd-style-accent {
231
+ --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent);
232
+ }
233
+ .flow-table-shell { stroke: none; }
234
+ .flow-table-border { fill: none; stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.5; }
235
+ .flow-table-title { fill: var(--kd-table-title, var(--kd-text)); font-weight: 700; letter-spacing: -0.01em; }
236
+ .flow-table-col-name { fill: var(--kd-text); font-weight: 600; letter-spacing: -0.01em; }
237
+ .flow-table-col-pk { font-weight: 700; }
238
+ .flow-table-col-type { fill: var(--kd-muted); font-weight: 500; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0; font-size: 10px; }
239
+ .flow-table-col-nn { fill: var(--kd-muted); font-weight: 700; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.02em; font-size: 9px; opacity: 0.72; }
240
+ .flow-table-col-note { fill: var(--kd-muted); font-weight: 500; opacity: 0.78; letter-spacing: 0; font-size: 9.5px; }
241
+ .flow-table-badge-pk { fill: var(--kd-table-badge-pk-fill); }
242
+ .flow-table-badge-fk { fill: var(--kd-table-badge-fk-fill); }
243
+ .flow-table-badge-uk { fill: var(--kd-table-badge-uk-fill); }
244
+ .flow-table-badge-text { fill: var(--kd-table-badge-text); font-weight: 800; letter-spacing: 0.02em; }
245
+ .flow-table-zebra { pointer-events: none; }
246
+ .flow-group-box { fill: var(--kd-group-fill); stroke: var(--kd-group-stroke); }
247
+ .flow-group-header-bar { pointer-events: none; }
248
+ .flow-group-header-accent { pointer-events: none; }
249
+ .flow-group-header-rule { pointer-events: none; }
250
+ .flow-group-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }
251
+ .flow-group-label-icon { color: var(--icon-color, var(--kd-muted)); }
252
+ .flow-group-chromeless { pointer-events: none; }
253
+ .flow-edge-label-bg { fill: var(--kd-edge-label-bg); stroke: var(--kd-edge-label-stroke); stroke-width: 1; }
254
+ .flow-edge-label-leader { stroke: var(--kd-edge-label-stroke); stroke-width: 1; fill: none; opacity: 0.85; }
255
+ .flow-edge-label-text { fill: var(--kd-edge-label-text); font-size: 11px; font-weight: 700; letter-spacing: 0.02em; }
256
+ .flow-edge-label-icon { color: var(--icon-color, var(--kd-edge-label-text)); }
257
+ .flow-debug-port { fill: var(--kd-accent); stroke: var(--kd-bg); stroke-width: 1.5; opacity: 0.95; }
258
+ .flow-debug-port-source { fill: var(--kd-edge-yes, var(--kd-accent)); }
259
+ .flow-debug-port-target { fill: var(--kd-edge-no, var(--kd-danger, var(--kd-accent))); }
260
+ .flow-edge-endpoint { stroke: var(--kd-bg); stroke-width: 1.25; opacity: 0.95; }
261
+ .flow-canvas-title-text { fill: var(--kd-text); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
262
+ .flow-canvas-subtitle-text { fill: var(--kd-muted); font-size: 13px; font-weight: 500; letter-spacing: 0.01em; }
263
+ .flow-canvas-legend-text { fill: var(--kd-muted); font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
264
+ .flow-group-label-bar { fill: var(--kd-text); font-weight: 700; }
265
+ .flow-edge-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
266
+ .flow-sequence-message-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
267
+ .flow-sequence-message-label { fill: var(--kd-text); font-size: 11px; font-weight: 700; letter-spacing: 0.01em; }
268
+ .flow-sequence-fragment-box { fill: var(--kd-sequence-fragment-fill); stroke: var(--kd-sequence-fragment-stroke); }
269
+ .flow-sequence-fragment-operand {
270
+ fill: var(--kd-sequence-fragment-fill, transparent);
271
+ stroke: none;
272
+ pointer-events: none;
273
+ }
274
+ .flow-sequence-fragment-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }
275
+ .flow-sequence-divider-label { fill: var(--kd-muted); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; }
276
+ .flow-sequence-note-text { fill: var(--kd-text); font-size: 11px; font-weight: 500; }
277
+ .flow-edge-label-yes .flow-edge-label-bg { fill: var(--kd-edge-yes-label-bg); stroke: var(--kd-edge-yes); }
278
+ .flow-edge-label-yes .flow-edge-label-text { fill: var(--kd-edge-yes-label-text); }
279
+ .flow-edge-label-no .flow-edge-label-bg { fill: var(--kd-edge-no-label-bg); stroke: var(--kd-edge-no); }
280
+ .flow-edge-label-no .flow-edge-label-text { fill: var(--kd-edge-no-label-text); }
281
+ .flow-edge-branch-yes { stroke: var(--kd-edge-yes); }
282
+ .flow-edge-branch-no { stroke: var(--kd-edge-no); }
283
+ .flow-edge-async { stroke-dasharray: 6 4; }
284
+ .flow-edge-eventual { stroke-dasharray: 2 6; opacity: 0.85; }
285
+ .flow-edge-association { stroke-dasharray: 1 0; opacity: 0.75; }
286
+ .flow-edge-dependency { stroke-dasharray: 2 4; opacity: 0.82; }
287
+ .flow-edge-failure { stroke: var(--kd-danger); }
288
+
289
+ /* Diagram animation emphasis (interactive player)
290
+ * Resolve --kd-* on .k-diagram (SVG root), not on .kdiagram-viewport —
291
+ * intermediate vars on the viewport can't see tokens defined on the SVG child.
292
+ * Active emphasis must NOT replace semantic strokes (yes/no, kind, danger, …).
293
+ */
294
+ .k-diagram {
295
+ --kd-anim-dim-opacity: 0.62;
296
+ /* Soft haze — keep presence via wide blur, not a tight bright ring. */
297
+ --kd-anim-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 18%, transparent);
298
+ --kd-anim-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 9%, transparent);
299
+ /* Pulse peak — stronger than steady active so the beat reads clearly. */
300
+ --kd-anim-pulse-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 36%, transparent);
301
+ --kd-anim-pulse-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 16%, transparent);
302
+ /* Fallback only when an edge has no resolvable stroke (flow overlays set inline). */
303
+ --kd-anim-flow-stroke: color-mix(in srgb, var(--kd-accent, #a195f7) 55%, white);
304
+ --kd-anim-flow-core: color-mix(in srgb, var(--kd-accent, #a195f7) 30%, white);
305
+ }
306
+ .k-diagram .flow-anim-dim {
307
+ opacity: var(--kd-anim-dim-opacity);
308
+ }
309
+ .k-diagram .flow-anim-active {
310
+ opacity: 1 !important;
311
+ }
312
+ .k-diagram .flow-anim-active.flow-node {
313
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
314
+ drop-shadow(0 0 22px var(--kd-anim-glow-soft));
315
+ }
316
+ .k-diagram .flow-anim-active.flow-node :is(.flow-node-shell, rect.flow-node-shell) {
317
+ stroke-width: 2.5px;
318
+ }
319
+ .k-diagram .flow-anim-active.flow-edge .flow-edge-path,
320
+ .k-diagram .flow-anim-active.flow-edge .flow-edge-jump,
321
+ .k-diagram .flow-anim-active.flow-edge .flow-sequence-message-path {
322
+ stroke-width: 2.6px;
323
+ stroke-opacity: 1;
324
+ filter: drop-shadow(0 0 6px var(--kd-anim-glow))
325
+ drop-shadow(0 0 16px var(--kd-anim-glow-soft));
326
+ }
327
+ .k-diagram .flow-anim-active.flow-sequence-fragment .flow-sequence-fragment-box {
328
+ stroke-width: 2.2px;
329
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
330
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
331
+ }
332
+ .k-diagram .flow-anim-active.flow-sequence-fragment-operand {
333
+ filter: drop-shadow(0 0 6px var(--kd-anim-glow-soft));
334
+ }
335
+ .k-diagram .flow-anim-pulse {
336
+ animation: flow-anim-pulse 0.65s ease-in-out infinite;
337
+ }
338
+ @keyframes flow-anim-pulse {
339
+ 0%, 100% {
340
+ filter: drop-shadow(0 0 6px var(--kd-anim-glow))
341
+ drop-shadow(0 0 14px var(--kd-anim-glow-soft));
342
+ }
343
+ 50% {
344
+ filter: drop-shadow(0 0 14px var(--kd-anim-pulse-glow))
345
+ drop-shadow(0 0 36px var(--kd-anim-pulse-glow-soft));
346
+ }
347
+ }
348
+ .k-diagram .flow-anim-flow-track {
349
+ fill: none;
350
+ stroke: color-mix(in srgb, var(--kd-anim-flow-stroke) 40%, transparent);
351
+ stroke-width: 5;
352
+ stroke-linecap: round;
353
+ stroke-linejoin: round;
354
+ pointer-events: none;
355
+ }
356
+ .k-diagram .flow-anim-flow-overlay {
357
+ fill: none;
358
+ stroke: var(--kd-anim-flow-stroke);
359
+ stroke-width: 3.75;
360
+ stroke-linecap: round;
361
+ stroke-linejoin: round;
362
+ pointer-events: none;
363
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
364
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
365
+ }
366
+ .k-diagram .flow-anim-flow-core {
367
+ fill: none;
368
+ stroke: var(--kd-anim-flow-core);
369
+ stroke-width: 1.75;
370
+ stroke-linecap: round;
371
+ stroke-linejoin: round;
372
+ pointer-events: none;
373
+ }
374
+ .k-diagram .flow-anim-flow-head {
375
+ fill: var(--kd-anim-flow-core);
376
+ stroke: var(--kd-anim-flow-stroke);
377
+ stroke-width: 1.5;
378
+ pointer-events: none;
379
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
380
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
381
+ }
382
+ @media (prefers-reduced-motion: reduce) {
383
+ .k-diagram .flow-anim-pulse {
384
+ animation: none;
385
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
386
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
387
+ }
388
+ }
389
+ `;
390
+ //#endregion
391
+ //#region src/theme/kind-labels.ts
392
+ /** Human-readable kind subtitles for node chrome (not debug identifiers). */
393
+ function kindSubtitle(kind) {
394
+ return kindSubtitle$1(kind);
395
+ }
396
+ //#endregion
397
+ //#region src/theme/edge-semantics.ts
398
+ /** Prefer compiled `GraphEdge.branch`; fall back to label classification. */
399
+ function branchSemantics(label, branch) {
400
+ if (branch) return branch;
401
+ return classifyBranch(label);
402
+ }
403
+ function branchEdgeClass(semantics) {
404
+ if (semantics === "yes") return "flow-edge-branch-yes";
405
+ if (semantics === "no") return "flow-edge-branch-no";
406
+ return "";
407
+ }
408
+ function branchLabelClass(semantics) {
409
+ if (semantics === "yes") return "flow-edge-label-yes";
410
+ if (semantics === "no") return "flow-edge-label-no";
411
+ return "";
412
+ }
413
+ function branchStrokeColor(semantics, fallback) {
414
+ if (semantics === "yes") return "var(--kd-edge-yes)";
415
+ if (semantics === "no") return "var(--kd-edge-no)";
416
+ return fallback;
417
+ }
418
+ //#endregion
419
+ //#region src/theme/builtin-styles.ts
420
+ function tint(token, fillPct, strokePct) {
421
+ return {
422
+ "--kd-sequence-fragment-fill": `color-mix(in srgb, var(${token}) ${fillPct}%, transparent)`,
423
+ "--kd-sequence-fragment-stroke": `color-mix(in srgb, var(${token}) ${strokePct}%, transparent)`
424
+ };
425
+ }
426
+ const SEMANTICS = [
427
+ {
428
+ aliases: [
429
+ "danger",
430
+ "dangerous",
431
+ "error",
432
+ "fail",
433
+ "failed",
434
+ "failure"
435
+ ],
436
+ node: {
437
+ "--node-fill": "var(--kd-danger-fill)",
438
+ "--node-stroke": "var(--kd-danger)",
439
+ badge: "!"
440
+ },
441
+ edge: {
442
+ "--edge-stroke": "var(--kd-danger)",
443
+ strokeWidth: "2.4"
444
+ },
445
+ fragment: tint("--kd-danger", 3.5, 34)
446
+ },
447
+ {
448
+ aliases: [
449
+ "warning",
450
+ "warn",
451
+ "caution"
452
+ ],
453
+ node: {
454
+ "--node-fill": "var(--kd-warning-fill)",
455
+ "--node-stroke": "var(--kd-warning-stroke)",
456
+ badge: "!"
457
+ },
458
+ edge: {
459
+ "--edge-stroke": "var(--kd-warning)",
460
+ strokeWidth: "2.2"
461
+ },
462
+ fragment: tint("--kd-warning", 3.5, 32)
463
+ },
464
+ {
465
+ aliases: [
466
+ "success",
467
+ "ok",
468
+ "pass"
469
+ ],
470
+ node: {
471
+ "--node-fill": "var(--kd-success-fill)",
472
+ "--node-stroke": "var(--kd-success-stroke)",
473
+ badge: "✓"
474
+ },
475
+ edge: {
476
+ "--edge-stroke": "var(--kd-success)",
477
+ strokeWidth: "2.2"
478
+ },
479
+ fragment: tint("--kd-success", 3.5, 32)
480
+ },
481
+ {
482
+ aliases: ["critical"],
483
+ node: {
484
+ "--node-fill": "var(--kd-danger-fill)",
485
+ "--node-stroke": "var(--kd-danger)",
486
+ badge: "!",
487
+ strokeWidth: "2.6"
488
+ },
489
+ edge: {
490
+ "--edge-stroke": "var(--kd-danger)",
491
+ strokeWidth: "3"
492
+ },
493
+ fragment: tint("--kd-danger", 5, 40)
494
+ },
495
+ {
496
+ aliases: [
497
+ "muted",
498
+ "deprecated",
499
+ "inactive"
500
+ ],
501
+ node: {
502
+ "--node-fill": "var(--kd-surface)",
503
+ "--node-stroke": "var(--kd-muted)",
504
+ strokeDash: "5 3.5"
505
+ },
506
+ edge: {
507
+ "--edge-stroke": "var(--kd-muted)",
508
+ strokeDash: "5 3.5"
509
+ },
510
+ fragment: tint("--kd-muted", 2.5, 26)
511
+ },
512
+ {
513
+ aliases: ["info", "accent"],
514
+ node: {
515
+ "--node-fill": "var(--kd-service-fill)",
516
+ "--node-stroke": "var(--kd-accent)"
517
+ },
518
+ edge: { "--edge-stroke": "var(--kd-accent)" },
519
+ fragment: tint("--kd-accent", 3, 28)
520
+ }
521
+ ];
522
+ function stylesFor(target, names, properties) {
523
+ return names.map((name) => ({
524
+ name,
525
+ target,
526
+ properties: { ...properties }
527
+ }));
528
+ }
529
+ /** Canonical names users should prefer in docs (`is danger`, not only aliases). */
530
+ const BUILTIN_SEMANTIC_STYLE_NAMES = [
531
+ "danger",
532
+ "warning",
533
+ "success",
534
+ "critical",
535
+ "muted",
536
+ "info"
537
+ ];
538
+ const BUILTIN_STYLE_DEFINITIONS = SEMANTICS.flatMap((pack) => [
539
+ ...stylesFor("node", pack.aliases, pack.node),
540
+ ...stylesFor("edge", pack.aliases, pack.edge),
541
+ ...stylesFor("fragment", pack.aliases, pack.fragment)
542
+ ]);
543
+ /** Merge authored styles over builtins (same name + target wins for authored). */
544
+ function withBuiltinStyles(authored) {
545
+ const key = (s) => `${s.target ?? "node"}:${s.name}`;
546
+ const map = /* @__PURE__ */ new Map();
547
+ for (const s of BUILTIN_STYLE_DEFINITIONS) map.set(key(s), s);
548
+ for (const s of authored) map.set(key(s), s);
549
+ return [...map.values()];
550
+ }
551
+ //#endregion
552
+ //#region src/theme/resolve-styles.ts
553
+ function applyStyleDef(def, cssVars, badge, strokeWidth, strokeDash) {
554
+ for (const [k, v] of Object.entries(def.properties)) if (k.startsWith("--")) cssVars[k] = v;
555
+ else if (k === "badge") badge = v;
556
+ else if (k === "strokeWidth") strokeWidth = Number(v);
557
+ else if (k === "strokeDash") strokeDash = v;
558
+ return {
559
+ badge,
560
+ strokeWidth,
561
+ strokeDash
562
+ };
563
+ }
564
+ function applyFragmentStyleDef(def, cssVars) {
565
+ for (const [k, v] of Object.entries(def.properties)) if (k.startsWith("--")) cssVars[k] = v;
566
+ else if (k === "fill") cssVars["--kd-sequence-fragment-fill"] = v;
567
+ else if (k === "stroke") cssVars["--kd-sequence-fragment-stroke"] = v;
568
+ }
569
+ function resolveNodeStyles(node, styles) {
570
+ const kindDefaults = getKindDefaults(node.kind).defaults;
571
+ const cssVars = {
572
+ ...kindDefaults.cssVars,
573
+ ...node.unresolvedVars
574
+ };
575
+ let badge;
576
+ let strokeWidth;
577
+ let strokeDash;
578
+ const catalog = withBuiltinStyles(styles);
579
+ for (const ref of node.styleRefs) {
580
+ const def = catalog.find((s) => s.name === ref && (s.target ?? "node") === "node");
581
+ if (!def) continue;
582
+ ({badge, strokeWidth, strokeDash} = applyStyleDef(def, cssVars, badge, strokeWidth, strokeDash));
583
+ }
584
+ if (node.kind === "external" && !strokeDash) strokeDash = "5 3.5";
585
+ const kindClasses = kindDefaults.classNames.filter((c) => c !== `flow-node-${node.kind}`);
586
+ const styleClasses = node.styleRefs.map((r) => `kd-style-${r.replace(/[^a-z0-9-]/gi, "")}`);
587
+ return {
588
+ cssVars,
589
+ classes: [...kindClasses, ...styleClasses],
590
+ badge,
591
+ strokeWidth,
592
+ strokeDash
593
+ };
594
+ }
595
+ function resolveEdgeStyles(edge, styles) {
596
+ const cssVars = {};
597
+ let strokeWidth;
598
+ let strokeDash;
599
+ const catalog = withBuiltinStyles(styles);
600
+ for (const ref of edge.styleRefs) {
601
+ const def = catalog.find((s) => s.name === ref && s.target === "edge");
602
+ if (!def) continue;
603
+ ({strokeWidth, strokeDash} = applyStyleDef(def, cssVars, void 0, strokeWidth, strokeDash));
604
+ }
605
+ return {
606
+ cssVars,
607
+ classes: edge.styleRefs.map((r) => `kd-style-${r.replace(/[^a-z0-9-]/gi, "")}`),
608
+ strokeWidth,
609
+ strokeDash
610
+ };
611
+ }
612
+ /** Resolve semantic / authored styles onto a sequence fragment frame. */
613
+ function resolveFragmentStyles(fragment, styles) {
614
+ const cssVars = { ...fragment.unresolvedVars };
615
+ const catalog = withBuiltinStyles(styles);
616
+ for (const ref of fragment.styleRefs) {
617
+ const def = catalog.find((s) => s.name === ref && s.target === "fragment");
618
+ if (!def) continue;
619
+ applyFragmentStyleDef(def, cssVars);
620
+ }
621
+ return {
622
+ cssVars,
623
+ classes: fragment.styleRefs.map((r) => `kd-style-${r.replace(/[^a-z0-9-]/gi, "")}`)
624
+ };
625
+ }
626
+ function stylesToInlineCss(vars) {
627
+ return Object.entries(vars).map(([k, v]) => `${k}: ${v}`).join("; ");
628
+ }
629
+ //#endregion
630
+ export { BUILTIN_SEMANTIC_STYLE_NAMES, BUILTIN_STYLE_DEFINITIONS, THEME_CSS, branchEdgeClass, branchLabelClass, branchSemantics, branchStrokeColor, getThemeTokens, kindClassName, kindSubtitle, registerTheme, resolveEdgeStyles, resolveFragmentStyles, resolveNodeStyles, stylesToInlineCss, themeToCss, withBuiltinStyles };
631
+
632
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["coreKindSubtitle"],"sources":["../src/theme/tokens.ts","../src/theme/kind-labels.ts","../src/theme/edge-semantics.ts","../src/theme/builtin-styles.ts","../src/theme/resolve-styles.ts"],"sourcesContent":["export type ThemeTokens = Record<string, string>;\n\nconst DARK_TOKENS: ThemeTokens = {\n \"--kd-bg\": \"#070b12\",\n \"--kd-surface\": \"#10161f\",\n \"--kd-surface-2\": \"#171e29\",\n \"--kd-surface-3\": \"#222a36\",\n \"--kd-text\": \"#e8ebf1\",\n \"--kd-muted\": \"#9099a5\",\n \"--kd-border\": \"#343e4a\",\n \"--kd-edge\": \"#7e8792\",\n \"--kd-edge-label-bg\": \"#151c26\",\n \"--kd-edge-label-text\": \"#e8ebf1\",\n \"--kd-edge-label-stroke\": \"#3d4754\",\n \"--kd-accent\": \"#a195f7\",\n \"--kd-danger\": \"#f07f77\",\n \"--kd-danger-fill\": \"#2a1214\",\n \"--kd-warning\": \"#ebbd57\",\n \"--kd-success\": \"#61c594\",\n \"--kd-choice\": \"#a3a0d8\",\n \"--kd-user-fill\": \"#16122a\",\n \"--kd-user-stroke\": \"#a195f7\",\n \"--kd-service-fill\": \"#141222\",\n \"--kd-service-stroke\": \"#a195f7\",\n \"--kd-system-fill\": \"#17102e\",\n \"--kd-system-stroke\": \"#c1bbfc\",\n \"--kd-container-fill\": \"#141222\",\n \"--kd-container-stroke\": \"#a195f7\",\n \"--kd-component-fill\": \"#161822\",\n \"--kd-component-stroke\": \"#9a8fb8\",\n \"--kd-data-fill\": \"#0a1a20\",\n \"--kd-data-stroke\": \"#5bbdda\",\n \"--kd-table-fill\": \"#10161f\",\n \"--kd-table-header-fill\": \"#171e29\",\n \"--kd-table-title\": \"#e8ebf1\",\n \"--kd-table-zebra\": \"rgba(144, 153, 165, 0.06)\",\n \"--kd-table-badge-pk-fill\": \"#7c5cbf\",\n \"--kd-table-badge-fk-fill\": \"#6b5f9a\",\n \"--kd-table-badge-uk-fill\": \"#2f7a62\",\n \"--kd-table-badge-text\": \"#e8ebf1\",\n \"--kd-choice-fill\": \"#1a1b2c\",\n \"--kd-choice-stroke\": \"#a3a0d8\",\n \"--kd-success-fill\": \"#0a2015\",\n \"--kd-success-stroke\": \"#61c594\",\n \"--kd-warning-fill\": \"#271e0a\",\n \"--kd-warning-stroke\": \"#ebbd57\",\n \"--kd-event-fill\": \"#101e17\",\n \"--kd-async-stroke\": \"#61c594\",\n \"--kd-external-fill\": \"#111418\",\n \"--kd-external-stroke\": \"#9099a5\",\n \"--kd-group-fill\": \"rgba(161, 149, 247, 0.1)\",\n \"--kd-group-stroke\": \"rgba(161, 149, 247, 0.42)\",\n \"--kd-node-fill\": \"var(--kd-surface)\",\n \"--kd-node-stroke\": \"var(--kd-border)\",\n \"--kd-node-shadow\": \"rgba(2, 6, 12, 0.45)\",\n \"--kd-node-glow\": \"rgba(161, 149, 247, 0.14)\",\n \"--kd-edge-yes\": \"var(--kd-success)\",\n \"--kd-edge-no\": \"var(--kd-danger)\",\n \"--kd-edge-yes-label-bg\": \"#0f5138\",\n \"--kd-edge-yes-label-text\": \"#ecfdf5\",\n \"--kd-edge-no-label-bg\": \"#8f2a2a\",\n \"--kd-edge-no-label-text\": \"#fef2f2\",\n \"--kd-port-fill\": \"var(--kd-bg)\",\n \"--kd-port-stroke\": \"var(--kd-accent)\",\n \"--kd-sequence-lifeline\": \"var(--kd-edge)\",\n \"--kd-sequence-activation-fill\": \"var(--kd-surface-2)\",\n \"--kd-sequence-activation-stroke\": \"var(--kd-edge)\",\n \"--kd-sequence-fragment-fill\": \"rgba(161, 149, 247, 0.04)\",\n \"--kd-sequence-fragment-stroke\": \"rgba(161, 149, 247, 0.28)\",\n \"--kd-sequence-note-fill\": \"var(--kd-surface-2)\",\n \"--kd-sequence-note-stroke\": \"var(--kd-border)\",\n};\n\nconst LIGHT_TOKENS: ThemeTokens = {\n \"--kd-bg\": \"#f2f5f9\",\n \"--kd-surface\": \"#ffffff\",\n \"--kd-surface-2\": \"#eef2f7\",\n \"--kd-surface-3\": \"#e0e5eb\",\n \"--kd-text\": \"#141b24\",\n \"--kd-muted\": \"#565e69\",\n \"--kd-border\": \"#c4cbd4\",\n \"--kd-edge\": \"#616a75\",\n \"--kd-edge-label-bg\": \"#ffffff\",\n \"--kd-edge-label-text\": \"#141b24\",\n \"--kd-edge-label-stroke\": \"#c4cbd4\",\n \"--kd-accent\": \"#5f38ca\",\n \"--kd-danger\": \"#c53637\",\n \"--kd-danger-fill\": \"#fef2f2\",\n \"--kd-warning\": \"#b67700\",\n \"--kd-success\": \"#007d50\",\n \"--kd-choice\": \"#6360a0\",\n \"--kd-user-fill\": \"#f3eefc\",\n \"--kd-user-stroke\": \"#5f38ca\",\n \"--kd-service-fill\": \"#f5f0fc\",\n \"--kd-service-stroke\": \"#5f38ca\",\n \"--kd-system-fill\": \"#ede5fa\",\n \"--kd-system-stroke\": \"#432990\",\n \"--kd-container-fill\": \"#f5f0fc\",\n \"--kd-container-stroke\": \"#5f38ca\",\n \"--kd-component-fill\": \"#f7f5fb\",\n \"--kd-component-stroke\": \"#6b5f8a\",\n \"--kd-data-fill\": \"#e8f7fb\",\n \"--kd-data-stroke\": \"#0e7490\",\n \"--kd-table-fill\": \"#ffffff\",\n \"--kd-table-header-fill\": \"#eef2f7\",\n \"--kd-table-title\": \"#141b24\",\n \"--kd-table-zebra\": \"rgba(86, 94, 105, 0.07)\",\n \"--kd-table-badge-pk-fill\": \"#5f38ca\",\n \"--kd-table-badge-fk-fill\": \"#6b5f9a\",\n \"--kd-table-badge-uk-fill\": \"#2f7a62\",\n \"--kd-table-badge-text\": \"#ffffff\",\n \"--kd-choice-fill\": \"#f3f2fa\",\n \"--kd-choice-stroke\": \"#6360a0\",\n \"--kd-success-fill\": \"#ecfdf5\",\n \"--kd-success-stroke\": \"#007d50\",\n \"--kd-warning-fill\": \"#fffbeb\",\n \"--kd-warning-stroke\": \"#b67700\",\n \"--kd-event-fill\": \"#f0fdf4\",\n \"--kd-async-stroke\": \"#007d50\",\n \"--kd-external-fill\": \"#f8fafc\",\n \"--kd-external-stroke\": \"#565e69\",\n \"--kd-group-fill\": \"rgba(95, 56, 202, 0.07)\",\n \"--kd-group-stroke\": \"rgba(95, 56, 202, 0.32)\",\n \"--kd-node-fill\": \"var(--kd-surface)\",\n \"--kd-node-stroke\": \"var(--kd-border)\",\n \"--kd-node-shadow\": \"rgba(20, 27, 36, 0.12)\",\n \"--kd-node-glow\": \"rgba(95, 56, 202, 0.08)\",\n \"--kd-edge-yes\": \"var(--kd-success)\",\n \"--kd-edge-no\": \"var(--kd-danger)\",\n \"--kd-edge-yes-label-bg\": \"#007d50\",\n \"--kd-edge-yes-label-text\": \"#ffffff\",\n \"--kd-edge-no-label-bg\": \"#c53637\",\n \"--kd-edge-no-label-text\": \"#ffffff\",\n \"--kd-port-fill\": \"var(--kd-bg)\",\n \"--kd-port-stroke\": \"var(--kd-accent)\",\n \"--kd-sequence-lifeline\": \"var(--kd-edge)\",\n \"--kd-sequence-activation-fill\": \"var(--kd-surface-2)\",\n \"--kd-sequence-activation-stroke\": \"var(--kd-edge)\",\n \"--kd-sequence-fragment-fill\": \"rgba(95, 56, 202, 0.03)\",\n \"--kd-sequence-fragment-stroke\": \"rgba(95, 56, 202, 0.22)\",\n \"--kd-sequence-note-fill\": \"var(--kd-surface-2)\",\n \"--kd-sequence-note-stroke\": \"var(--kd-border)\",\n};\n\nconst registeredThemes = new Map<string, ThemeTokens>();\n\nexport function getThemeTokens(mode: string): ThemeTokens {\n const registered = registeredThemes.get(mode);\n if (registered) return { ...registered };\n return mode === \"light\" ? { ...LIGHT_TOKENS } : { ...DARK_TOKENS };\n}\n\nexport function registerTheme(name: string, tokens: ThemeTokens): void {\n registeredThemes.set(name, tokens);\n}\n\nexport function themeToCss(mode: string, snapshot = false): string {\n if (!snapshot) return THEME_CSS;\n const tokens = getThemeTokens(mode);\n const lines = Object.entries(tokens).map(([k, v]) => ` ${k}: ${v};`);\n // Keep snapshot tokens scoped to the SVG itself. `:root` works for an SVG\n // file opened on its own, but targets the host document when that same SVG\n // is inlined into HTML and lets page-level tokens override the snapshot.\n return `.k-diagram {\\n${lines.join(\"\\n\")}\\n}\\n${THEME_CSS}`;\n}\n\nexport function kindClassName(kind: string): string {\n return `flow-node-${kind.replace(/[^a-z0-9-]/gi, \"\")}`;\n}\n\nexport const THEME_CSS = `\n.k-diagram {\n font-family: \"Inter\", \"Segoe UI\", system-ui, sans-serif;\n}\n.flow-node-shell { fill: var(--node-fill, var(--kd-node-fill)); stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }\n.flow-node-shell-rim { pointer-events: none; }\n.flow-node-title { fill: var(--kd-text); font-size: 15px; font-weight: 800; letter-spacing: -0.02em; }\n.flow-node-subtitle { fill: var(--kd-muted); font-size: 10.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; }\n.flow-node-technology { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; font-style: italic; }\n.flow-node-description { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; opacity: 0.92; }\n.flow-node-note-text { fill: var(--kd-muted); font-size: 9px; font-style: italic; }\n.flow-node-icon-glyph { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: 0.88; }\n.flow-port-marker { fill: var(--node-stroke, var(--kd-node-stroke)); stroke: none; }\n.flow-node-user, .flow-node-actor, .flow-node-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }\n.flow-node-participant { --node-fill: var(--kd-surface); --node-stroke: var(--kd-border); }\n.flow-node-system { --node-fill: var(--kd-system-fill); --node-stroke: var(--kd-system-stroke); }\n.flow-node-system .flow-node-shell { stroke-width: 2; }\n.flow-node-container { --node-fill: var(--kd-container-fill); --node-stroke: var(--kd-container-stroke); }\n.flow-node-component { --node-fill: var(--kd-component-fill); --node-stroke: var(--kd-component-stroke); }\n.flow-node-service, .flow-node-worker, .flow-node-application, .flow-node-module, .flow-node-process, .flow-node-task, .flow-node-subprocess, .flow-node-command, .flow-node-producer, .flow-node-consumer, .flow-node-job, .flow-node-aggregate, .flow-node-projection, .flow-node-interface, .flow-node-deployment, .flow-node-server, .flow-node-state { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-node-database, .flow-node-entity, .flow-node-cache, .flow-node-store, .flow-node-bucket { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }\n.flow-node-table, .flow-node-class { --node-fill: var(--kd-table-fill, var(--kd-service-fill)); --node-stroke: var(--kd-service-stroke); }\n.flow-node-gateway, .flow-node-broker, .flow-node-loadbalancer, .flow-node-firewall, .flow-node-preparation, .flow-node-policy { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-queue, .flow-node-dlq, .flow-node-topic, .flow-node-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-event, .flow-node-start, .flow-node-end, .flow-node-branch, .flow-node-tag { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }\n.flow-node-choice, .flow-node-decision { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }\n.flow-node-warning, .flow-node-failure, .flow-node-manual { --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke); }\n.flow-node-success { --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke); }\n.flow-node-external { --node-fill: var(--kd-external-fill); --node-stroke: var(--kd-external-stroke); --node-stroke-dash: 5 3.5; }\n.flow-node-note, .flow-node-document, .flow-node-message, .flow-node-package { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }\n.flow-node-cloud, .flow-node-zone, .flow-node-group, .flow-node-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-node-io { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-connector, .flow-node-initial, .flow-node-final, .flow-node-junction, .flow-node-commit { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }\n/* Icon-only kind (node shell) — do not put glyph color rules here. */\n.flow-node-icon {\n --node-fill: color-mix(in srgb, var(--kd-node-fill) 88%, var(--kd-accent, var(--kd-text)) 12%);\n --node-stroke: var(--kd-node-stroke);\n}\n/* Glyph wrapper — --icon-color for icon-only tint; else node stroke / theme. */\n.flow-node-icon-mark {\n color: var(--icon-color, var(--node-stroke, var(--kd-node-stroke)));\n}\n.flow-node-fork, .flow-node-join { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }\n.flow-node-rectangle, .flow-node-rounded, .flow-shape-rectangle, .flow-shape-rounded { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-node-pill, .flow-shape-pill { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }\n.flow-node-diamond, .flow-shape-diamond { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }\n.flow-node-hexagon, .flow-shape-hexagon { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-cylinder, .flow-shape-cylinder { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }\n.flow-node-ellipse, .flow-shape-ellipse, .flow-node-circle, .flow-shape-circle { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }\n.flow-node-triangle, .flow-shape-triangle, .flow-node-parallelogram, .flow-shape-parallelogram, .flow-node-trapezoid, .flow-shape-trapezoid { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-folded-document, .flow-shape-folded-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }\n.flow-shape-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }\n.flow-shape-cloud, .flow-shape-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }\n.flow-shape-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }\n.flow-shape-queue, .flow-shape-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }\n.flow-node-shell-partition { pointer-events: none; }\n/* Built-in semantic styles (is danger, is success, ...) — also applied via StyleDefinition. */\n.kd-style-danger, .kd-style-dangerous, .kd-style-error, .kd-style-fail, .kd-style-failure,\n.kd-style-critical {\n --node-fill: var(--kd-danger-fill); --node-stroke: var(--kd-danger);\n}\n.kd-style-warning, .kd-style-warn, .kd-style-caution {\n --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke);\n}\n.kd-style-success, .kd-style-ok, .kd-style-pass {\n --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke);\n}\n.kd-style-muted, .kd-style-deprecated, .kd-style-inactive {\n --node-fill: var(--kd-surface); --node-stroke: var(--kd-muted); --node-stroke-dash: 5 3.5;\n}\n.kd-style-info, .kd-style-accent {\n --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent);\n}\n.flow-table-shell { stroke: none; }\n.flow-table-border { fill: none; stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.5; }\n.flow-table-title { fill: var(--kd-table-title, var(--kd-text)); font-weight: 700; letter-spacing: -0.01em; }\n.flow-table-col-name { fill: var(--kd-text); font-weight: 600; letter-spacing: -0.01em; }\n.flow-table-col-pk { font-weight: 700; }\n.flow-table-col-type { fill: var(--kd-muted); font-weight: 500; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0; font-size: 10px; }\n.flow-table-col-nn { fill: var(--kd-muted); font-weight: 700; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.02em; font-size: 9px; opacity: 0.72; }\n.flow-table-col-note { fill: var(--kd-muted); font-weight: 500; opacity: 0.78; letter-spacing: 0; font-size: 9.5px; }\n.flow-table-badge-pk { fill: var(--kd-table-badge-pk-fill); }\n.flow-table-badge-fk { fill: var(--kd-table-badge-fk-fill); }\n.flow-table-badge-uk { fill: var(--kd-table-badge-uk-fill); }\n.flow-table-badge-text { fill: var(--kd-table-badge-text); font-weight: 800; letter-spacing: 0.02em; }\n.flow-table-zebra { pointer-events: none; }\n.flow-group-box { fill: var(--kd-group-fill); stroke: var(--kd-group-stroke); }\n.flow-group-header-bar { pointer-events: none; }\n.flow-group-header-accent { pointer-events: none; }\n.flow-group-header-rule { pointer-events: none; }\n.flow-group-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }\n.flow-group-label-icon { color: var(--icon-color, var(--kd-muted)); }\n.flow-group-chromeless { pointer-events: none; }\n.flow-edge-label-bg { fill: var(--kd-edge-label-bg); stroke: var(--kd-edge-label-stroke); stroke-width: 1; }\n.flow-edge-label-leader { stroke: var(--kd-edge-label-stroke); stroke-width: 1; fill: none; opacity: 0.85; }\n.flow-edge-label-text { fill: var(--kd-edge-label-text); font-size: 11px; font-weight: 700; letter-spacing: 0.02em; }\n.flow-edge-label-icon { color: var(--icon-color, var(--kd-edge-label-text)); }\n.flow-debug-port { fill: var(--kd-accent); stroke: var(--kd-bg); stroke-width: 1.5; opacity: 0.95; }\n.flow-debug-port-source { fill: var(--kd-edge-yes, var(--kd-accent)); }\n.flow-debug-port-target { fill: var(--kd-edge-no, var(--kd-danger, var(--kd-accent))); }\n.flow-edge-endpoint { stroke: var(--kd-bg); stroke-width: 1.25; opacity: 0.95; }\n.flow-canvas-title-text { fill: var(--kd-text); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }\n.flow-canvas-subtitle-text { fill: var(--kd-muted); font-size: 13px; font-weight: 500; letter-spacing: 0.01em; }\n.flow-canvas-legend-text { fill: var(--kd-muted); font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }\n.flow-group-label-bar { fill: var(--kd-text); font-weight: 700; }\n.flow-edge-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }\n.flow-sequence-message-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }\n.flow-sequence-message-label { fill: var(--kd-text); font-size: 11px; font-weight: 700; letter-spacing: 0.01em; }\n.flow-sequence-fragment-box { fill: var(--kd-sequence-fragment-fill); stroke: var(--kd-sequence-fragment-stroke); }\n.flow-sequence-fragment-operand {\n fill: var(--kd-sequence-fragment-fill, transparent);\n stroke: none;\n pointer-events: none;\n}\n.flow-sequence-fragment-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }\n.flow-sequence-divider-label { fill: var(--kd-muted); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; }\n.flow-sequence-note-text { fill: var(--kd-text); font-size: 11px; font-weight: 500; }\n.flow-edge-label-yes .flow-edge-label-bg { fill: var(--kd-edge-yes-label-bg); stroke: var(--kd-edge-yes); }\n.flow-edge-label-yes .flow-edge-label-text { fill: var(--kd-edge-yes-label-text); }\n.flow-edge-label-no .flow-edge-label-bg { fill: var(--kd-edge-no-label-bg); stroke: var(--kd-edge-no); }\n.flow-edge-label-no .flow-edge-label-text { fill: var(--kd-edge-no-label-text); }\n.flow-edge-branch-yes { stroke: var(--kd-edge-yes); }\n.flow-edge-branch-no { stroke: var(--kd-edge-no); }\n.flow-edge-async { stroke-dasharray: 6 4; }\n.flow-edge-eventual { stroke-dasharray: 2 6; opacity: 0.85; }\n.flow-edge-association { stroke-dasharray: 1 0; opacity: 0.75; }\n.flow-edge-dependency { stroke-dasharray: 2 4; opacity: 0.82; }\n.flow-edge-failure { stroke: var(--kd-danger); }\n\n/* Diagram animation emphasis (interactive player)\n * Resolve --kd-* on .k-diagram (SVG root), not on .kdiagram-viewport —\n * intermediate vars on the viewport can't see tokens defined on the SVG child.\n * Active emphasis must NOT replace semantic strokes (yes/no, kind, danger, …).\n */\n.k-diagram {\n --kd-anim-dim-opacity: 0.62;\n /* Soft haze — keep presence via wide blur, not a tight bright ring. */\n --kd-anim-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 18%, transparent);\n --kd-anim-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 9%, transparent);\n /* Pulse peak — stronger than steady active so the beat reads clearly. */\n --kd-anim-pulse-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 36%, transparent);\n --kd-anim-pulse-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 16%, transparent);\n /* Fallback only when an edge has no resolvable stroke (flow overlays set inline). */\n --kd-anim-flow-stroke: color-mix(in srgb, var(--kd-accent, #a195f7) 55%, white);\n --kd-anim-flow-core: color-mix(in srgb, var(--kd-accent, #a195f7) 30%, white);\n}\n.k-diagram .flow-anim-dim {\n opacity: var(--kd-anim-dim-opacity);\n}\n.k-diagram .flow-anim-active {\n opacity: 1 !important;\n}\n.k-diagram .flow-anim-active.flow-node {\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 22px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-active.flow-node :is(.flow-node-shell, rect.flow-node-shell) {\n stroke-width: 2.5px;\n}\n.k-diagram .flow-anim-active.flow-edge .flow-edge-path,\n.k-diagram .flow-anim-active.flow-edge .flow-edge-jump,\n.k-diagram .flow-anim-active.flow-edge .flow-sequence-message-path {\n stroke-width: 2.6px;\n stroke-opacity: 1;\n filter: drop-shadow(0 0 6px var(--kd-anim-glow))\n drop-shadow(0 0 16px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-active.flow-sequence-fragment .flow-sequence-fragment-box {\n stroke-width: 2.2px;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-active.flow-sequence-fragment-operand {\n filter: drop-shadow(0 0 6px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-pulse {\n animation: flow-anim-pulse 0.65s ease-in-out infinite;\n}\n@keyframes flow-anim-pulse {\n 0%, 100% {\n filter: drop-shadow(0 0 6px var(--kd-anim-glow))\n drop-shadow(0 0 14px var(--kd-anim-glow-soft));\n }\n 50% {\n filter: drop-shadow(0 0 14px var(--kd-anim-pulse-glow))\n drop-shadow(0 0 36px var(--kd-anim-pulse-glow-soft));\n }\n}\n.k-diagram .flow-anim-flow-track {\n fill: none;\n stroke: color-mix(in srgb, var(--kd-anim-flow-stroke) 40%, transparent);\n stroke-width: 5;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n}\n.k-diagram .flow-anim-flow-overlay {\n fill: none;\n stroke: var(--kd-anim-flow-stroke);\n stroke-width: 3.75;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n}\n.k-diagram .flow-anim-flow-core {\n fill: none;\n stroke: var(--kd-anim-flow-core);\n stroke-width: 1.75;\n stroke-linecap: round;\n stroke-linejoin: round;\n pointer-events: none;\n}\n.k-diagram .flow-anim-flow-head {\n fill: var(--kd-anim-flow-core);\n stroke: var(--kd-anim-flow-stroke);\n stroke-width: 1.5;\n pointer-events: none;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n}\n@media (prefers-reduced-motion: reduce) {\n .k-diagram .flow-anim-pulse {\n animation: none;\n filter: drop-shadow(0 0 8px var(--kd-anim-glow))\n drop-shadow(0 0 18px var(--kd-anim-glow-soft));\n }\n}\n`;\n","import { kindSubtitle as coreKindSubtitle } from \"@kekonic/diagrams-core\";\n\n/** Human-readable kind subtitles for node chrome (not debug identifiers). */\nexport function kindSubtitle(kind: string): string {\n return coreKindSubtitle(kind);\n}\n","import { classifyBranch, type BranchKind } from \"@kekonic/diagrams-core\";\n\n/** Semantic branch styling for edge labels and strokes. */\nexport type BranchSemantics = BranchKind;\n\n/** Prefer compiled `GraphEdge.branch`; fall back to label classification. */\nexport function branchSemantics(label?: string, branch?: BranchKind): BranchSemantics {\n if (branch) return branch;\n return classifyBranch(label);\n}\n\nexport function branchEdgeClass(semantics: BranchSemantics): string {\n if (semantics === \"yes\") return \"flow-edge-branch-yes\";\n if (semantics === \"no\") return \"flow-edge-branch-no\";\n return \"\";\n}\n\nexport function branchLabelClass(semantics: BranchSemantics): string {\n if (semantics === \"yes\") return \"flow-edge-label-yes\";\n if (semantics === \"no\") return \"flow-edge-label-no\";\n return \"\";\n}\n\nexport function branchStrokeColor(semantics: BranchSemantics, fallback: string): string {\n if (semantics === \"yes\") return \"var(--kd-edge-yes)\";\n if (semantics === \"no\") return \"var(--kd-edge-no)\";\n return fallback;\n}\n","import type { StyleDefinition } from \"@kekonic/diagrams-core\";\n\n/**\n * Built-in semantic styles — attach with `is danger` / `styles: [success]` without\n * declaring a `style` block. Authored styles with the same name override these.\n */\n\ntype SemanticPack = {\n aliases: string[];\n node: Record<string, string>;\n edge: Record<string, string>;\n fragment: Record<string, string>;\n};\n\nfunction tint(token: string, fillPct: number, strokePct: number): Record<string, string> {\n return {\n \"--kd-sequence-fragment-fill\": `color-mix(in srgb, var(${token}) ${fillPct}%, transparent)`,\n \"--kd-sequence-fragment-stroke\": `color-mix(in srgb, var(${token}) ${strokePct}%, transparent)`,\n };\n}\n\nconst SEMANTICS: SemanticPack[] = [\n {\n aliases: [\"danger\", \"dangerous\", \"error\", \"fail\", \"failed\", \"failure\"],\n node: {\n \"--node-fill\": \"var(--kd-danger-fill)\",\n \"--node-stroke\": \"var(--kd-danger)\",\n badge: \"!\",\n },\n edge: { \"--edge-stroke\": \"var(--kd-danger)\", strokeWidth: \"2.4\" },\n fragment: tint(\"--kd-danger\", 3.5, 34),\n },\n {\n aliases: [\"warning\", \"warn\", \"caution\"],\n node: {\n \"--node-fill\": \"var(--kd-warning-fill)\",\n \"--node-stroke\": \"var(--kd-warning-stroke)\",\n badge: \"!\",\n },\n edge: { \"--edge-stroke\": \"var(--kd-warning)\", strokeWidth: \"2.2\" },\n fragment: tint(\"--kd-warning\", 3.5, 32),\n },\n {\n aliases: [\"success\", \"ok\", \"pass\"],\n node: {\n \"--node-fill\": \"var(--kd-success-fill)\",\n \"--node-stroke\": \"var(--kd-success-stroke)\",\n badge: \"✓\",\n },\n edge: { \"--edge-stroke\": \"var(--kd-success)\", strokeWidth: \"2.2\" },\n fragment: tint(\"--kd-success\", 3.5, 32),\n },\n {\n aliases: [\"critical\"],\n node: {\n \"--node-fill\": \"var(--kd-danger-fill)\",\n \"--node-stroke\": \"var(--kd-danger)\",\n badge: \"!\",\n strokeWidth: \"2.6\",\n },\n edge: { \"--edge-stroke\": \"var(--kd-danger)\", strokeWidth: \"3\" },\n fragment: tint(\"--kd-danger\", 5, 40),\n },\n {\n aliases: [\"muted\", \"deprecated\", \"inactive\"],\n node: {\n \"--node-fill\": \"var(--kd-surface)\",\n \"--node-stroke\": \"var(--kd-muted)\",\n strokeDash: \"5 3.5\",\n },\n edge: { \"--edge-stroke\": \"var(--kd-muted)\", strokeDash: \"5 3.5\" },\n fragment: tint(\"--kd-muted\", 2.5, 26),\n },\n {\n aliases: [\"info\", \"accent\"],\n node: {\n \"--node-fill\": \"var(--kd-service-fill)\",\n \"--node-stroke\": \"var(--kd-accent)\",\n },\n edge: { \"--edge-stroke\": \"var(--kd-accent)\" },\n fragment: tint(\"--kd-accent\", 3, 28),\n },\n];\n\nfunction stylesFor(\n target: \"node\" | \"edge\" | \"fragment\",\n names: string[],\n properties: Record<string, string>,\n): StyleDefinition[] {\n return names.map((name) => ({ name, target, properties: { ...properties } }));\n}\n\n/** Canonical names users should prefer in docs (`is danger`, not only aliases). */\nexport const BUILTIN_SEMANTIC_STYLE_NAMES = [\n \"danger\",\n \"warning\",\n \"success\",\n \"critical\",\n \"muted\",\n \"info\",\n] as const;\n\nexport type BuiltinSemanticStyleName = (typeof BUILTIN_SEMANTIC_STYLE_NAMES)[number];\n\nexport const BUILTIN_STYLE_DEFINITIONS: readonly StyleDefinition[] = SEMANTICS.flatMap((pack) => [\n ...stylesFor(\"node\", pack.aliases, pack.node),\n ...stylesFor(\"edge\", pack.aliases, pack.edge),\n ...stylesFor(\"fragment\", pack.aliases, pack.fragment),\n]);\n\n/** Merge authored styles over builtins (same name + target wins for authored). */\nexport function withBuiltinStyles(authored: StyleDefinition[]): StyleDefinition[] {\n const key = (s: StyleDefinition) => `${s.target ?? \"node\"}:${s.name}`;\n const map = new Map<string, StyleDefinition>();\n for (const s of BUILTIN_STYLE_DEFINITIONS) map.set(key(s), s);\n for (const s of authored) map.set(key(s), s);\n return [...map.values()];\n}\n","import type {\n GraphEdge,\n GraphNode,\n SequenceFragment,\n StyleDefinition,\n} from \"@kekonic/diagrams-core\";\nimport { getKindDefaults } from \"@kekonic/diagrams-core\";\nimport { withBuiltinStyles } from \"./builtin-styles.ts\";\n\nexport type ResolvedStyles = {\n cssVars: Record<string, string>;\n classes: string[];\n badge?: string;\n strokeWidth?: number;\n strokeDash?: string;\n};\n\nfunction applyStyleDef(\n def: StyleDefinition,\n cssVars: Record<string, string>,\n badge: string | undefined,\n strokeWidth: number | undefined,\n strokeDash: string | undefined,\n): { badge?: string; strokeWidth?: number; strokeDash?: string } {\n for (const [k, v] of Object.entries(def.properties)) {\n if (k.startsWith(\"--\")) cssVars[k] = v;\n else if (k === \"badge\") badge = v;\n else if (k === \"strokeWidth\") strokeWidth = Number(v);\n else if (k === \"strokeDash\") strokeDash = v;\n }\n return { badge, strokeWidth, strokeDash };\n}\n\nfunction applyFragmentStyleDef(def: StyleDefinition, cssVars: Record<string, string>): void {\n for (const [k, v] of Object.entries(def.properties)) {\n if (k.startsWith(\"--\")) cssVars[k] = v;\n else if (k === \"fill\") cssVars[\"--kd-sequence-fragment-fill\"] = v;\n else if (k === \"stroke\") cssVars[\"--kd-sequence-fragment-stroke\"] = v;\n }\n}\n\nexport function resolveNodeStyles(node: GraphNode, styles: StyleDefinition[]): ResolvedStyles {\n const kindDefaults = getKindDefaults(node.kind).defaults;\n const cssVars: Record<string, string> = { ...kindDefaults.cssVars, ...node.unresolvedVars };\n let badge: string | undefined;\n let strokeWidth: number | undefined;\n let strokeDash: string | undefined;\n const catalog = withBuiltinStyles(styles);\n\n for (const ref of node.styleRefs) {\n const def = catalog.find((s) => s.name === ref && (s.target ?? \"node\") === \"node\");\n if (!def) continue;\n ({ badge, strokeWidth, strokeDash } = applyStyleDef(\n def,\n cssVars,\n badge,\n strokeWidth,\n strokeDash,\n ));\n }\n\n if (node.kind === \"external\" && !strokeDash) strokeDash = \"5 3.5\";\n\n // Extra kind classes (e.g. flow-shape-diamond) beyond the primary flow-node-${kind}.\n const kindClasses = kindDefaults.classNames.filter((c) => c !== `flow-node-${node.kind}`);\n const styleClasses = node.styleRefs.map((r) => `kd-style-${r.replace(/[^a-z0-9-]/gi, \"\")}`);\n\n return {\n cssVars,\n classes: [...kindClasses, ...styleClasses],\n badge,\n strokeWidth,\n strokeDash,\n };\n}\n\nexport function resolveEdgeStyles(edge: GraphEdge, styles: StyleDefinition[]): ResolvedStyles {\n const cssVars: Record<string, string> = {};\n let strokeWidth: number | undefined;\n let strokeDash: string | undefined;\n const catalog = withBuiltinStyles(styles);\n\n for (const ref of edge.styleRefs) {\n const def = catalog.find((s) => s.name === ref && s.target === \"edge\");\n if (!def) continue;\n ({ strokeWidth, strokeDash } = applyStyleDef(def, cssVars, undefined, strokeWidth, strokeDash));\n }\n\n return {\n cssVars,\n classes: edge.styleRefs.map((r) => `kd-style-${r.replace(/[^a-z0-9-]/gi, \"\")}`),\n strokeWidth,\n strokeDash,\n };\n}\n\n/** Resolve semantic / authored styles onto a sequence fragment frame. */\nexport function resolveFragmentStyles(\n fragment: Pick<SequenceFragment, \"styleRefs\" | \"unresolvedVars\">,\n styles: StyleDefinition[],\n): ResolvedStyles {\n const cssVars: Record<string, string> = { ...fragment.unresolvedVars };\n const catalog = withBuiltinStyles(styles);\n\n for (const ref of fragment.styleRefs) {\n const def = catalog.find((s) => s.name === ref && s.target === \"fragment\");\n if (!def) continue;\n applyFragmentStyleDef(def, cssVars);\n }\n\n return {\n cssVars,\n classes: fragment.styleRefs.map((r) => `kd-style-${r.replace(/[^a-z0-9-]/gi, \"\")}`),\n };\n}\n\nexport function stylesToInlineCss(vars: Record<string, string>): string {\n return Object.entries(vars)\n .map(([k, v]) => `${k}: ${v}`)\n .join(\"; \");\n}\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,46 @@
1
+ {
2
+ "name": "@kekonic/diagrams-theme",
3
+ "version": "1.0.0-rc.4",
4
+ "description": "KDiagram dark/light theme tokens, custom theme registry, and CSS generation.",
5
+ "homepage": "https://github.com/kekonic/diagrams#readme",
6
+ "bugs": {
7
+ "url": "https://github.com/kekonic/diagrams/issues"
8
+ },
9
+ "license": "MIT",
10
+ "repository": {
11
+ "type": "git",
12
+ "url": "git+https://github.com/kekonic/diagrams.git",
13
+ "directory": "packages/diagrams-theme"
14
+ },
15
+ "files": [
16
+ "dist",
17
+ "theme.css"
18
+ ],
19
+ "type": "module",
20
+ "exports": {
21
+ ".": {
22
+ "types": "./dist/index.d.mts",
23
+ "import": "./dist/index.mjs"
24
+ },
25
+ "./theme.css": "./theme.css"
26
+ },
27
+ "publishConfig": {
28
+ "access": "public"
29
+ },
30
+ "dependencies": {
31
+ "@kekonic/diagrams-core": "1.0.0-rc.4"
32
+ },
33
+ "devDependencies": {
34
+ "@types/node": "^26",
35
+ "typescript": "^5",
36
+ "vitest": "^4"
37
+ },
38
+ "engines": {
39
+ "node": ">=22.18.0"
40
+ },
41
+ "scripts": {
42
+ "build": "vp pack && node ./scripts/generate-theme-css.mjs",
43
+ "generate:theme-css": "vp pack && node ./scripts/generate-theme-css.mjs",
44
+ "test": "vp test"
45
+ }
46
+ }
package/theme.css ADDED
@@ -0,0 +1,373 @@
1
+ /* Generated by scripts/generate-theme-css.mjs — do not edit by hand. */
2
+
3
+ .k-diagram, .k-diagram[data-theme="dark"], .k-diagram.kdiagram-theme-dark, :root, :root[data-theme="dark"] {
4
+ --kd-bg: #070b12;
5
+ --kd-surface: #10161f;
6
+ --kd-surface-2: #171e29;
7
+ --kd-surface-3: #222a36;
8
+ --kd-text: #e8ebf1;
9
+ --kd-muted: #9099a5;
10
+ --kd-border: #343e4a;
11
+ --kd-edge: #7e8792;
12
+ --kd-edge-label-bg: #151c26;
13
+ --kd-edge-label-text: #e8ebf1;
14
+ --kd-edge-label-stroke: #3d4754;
15
+ --kd-accent: #a195f7;
16
+ --kd-danger: #f07f77;
17
+ --kd-danger-fill: #2a1214;
18
+ --kd-warning: #ebbd57;
19
+ --kd-success: #61c594;
20
+ --kd-choice: #a3a0d8;
21
+ --kd-user-fill: #16122a;
22
+ --kd-user-stroke: #a195f7;
23
+ --kd-service-fill: #141222;
24
+ --kd-service-stroke: #a195f7;
25
+ --kd-system-fill: #17102e;
26
+ --kd-system-stroke: #c1bbfc;
27
+ --kd-container-fill: #141222;
28
+ --kd-container-stroke: #a195f7;
29
+ --kd-component-fill: #161822;
30
+ --kd-component-stroke: #9a8fb8;
31
+ --kd-data-fill: #0a1a20;
32
+ --kd-data-stroke: #5bbdda;
33
+ --kd-table-fill: #10161f;
34
+ --kd-table-header-fill: #171e29;
35
+ --kd-table-title: #e8ebf1;
36
+ --kd-table-zebra: rgba(144, 153, 165, 0.06);
37
+ --kd-table-badge-pk-fill: #7c5cbf;
38
+ --kd-table-badge-fk-fill: #6b5f9a;
39
+ --kd-table-badge-uk-fill: #2f7a62;
40
+ --kd-table-badge-text: #e8ebf1;
41
+ --kd-choice-fill: #1a1b2c;
42
+ --kd-choice-stroke: #a3a0d8;
43
+ --kd-success-fill: #0a2015;
44
+ --kd-success-stroke: #61c594;
45
+ --kd-warning-fill: #271e0a;
46
+ --kd-warning-stroke: #ebbd57;
47
+ --kd-event-fill: #101e17;
48
+ --kd-async-stroke: #61c594;
49
+ --kd-external-fill: #111418;
50
+ --kd-external-stroke: #9099a5;
51
+ --kd-group-fill: rgba(161, 149, 247, 0.1);
52
+ --kd-group-stroke: rgba(161, 149, 247, 0.42);
53
+ --kd-node-fill: var(--kd-surface);
54
+ --kd-node-stroke: var(--kd-border);
55
+ --kd-node-shadow: rgba(2, 6, 12, 0.45);
56
+ --kd-node-glow: rgba(161, 149, 247, 0.14);
57
+ --kd-edge-yes: var(--kd-success);
58
+ --kd-edge-no: var(--kd-danger);
59
+ --kd-edge-yes-label-bg: #0f5138;
60
+ --kd-edge-yes-label-text: #ecfdf5;
61
+ --kd-edge-no-label-bg: #8f2a2a;
62
+ --kd-edge-no-label-text: #fef2f2;
63
+ --kd-port-fill: var(--kd-bg);
64
+ --kd-port-stroke: var(--kd-accent);
65
+ --kd-sequence-lifeline: var(--kd-edge);
66
+ --kd-sequence-activation-fill: var(--kd-surface-2);
67
+ --kd-sequence-activation-stroke: var(--kd-edge);
68
+ --kd-sequence-fragment-fill: rgba(161, 149, 247, 0.04);
69
+ --kd-sequence-fragment-stroke: rgba(161, 149, 247, 0.28);
70
+ --kd-sequence-note-fill: var(--kd-surface-2);
71
+ --kd-sequence-note-stroke: var(--kd-border);
72
+ }
73
+
74
+ .k-diagram[data-theme="light"], .k-diagram.kdiagram-theme-light, :root[data-theme="light"] {
75
+ --kd-bg: #f2f5f9;
76
+ --kd-surface: #ffffff;
77
+ --kd-surface-2: #eef2f7;
78
+ --kd-surface-3: #e0e5eb;
79
+ --kd-text: #141b24;
80
+ --kd-muted: #565e69;
81
+ --kd-border: #c4cbd4;
82
+ --kd-edge: #616a75;
83
+ --kd-edge-label-bg: #ffffff;
84
+ --kd-edge-label-text: #141b24;
85
+ --kd-edge-label-stroke: #c4cbd4;
86
+ --kd-accent: #5f38ca;
87
+ --kd-danger: #c53637;
88
+ --kd-danger-fill: #fef2f2;
89
+ --kd-warning: #b67700;
90
+ --kd-success: #007d50;
91
+ --kd-choice: #6360a0;
92
+ --kd-user-fill: #f3eefc;
93
+ --kd-user-stroke: #5f38ca;
94
+ --kd-service-fill: #f5f0fc;
95
+ --kd-service-stroke: #5f38ca;
96
+ --kd-system-fill: #ede5fa;
97
+ --kd-system-stroke: #432990;
98
+ --kd-container-fill: #f5f0fc;
99
+ --kd-container-stroke: #5f38ca;
100
+ --kd-component-fill: #f7f5fb;
101
+ --kd-component-stroke: #6b5f8a;
102
+ --kd-data-fill: #e8f7fb;
103
+ --kd-data-stroke: #0e7490;
104
+ --kd-table-fill: #ffffff;
105
+ --kd-table-header-fill: #eef2f7;
106
+ --kd-table-title: #141b24;
107
+ --kd-table-zebra: rgba(86, 94, 105, 0.07);
108
+ --kd-table-badge-pk-fill: #5f38ca;
109
+ --kd-table-badge-fk-fill: #6b5f9a;
110
+ --kd-table-badge-uk-fill: #2f7a62;
111
+ --kd-table-badge-text: #ffffff;
112
+ --kd-choice-fill: #f3f2fa;
113
+ --kd-choice-stroke: #6360a0;
114
+ --kd-success-fill: #ecfdf5;
115
+ --kd-success-stroke: #007d50;
116
+ --kd-warning-fill: #fffbeb;
117
+ --kd-warning-stroke: #b67700;
118
+ --kd-event-fill: #f0fdf4;
119
+ --kd-async-stroke: #007d50;
120
+ --kd-external-fill: #f8fafc;
121
+ --kd-external-stroke: #565e69;
122
+ --kd-group-fill: rgba(95, 56, 202, 0.07);
123
+ --kd-group-stroke: rgba(95, 56, 202, 0.32);
124
+ --kd-node-fill: var(--kd-surface);
125
+ --kd-node-stroke: var(--kd-border);
126
+ --kd-node-shadow: rgba(20, 27, 36, 0.12);
127
+ --kd-node-glow: rgba(95, 56, 202, 0.08);
128
+ --kd-edge-yes: var(--kd-success);
129
+ --kd-edge-no: var(--kd-danger);
130
+ --kd-edge-yes-label-bg: #007d50;
131
+ --kd-edge-yes-label-text: #ffffff;
132
+ --kd-edge-no-label-bg: #c53637;
133
+ --kd-edge-no-label-text: #ffffff;
134
+ --kd-port-fill: var(--kd-bg);
135
+ --kd-port-stroke: var(--kd-accent);
136
+ --kd-sequence-lifeline: var(--kd-edge);
137
+ --kd-sequence-activation-fill: var(--kd-surface-2);
138
+ --kd-sequence-activation-stroke: var(--kd-edge);
139
+ --kd-sequence-fragment-fill: rgba(95, 56, 202, 0.03);
140
+ --kd-sequence-fragment-stroke: rgba(95, 56, 202, 0.22);
141
+ --kd-sequence-note-fill: var(--kd-surface-2);
142
+ --kd-sequence-note-stroke: var(--kd-border);
143
+ }
144
+
145
+ .k-diagram {
146
+ font-family: "Inter", "Segoe UI", system-ui, sans-serif;
147
+ }
148
+ .flow-node-shell { fill: var(--node-fill, var(--kd-node-fill)); stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.6; stroke-linejoin: round; stroke-linecap: round; }
149
+ .flow-node-shell-rim { pointer-events: none; }
150
+ .flow-node-title { fill: var(--kd-text); font-size: 15px; font-weight: 800; letter-spacing: -0.02em; }
151
+ .flow-node-subtitle { fill: var(--kd-muted); font-size: 10.5px; font-weight: 600; letter-spacing: 0.05em; text-transform: uppercase; }
152
+ .flow-node-technology { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; font-style: italic; }
153
+ .flow-node-description { fill: var(--kd-muted); font-size: 11px; font-weight: 500; letter-spacing: 0.01em; opacity: 0.92; }
154
+ .flow-node-note-text { fill: var(--kd-muted); font-size: 9px; font-style: italic; }
155
+ .flow-node-icon-glyph { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; opacity: 0.88; }
156
+ .flow-port-marker { fill: var(--node-stroke, var(--kd-node-stroke)); stroke: none; }
157
+ .flow-node-user, .flow-node-actor, .flow-node-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }
158
+ .flow-node-participant { --node-fill: var(--kd-surface); --node-stroke: var(--kd-border); }
159
+ .flow-node-system { --node-fill: var(--kd-system-fill); --node-stroke: var(--kd-system-stroke); }
160
+ .flow-node-system .flow-node-shell { stroke-width: 2; }
161
+ .flow-node-container { --node-fill: var(--kd-container-fill); --node-stroke: var(--kd-container-stroke); }
162
+ .flow-node-component { --node-fill: var(--kd-component-fill); --node-stroke: var(--kd-component-stroke); }
163
+ .flow-node-service, .flow-node-worker, .flow-node-application, .flow-node-module, .flow-node-process, .flow-node-task, .flow-node-subprocess, .flow-node-command, .flow-node-producer, .flow-node-consumer, .flow-node-job, .flow-node-aggregate, .flow-node-projection, .flow-node-interface, .flow-node-deployment, .flow-node-server, .flow-node-state { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
164
+ .flow-node-database, .flow-node-entity, .flow-node-cache, .flow-node-store, .flow-node-bucket { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }
165
+ .flow-node-table, .flow-node-class { --node-fill: var(--kd-table-fill, var(--kd-service-fill)); --node-stroke: var(--kd-service-stroke); }
166
+ .flow-node-gateway, .flow-node-broker, .flow-node-loadbalancer, .flow-node-firewall, .flow-node-preparation, .flow-node-policy { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
167
+ .flow-node-queue, .flow-node-dlq, .flow-node-topic, .flow-node-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
168
+ .flow-node-event, .flow-node-start, .flow-node-end, .flow-node-branch, .flow-node-tag { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }
169
+ .flow-node-choice, .flow-node-decision { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }
170
+ .flow-node-warning, .flow-node-failure, .flow-node-manual { --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke); }
171
+ .flow-node-success { --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke); }
172
+ .flow-node-external { --node-fill: var(--kd-external-fill); --node-stroke: var(--kd-external-stroke); --node-stroke-dash: 5 3.5; }
173
+ .flow-node-note, .flow-node-document, .flow-node-message, .flow-node-package { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }
174
+ .flow-node-cloud, .flow-node-zone, .flow-node-group, .flow-node-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
175
+ .flow-node-io { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
176
+ .flow-node-connector, .flow-node-initial, .flow-node-final, .flow-node-junction, .flow-node-commit { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }
177
+ /* Icon-only kind (node shell) — do not put glyph color rules here. */
178
+ .flow-node-icon {
179
+ --node-fill: color-mix(in srgb, var(--kd-node-fill) 88%, var(--kd-accent, var(--kd-text)) 12%);
180
+ --node-stroke: var(--kd-node-stroke);
181
+ }
182
+ /* Glyph wrapper — --icon-color for icon-only tint; else node stroke / theme. */
183
+ .flow-node-icon-mark {
184
+ color: var(--icon-color, var(--node-stroke, var(--kd-node-stroke)));
185
+ }
186
+ .flow-node-fork, .flow-node-join { --node-fill: var(--kd-text); --node-stroke: var(--kd-text); }
187
+ .flow-node-rectangle, .flow-node-rounded, .flow-shape-rectangle, .flow-shape-rounded { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
188
+ .flow-node-pill, .flow-shape-pill { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }
189
+ .flow-node-diamond, .flow-shape-diamond { --node-fill: var(--kd-choice-fill); --node-stroke: var(--kd-choice-stroke); }
190
+ .flow-node-hexagon, .flow-shape-hexagon { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
191
+ .flow-node-cylinder, .flow-shape-cylinder { --node-fill: var(--kd-data-fill); --node-stroke: var(--kd-data-stroke); }
192
+ .flow-node-ellipse, .flow-shape-ellipse, .flow-node-circle, .flow-shape-circle { --node-fill: var(--kd-event-fill); --node-stroke: var(--kd-async-stroke); }
193
+ .flow-node-triangle, .flow-shape-triangle, .flow-node-parallelogram, .flow-shape-parallelogram, .flow-node-trapezoid, .flow-shape-trapezoid { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
194
+ .flow-node-folded-document, .flow-shape-folded-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }
195
+ .flow-shape-document { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-muted); }
196
+ .flow-shape-cloud, .flow-shape-boundary { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-service-stroke); }
197
+ .flow-shape-person { --node-fill: var(--kd-user-fill); --node-stroke: var(--kd-user-stroke); }
198
+ .flow-shape-queue, .flow-shape-stream { --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent); }
199
+ .flow-node-shell-partition { pointer-events: none; }
200
+ /* Built-in semantic styles (is danger, is success, ...) — also applied via StyleDefinition. */
201
+ .kd-style-danger, .kd-style-dangerous, .kd-style-error, .kd-style-fail, .kd-style-failure,
202
+ .kd-style-critical {
203
+ --node-fill: var(--kd-danger-fill); --node-stroke: var(--kd-danger);
204
+ }
205
+ .kd-style-warning, .kd-style-warn, .kd-style-caution {
206
+ --node-fill: var(--kd-warning-fill); --node-stroke: var(--kd-warning-stroke);
207
+ }
208
+ .kd-style-success, .kd-style-ok, .kd-style-pass {
209
+ --node-fill: var(--kd-success-fill); --node-stroke: var(--kd-success-stroke);
210
+ }
211
+ .kd-style-muted, .kd-style-deprecated, .kd-style-inactive {
212
+ --node-fill: var(--kd-surface); --node-stroke: var(--kd-muted); --node-stroke-dash: 5 3.5;
213
+ }
214
+ .kd-style-info, .kd-style-accent {
215
+ --node-fill: var(--kd-service-fill); --node-stroke: var(--kd-accent);
216
+ }
217
+ .flow-table-shell { stroke: none; }
218
+ .flow-table-border { fill: none; stroke: var(--node-stroke, var(--kd-node-stroke)); stroke-width: 1.5; }
219
+ .flow-table-title { fill: var(--kd-table-title, var(--kd-text)); font-weight: 700; letter-spacing: -0.01em; }
220
+ .flow-table-col-name { fill: var(--kd-text); font-weight: 600; letter-spacing: -0.01em; }
221
+ .flow-table-col-pk { font-weight: 700; }
222
+ .flow-table-col-type { fill: var(--kd-muted); font-weight: 500; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0; font-size: 10px; }
223
+ .flow-table-col-nn { fill: var(--kd-muted); font-weight: 700; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; letter-spacing: 0.02em; font-size: 9px; opacity: 0.72; }
224
+ .flow-table-col-note { fill: var(--kd-muted); font-weight: 500; opacity: 0.78; letter-spacing: 0; font-size: 9.5px; }
225
+ .flow-table-badge-pk { fill: var(--kd-table-badge-pk-fill); }
226
+ .flow-table-badge-fk { fill: var(--kd-table-badge-fk-fill); }
227
+ .flow-table-badge-uk { fill: var(--kd-table-badge-uk-fill); }
228
+ .flow-table-badge-text { fill: var(--kd-table-badge-text); font-weight: 800; letter-spacing: 0.02em; }
229
+ .flow-table-zebra { pointer-events: none; }
230
+ .flow-group-box { fill: var(--kd-group-fill); stroke: var(--kd-group-stroke); }
231
+ .flow-group-header-bar { pointer-events: none; }
232
+ .flow-group-header-accent { pointer-events: none; }
233
+ .flow-group-header-rule { pointer-events: none; }
234
+ .flow-group-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }
235
+ .flow-group-label-icon { color: var(--icon-color, var(--kd-muted)); }
236
+ .flow-group-chromeless { pointer-events: none; }
237
+ .flow-edge-label-bg { fill: var(--kd-edge-label-bg); stroke: var(--kd-edge-label-stroke); stroke-width: 1; }
238
+ .flow-edge-label-leader { stroke: var(--kd-edge-label-stroke); stroke-width: 1; fill: none; opacity: 0.85; }
239
+ .flow-edge-label-text { fill: var(--kd-edge-label-text); font-size: 11px; font-weight: 700; letter-spacing: 0.02em; }
240
+ .flow-edge-label-icon { color: var(--icon-color, var(--kd-edge-label-text)); }
241
+ .flow-debug-port { fill: var(--kd-accent); stroke: var(--kd-bg); stroke-width: 1.5; opacity: 0.95; }
242
+ .flow-debug-port-source { fill: var(--kd-edge-yes, var(--kd-accent)); }
243
+ .flow-debug-port-target { fill: var(--kd-edge-no, var(--kd-danger, var(--kd-accent))); }
244
+ .flow-edge-endpoint { stroke: var(--kd-bg); stroke-width: 1.25; opacity: 0.95; }
245
+ .flow-canvas-title-text { fill: var(--kd-text); font-size: 22px; font-weight: 700; letter-spacing: -0.02em; }
246
+ .flow-canvas-subtitle-text { fill: var(--kd-muted); font-size: 13px; font-weight: 500; letter-spacing: 0.01em; }
247
+ .flow-canvas-legend-text { fill: var(--kd-muted); font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
248
+ .flow-group-label-bar { fill: var(--kd-text); font-weight: 700; }
249
+ .flow-edge-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
250
+ .flow-sequence-message-path { fill: none; stroke-linecap: round; stroke-linejoin: round; }
251
+ .flow-sequence-message-label { fill: var(--kd-text); font-size: 11px; font-weight: 700; letter-spacing: 0.01em; }
252
+ .flow-sequence-fragment-box { fill: var(--kd-sequence-fragment-fill); stroke: var(--kd-sequence-fragment-stroke); }
253
+ .flow-sequence-fragment-operand {
254
+ fill: var(--kd-sequence-fragment-fill, transparent);
255
+ stroke: none;
256
+ pointer-events: none;
257
+ }
258
+ .flow-sequence-fragment-label { fill: var(--kd-muted); font-size: 11px; font-weight: 700; letter-spacing: 0.04em; }
259
+ .flow-sequence-divider-label { fill: var(--kd-muted); font-size: 11px; font-weight: 600; letter-spacing: 0.02em; }
260
+ .flow-sequence-note-text { fill: var(--kd-text); font-size: 11px; font-weight: 500; }
261
+ .flow-edge-label-yes .flow-edge-label-bg { fill: var(--kd-edge-yes-label-bg); stroke: var(--kd-edge-yes); }
262
+ .flow-edge-label-yes .flow-edge-label-text { fill: var(--kd-edge-yes-label-text); }
263
+ .flow-edge-label-no .flow-edge-label-bg { fill: var(--kd-edge-no-label-bg); stroke: var(--kd-edge-no); }
264
+ .flow-edge-label-no .flow-edge-label-text { fill: var(--kd-edge-no-label-text); }
265
+ .flow-edge-branch-yes { stroke: var(--kd-edge-yes); }
266
+ .flow-edge-branch-no { stroke: var(--kd-edge-no); }
267
+ .flow-edge-async { stroke-dasharray: 6 4; }
268
+ .flow-edge-eventual { stroke-dasharray: 2 6; opacity: 0.85; }
269
+ .flow-edge-association { stroke-dasharray: 1 0; opacity: 0.75; }
270
+ .flow-edge-dependency { stroke-dasharray: 2 4; opacity: 0.82; }
271
+ .flow-edge-failure { stroke: var(--kd-danger); }
272
+
273
+ /* Diagram animation emphasis (interactive player)
274
+ * Resolve --kd-* on .k-diagram (SVG root), not on .kdiagram-viewport —
275
+ * intermediate vars on the viewport can't see tokens defined on the SVG child.
276
+ * Active emphasis must NOT replace semantic strokes (yes/no, kind, danger, …).
277
+ */
278
+ .k-diagram {
279
+ --kd-anim-dim-opacity: 0.62;
280
+ /* Soft haze — keep presence via wide blur, not a tight bright ring. */
281
+ --kd-anim-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 18%, transparent);
282
+ --kd-anim-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 9%, transparent);
283
+ /* Pulse peak — stronger than steady active so the beat reads clearly. */
284
+ --kd-anim-pulse-glow: color-mix(in srgb, var(--kd-text, #e8ebf1) 36%, transparent);
285
+ --kd-anim-pulse-glow-soft: color-mix(in srgb, var(--kd-text, #e8ebf1) 16%, transparent);
286
+ /* Fallback only when an edge has no resolvable stroke (flow overlays set inline). */
287
+ --kd-anim-flow-stroke: color-mix(in srgb, var(--kd-accent, #a195f7) 55%, white);
288
+ --kd-anim-flow-core: color-mix(in srgb, var(--kd-accent, #a195f7) 30%, white);
289
+ }
290
+ .k-diagram .flow-anim-dim {
291
+ opacity: var(--kd-anim-dim-opacity);
292
+ }
293
+ .k-diagram .flow-anim-active {
294
+ opacity: 1 !important;
295
+ }
296
+ .k-diagram .flow-anim-active.flow-node {
297
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
298
+ drop-shadow(0 0 22px var(--kd-anim-glow-soft));
299
+ }
300
+ .k-diagram .flow-anim-active.flow-node :is(.flow-node-shell, rect.flow-node-shell) {
301
+ stroke-width: 2.5px;
302
+ }
303
+ .k-diagram .flow-anim-active.flow-edge .flow-edge-path,
304
+ .k-diagram .flow-anim-active.flow-edge .flow-edge-jump,
305
+ .k-diagram .flow-anim-active.flow-edge .flow-sequence-message-path {
306
+ stroke-width: 2.6px;
307
+ stroke-opacity: 1;
308
+ filter: drop-shadow(0 0 6px var(--kd-anim-glow))
309
+ drop-shadow(0 0 16px var(--kd-anim-glow-soft));
310
+ }
311
+ .k-diagram .flow-anim-active.flow-sequence-fragment .flow-sequence-fragment-box {
312
+ stroke-width: 2.2px;
313
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
314
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
315
+ }
316
+ .k-diagram .flow-anim-active.flow-sequence-fragment-operand {
317
+ filter: drop-shadow(0 0 6px var(--kd-anim-glow-soft));
318
+ }
319
+ .k-diagram .flow-anim-pulse {
320
+ animation: flow-anim-pulse 0.65s ease-in-out infinite;
321
+ }
322
+ @keyframes flow-anim-pulse {
323
+ 0%, 100% {
324
+ filter: drop-shadow(0 0 6px var(--kd-anim-glow))
325
+ drop-shadow(0 0 14px var(--kd-anim-glow-soft));
326
+ }
327
+ 50% {
328
+ filter: drop-shadow(0 0 14px var(--kd-anim-pulse-glow))
329
+ drop-shadow(0 0 36px var(--kd-anim-pulse-glow-soft));
330
+ }
331
+ }
332
+ .k-diagram .flow-anim-flow-track {
333
+ fill: none;
334
+ stroke: color-mix(in srgb, var(--kd-anim-flow-stroke) 40%, transparent);
335
+ stroke-width: 5;
336
+ stroke-linecap: round;
337
+ stroke-linejoin: round;
338
+ pointer-events: none;
339
+ }
340
+ .k-diagram .flow-anim-flow-overlay {
341
+ fill: none;
342
+ stroke: var(--kd-anim-flow-stroke);
343
+ stroke-width: 3.75;
344
+ stroke-linecap: round;
345
+ stroke-linejoin: round;
346
+ pointer-events: none;
347
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
348
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
349
+ }
350
+ .k-diagram .flow-anim-flow-core {
351
+ fill: none;
352
+ stroke: var(--kd-anim-flow-core);
353
+ stroke-width: 1.75;
354
+ stroke-linecap: round;
355
+ stroke-linejoin: round;
356
+ pointer-events: none;
357
+ }
358
+ .k-diagram .flow-anim-flow-head {
359
+ fill: var(--kd-anim-flow-core);
360
+ stroke: var(--kd-anim-flow-stroke);
361
+ stroke-width: 1.5;
362
+ pointer-events: none;
363
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
364
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
365
+ }
366
+ @media (prefers-reduced-motion: reduce) {
367
+ .k-diagram .flow-anim-pulse {
368
+ animation: none;
369
+ filter: drop-shadow(0 0 8px var(--kd-anim-glow))
370
+ drop-shadow(0 0 18px var(--kd-anim-glow-soft));
371
+ }
372
+ }
373
+