feat(central-ui): FlowGraphView SVG swimlane renderer
Dependency-free inline SVG: three fixed 110px lanes (Central / Site / Tags, sublabelled from the steps' site/instance), columns from a topological pass over the graph edges with same-lane collision bumping so independent siblings never overlap, 150x78 nodes on a 130+col*170 grid, orthogonal connectors with arrow markers (accent for boundary crossings, dashed for external actors, light for predicted edges). Renders a real FlowGraph, a PredictedFlow (dashed nodes, input-dependent finer-dashed), or both — predicted nodes matched to real steps by kind in order, the leftovers appended greyed as ghosts. Selected node = accent 2px on --active-bg; failed/timed-out/not-traced outcomes styled; each node a role=button tabindex=0 <g> with click + Enter/Space selection; svg role=img with a summary aria-label. Layout is a pure static ComputeLayout, every coordinate InvariantCulture. Razor reserves the <text> tag, so SVG text elements are emitted through a small HTML-encoding MarkupString helper. Claude-Session: https://claude.ai/code/session_01GSLkkHKz9JrAtmJjmQK3cP
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
/* Flow Debugger swimlane — mirrors the design mockup's values through the kit's
|
||||
CSS variables (theme.css light defaults; site.css dark overrides) so dark mode
|
||||
follows automatically. Border #d2d2cb = --rule-strong, selected 2px #2f5fd0 on
|
||||
#eef2fc = --accent on --active-bg. */
|
||||
|
||||
.fg-wrap {
|
||||
overflow: auto;
|
||||
border: 1px solid var(--rule);
|
||||
border-radius: 4px;
|
||||
background: var(--bs-body-bg, #fff);
|
||||
}
|
||||
|
||||
.fg-svg {
|
||||
display: block;
|
||||
font-family: var(--bs-body-font-family, system-ui, sans-serif);
|
||||
}
|
||||
|
||||
/* Lanes */
|
||||
.fg-lane-bg { fill: transparent; }
|
||||
.fg-lane-bg.alt { fill: var(--rule); fill-opacity: 0.25; }
|
||||
.fg-lane-rule { stroke: var(--rule); stroke-width: 1; }
|
||||
.fg-lane-label { fill: var(--ink-soft); font-size: 0.7rem; font-weight: 600; letter-spacing: 0.04em; text-transform: uppercase; }
|
||||
.fg-lane-sublabel{ fill: var(--ink-faint); font-size: 0.65rem; font-family: var(--mono); }
|
||||
.fg-empty { fill: var(--ink-faint); font-size: 0.85rem; font-style: italic; }
|
||||
|
||||
/* Nodes */
|
||||
.fg-node { cursor: pointer; outline: none; }
|
||||
.fg-node .fg-node-box {
|
||||
fill: var(--bs-body-bg, #fff);
|
||||
stroke: var(--rule-strong);
|
||||
stroke-width: 1;
|
||||
}
|
||||
.fg-node:hover .fg-node-box,
|
||||
.fg-node:focus-visible .fg-node-box { stroke: var(--accent); }
|
||||
.fg-node.selected .fg-node-box {
|
||||
stroke: var(--accent);
|
||||
stroke-width: 2;
|
||||
fill: var(--active-bg);
|
||||
}
|
||||
.fg-node.predicted .fg-node-box { stroke-dasharray: 4 3; }
|
||||
.fg-node.input-dependent .fg-node-box { stroke-dasharray: 2 3; }
|
||||
.fg-node.ghost { opacity: 0.45; }
|
||||
.fg-node.ghost .fg-node-box { fill: var(--rule); fill-opacity: 0.35; }
|
||||
.fg-node.failed .fg-node-box,
|
||||
.fg-node.timed-out .fg-node-box { stroke: var(--bs-danger, #c0392b); }
|
||||
.fg-node.not-traced .fg-node-box { stroke-dasharray: 1 3; }
|
||||
|
||||
.fg-eyebrow { fill: var(--ink-faint); font-size: 0.6rem; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase; }
|
||||
.fg-duration { fill: var(--ink-soft); font-size: 0.6rem; font-family: var(--mono); }
|
||||
.fg-title { fill: var(--ink); font-size: 0.8rem; font-weight: 600; }
|
||||
.fg-subtitle { fill: var(--ink-soft); font-size: 0.68rem; }
|
||||
.fg-node.failed .fg-eyebrow,
|
||||
.fg-node.timed-out .fg-eyebrow { fill: var(--bs-danger, #c0392b); }
|
||||
|
||||
/* Connectors */
|
||||
.fg-edge {
|
||||
fill: none;
|
||||
stroke: var(--ink-faint);
|
||||
stroke-width: 1.5;
|
||||
}
|
||||
.fg-edge.boundary { stroke: var(--accent); }
|
||||
.fg-edge.external { stroke-dasharray: 5 4; }
|
||||
.fg-edge.predicted { stroke-opacity: 0.5; stroke-dasharray: 3 3; }
|
||||
.fg-arrowhead { fill: var(--ink-faint); }
|
||||
.fg-arrowhead.accent { fill: var(--accent); }
|
||||
Reference in New Issue
Block a user