Skip to content

Commit a200ac0

Browse files
fahchenclaude
andcommitted
fix(dashboard): firing edge keeps enabled style + overlay fades smoothly
Two related polishes: 1. Edge retains its enabled style during firing buildEdges short-circuited to DEFAULT_EDGE_STYLE whenever isFiring=true, discarding the cf-accent-tint enabled-input styling. User saw enabled arcs lose their accent the moment firing started. Removed the firing branch from the style/marker/className decision; isEnabledInput drives the base styling regardless of firing state. 2. Overlay fades in/out instead of snapping OrthogonalEdgeImpl's overlay path previously only rendered while firing (mount on firing start, unmount on end), which snapped the accent color in and out. Overlay is now always mounted with opacity transitioned via CSS: - opacity: 0 when not firing. - opacity: 1 when firing. transition: opacity 200ms ease-out. - strokeDashoffset still drives the progress-bar fill while opacity handles the smooth color emergence. - Added cf-edge-overlay class + data-firing attribute for selector clarity. pointerEvents: none so the overlay never intercepts clicks. Test updated: enabled-input arc retains cf-edge-enabled className during firing. 172/172 vitest + 177/177 mix + dialyzer 0; lib/coloured_flow byte-identical. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 67b9a56 commit a200ac0

2 files changed

Lines changed: 16 additions & 17 deletions

File tree

dashboard/ui/src/components/NetDiagram.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -153,9 +153,9 @@ describe("NetDiagram", () => {
153153
const output = edges.find((e) => e.id === outputId)
154154
expect((input?.data as Record<string, unknown>).firingProgress).toBe(0.3)
155155
expect((output?.data as Record<string, unknown>).firingProgress).toBe(0.7)
156-
// No className for firing in the new design — the inline `pathLength="1"`
157-
// + dasharray combo replaces the CSS keyframe.
158-
expect(input?.className).toBeUndefined()
156+
// Firing does not strip the enabled-input className; base stays
157+
// accent-tint while the overlay paints cf-accent on top.
158+
expect(input?.className).toBe("cf-edge-enabled")
159159
expect(output?.className).toBeUndefined()
160160
})
161161

dashboard/ui/src/components/NetDiagram.tsx

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -475,17 +475,14 @@ function buildEdges(
475475
: clamp01(firingProgress.output)
476476
: undefined
477477

478-
const isFiring = edgeFiringProgress !== undefined
479478
const isEnabledInput =
480-
!isFiring && arc.orientation === "p_to_t" && isTransitionEnabled(arc.transition)
479+
arc.orientation === "p_to_t" && isTransitionEnabled(arc.transition)
481480

482481
let style: CSSProperties
483482
let markerEnd = DEFAULT_MARKER
484483
let className: string | undefined
485484

486-
if (isFiring) {
487-
style = DEFAULT_EDGE_STYLE
488-
} else if (isEnabledInput) {
485+
if (isEnabledInput) {
489486
style = ENABLED_EDGE_STYLE
490487
markerEnd = ENABLED_MARKER
491488
className = "cf-edge-enabled"
@@ -588,30 +585,32 @@ function OrthogonalEdgeImpl({ data, markerEnd, style }: EdgeProps) {
588585
if (!points || points.length < 2) return null
589586
const path = roundedPolylinePath(points, EDGE_CORNER_RADIUS)
590587
const progress = oedata?.firingProgress
588+
const isFiring = progress !== undefined
591589

592-
if (progress === undefined) {
593-
return <BaseEdge path={path} markerEnd={markerEnd} style={style} />
594-
}
595-
596-
// Base path keeps the edge's pre-firing color visible throughout the
597-
// animation; overlay path paints `cf-accent` on top, growing from 0..1 via
598-
// `pathLength="1"` + `strokeDasharray=1` + `strokeDashoffset=1-progress`.
590+
// Overlay path renders unconditionally so the opacity transition can blend
591+
// the cf-accent fill in and out smoothly instead of snapping on the firing
592+
// boundary. dashoffset drives the fill direction; opacity drives the colour
593+
// emergence. When not firing, opacity=0 hides it entirely (no hit testing,
594+
// no visual cost beyond the offscreen path).
599595
const overlayStyle: CSSProperties = {
600596
...style,
601597
stroke: "var(--color-cf-accent)",
602598
strokeDasharray: 1,
603-
strokeDashoffset: 1 - progress,
599+
strokeDashoffset: isFiring ? 1 - progress : 1,
600+
opacity: isFiring ? 1 : 0,
601+
transition: "opacity 200ms ease-out",
604602
pointerEvents: "none"
605603
}
606604
return (
607605
<g data-testid="cf-edge-firing-path">
608606
<BaseEdge path={path} markerEnd={markerEnd} style={style} />
609607
<path
610608
d={path}
611-
className="react-flow__edge-path"
609+
className="react-flow__edge-path cf-edge-overlay"
612610
fill="none"
613611
pathLength={1}
614612
style={overlayStyle}
613+
data-firing={isFiring ? "true" : "false"}
615614
data-testid="cf-edge-firing-overlay"
616615
/>
617616
</g>

0 commit comments

Comments
 (0)