@namespace ZB.MOM.WW.ScadaBridge.CentralUI.Components.Shared @using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging @using ZB.MOM.WW.ScadaBridge.ScriptAnalysis @* Flow Debugger swimlane (design 2026-09-05 §4; External lane, status pills and async/spawn connectors per the 2026-09-06 outbound-calls plan). Dependency-free inline SVG: four fixed lanes, one node per step, orthogonal connectors. All coordinate math lives in the code-behind (InvariantCulture throughout). *@
@* Lanes *@ @for (var lane = 0; lane < LaneCount; lane++) { var i = lane; @SvgText("12", LaneLabelY(i), "fg-lane-label", LaneName(i)) @if (!string.IsNullOrEmpty(_layout.LaneSublabels[i])) { @SvgText("12", F(i * LaneHeight + 40), "fg-lane-sublabel", Clip(_layout.LaneSublabels[i], 22), title: _layout.LaneSublabels[i]) } @if (!string.IsNullOrEmpty(_layout.LanePrefixes[i])) { @* The dotted head every node in this lane shares — shown here once, stripped from the nodes. *@ @SvgText("12", F(i * LaneHeight + 56), "fg-lane-prefix", Clip(_layout.LanePrefixes[i] + "*", 22), title: _layout.LanePrefixes[i] + "* (prefix shared by every node in this lane)", dataTest: "flow-lane-prefix") } } @if (_layout.IsEmpty) { @SvgText(F(ColumnOrigin), F(LanePad + 44), "fg-empty", "No steps", dataTest: "flow-graph-empty") } @* Connectors (under the nodes) *@ @foreach (var e in _layout.Edges) { @if (AsyncTagFor(e) is { } tag) { @* Names the branch where it lands: the run carried on without waiting for this. *@ @SvgText(tag.X, tag.Y, "fg-edge-tag", "async", dataTest: "flow-edge-tag") } @if (SpawnBadgeFor(e) is { } badge) { @* Trigger cascade (plan D9): a bolt badge on the connector itself — this write fired a value-change script. The edge keeps an ordinary arrowhead; the bolt used to BE the arrowhead, and on the upward Tags→Site hop it read as a broken arrow (rig, 2026-09-08). *@ Trigger cascade: this write fired the script } } @* Nodes *@ @foreach (var n in _layout.Nodes) { var node = n; var logs = LogCounts(node.Step); @* HTML body inside the SVG so the browser measures the text: the title wraps to two lines and both lines ellipsize by real width, never by a character count, and nothing can spill past the box. The tooltip carries the full, unstripped text. *@
@Eyebrow(node) @Duration(node)
@WrapHints(node.DisplayTitle)
@* Footer row: the pill-or-subtitle on the left, the log-count badge on the right (step-logs design 2026-09-09 D8). A node with no lines shows no badge at all. *@
@if (HasPill(node)) { @* Outbound kinds: the folded outcome as a pill, then the call mode (node anatomy: "● Ok · sync"). *@
@PillText(node.Step.Outcome) · @OutboundMode(node.Kind, node.Subtitle)
} else {
@node.DisplaySubtitle
} @if (logs.Errors + logs.Warnings + logs.Info > 0) { @if (logs.Errors > 0) { ✕ @logs.Errors } @if (logs.Warnings > 0) { ! @logs.Warnings } @if (logs.Info > 0) { ≡ @logs.Info } }
}