feat(central-ui): FlowGraphView External lane, outbound status pills, async/spawn connectors
Four lanes (LaneCount 4; External sublabel = distinct targets, three then +n); ApiCall/DbCall/Notify nodes carry a status pill (Ok/Pending/Parked/Failed — pending pulses, reduced-motion aware) plus a sync/cached/notify mode; folded lifecycles show '+4.1 s' submit→terminal wall time. Async edges are dashed and routed through the inter-lane strip with a gutter tag; spawn edges use a bolt marker. Predicted async edges (Task 6's PredictedEdge.Async) render the same way. de-DE height pinned at 319.2 = (4×110+16)×0.7. Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
@@ -2,9 +2,10 @@
|
||||
@using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging
|
||||
@using ZB.MOM.WW.ScadaBridge.ScriptAnalysis
|
||||
|
||||
@* Flow Debugger swimlane (design 2026-09-05 §4). Dependency-free inline SVG:
|
||||
three fixed lanes, one node per step, orthogonal connectors. All coordinate
|
||||
math lives in the code-behind (InvariantCulture throughout). *@
|
||||
@* 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). *@
|
||||
|
||||
<div class="fg-wrap" data-test="flow-graph">
|
||||
<svg width="@RenderedWidth" height="@RenderedHeight" viewBox="@ViewBox"
|
||||
@@ -16,10 +17,14 @@
|
||||
<marker id="fg-arrow-accent" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
|
||||
<path d="M 0 0 L 10 5 L 0 10 z" class="fg-arrowhead accent" />
|
||||
</marker>
|
||||
@* Trigger spawn (plan D9): a small bolt in place of the arrowhead — this write fired a value-change script. *@
|
||||
<marker id="fg-arrow-spawn" viewBox="0 0 12 14" refX="10" refY="7" markerWidth="12" markerHeight="14" orient="auto-start-reverse">
|
||||
<path d="M 5 1 l -4 6 h 5 l -3 6 l 7 -8 h -5 l 3 -4 z" class="fg-arrowhead spawn" />
|
||||
</marker>
|
||||
</defs>
|
||||
|
||||
@* Lanes *@
|
||||
@for (var lane = 0; lane < 3; lane++)
|
||||
@for (var lane = 0; lane < LaneCount; lane++)
|
||||
{
|
||||
var i = lane;
|
||||
<g class="fg-lane" data-lane="@LaneName(i)">
|
||||
@@ -47,6 +52,11 @@
|
||||
@foreach (var e in _layout.Edges)
|
||||
{
|
||||
<path d="@e.Path" class="@EdgeClass(e)" marker-end="@MarkerFor(e)" data-test="flow-edge" data-from="@e.From" data-to="@e.To" />
|
||||
@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")
|
||||
}
|
||||
}
|
||||
|
||||
@* Nodes *@
|
||||
@@ -69,7 +79,18 @@
|
||||
<span class="fg-duration">@Duration(node)</span>
|
||||
</div>
|
||||
<div class="fg-title" data-test="flow-node-title">@WrapHints(node.DisplayTitle)</div>
|
||||
<div class="fg-subtitle" data-test="flow-node-subtitle">@node.DisplaySubtitle</div>
|
||||
@if (HasPill(node))
|
||||
{
|
||||
@* Outbound kinds: the folded outcome as a pill, then the call mode (node anatomy: "● Ok · sync"). *@
|
||||
<div class="fg-status" data-test="flow-node-status">
|
||||
<span class="@PillClass(node.Step!.Outcome)" data-test="flow-node-pill">@PillText(node.Step.Outcome)</span>
|
||||
<span class="fg-mode">· @OutboundMode(node.Kind, node.Subtitle)</span>
|
||||
</div>
|
||||
}
|
||||
else
|
||||
{
|
||||
<div class="fg-subtitle" data-test="flow-node-subtitle">@node.DisplaySubtitle</div>
|
||||
}
|
||||
</div>
|
||||
</foreignObject>
|
||||
</g>
|
||||
|
||||
Reference in New Issue
Block a user