Gate the flow node pulse on a real emit
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01SKL7sUgNWhukDEz95vSMQv
This commit is contained in:
@@ -168,7 +168,6 @@ as an em dash.
|
||||
### Flow editor follow-ups
|
||||
|
||||
- BUG/UI: the payload of the inject node should adapt to the type set in the "provides" arguments and the number of fields should grow with the number of "provides" arguments. So if e.g. one argument is boolean, the payload should be a dropdown for true/false. If it is int, the value entered should be parsed as int etc. Check for other notes where this applies as well
|
||||
- BUG/UI: `FlowNode` still renders `emits > 0 ? <span key={emits} className="node-pulse"/>`, so opening the editor with a non-zero persisted count replays a pulse for something that happened before the canvas mounted. `BrainNode` gained a `seen` ref that gates the pulse on a real emit; the same gate belongs here.
|
||||
- CHORE/UI: a node's error status clears as soon as it runs again, so a failure that genuinely fired an alert can leave no trace on the canvas by the time anyone looks. The logs panel keeps the traceback; the node itself reads as healthy.
|
||||
- FEAT/UI: the flow graph should auto zoom-to fit when a panel is open and edges are created through assigning inputs/outputs
|
||||
- PERF/FLOW: every save rebuilds the whole pipeline. Fine at the current flow count; rebuild only the touched flow when it starts to show.
|
||||
|
||||
@@ -19,7 +19,7 @@ import {
|
||||
Terminal,
|
||||
Timer,
|
||||
} from "lucide-react"
|
||||
import { memo } from "react"
|
||||
import { memo, useEffect, useRef, useState } from "react"
|
||||
|
||||
import { FlowsService, type MessageSpec, type NodeDef_Input } from "@/client"
|
||||
import { Button } from "@/components/ui/button"
|
||||
@@ -29,6 +29,7 @@ import {
|
||||
TooltipTrigger,
|
||||
} from "@/components/ui/tooltip"
|
||||
import { useIsMobile } from "@/hooks/useMobile"
|
||||
import { duration } from "@/lib/motion"
|
||||
import { cn } from "@/lib/utils"
|
||||
import { portOf } from "./deriveEdges"
|
||||
import { useNodeEmits, useNodeStatus } from "./liveStore"
|
||||
@@ -119,6 +120,22 @@ function FlowNodeComponent({ data, selected }: NodeProps) {
|
||||
// The graph runs top to bottom on a phone, so the ports have to face that
|
||||
// way too — see DESIGN-GUIDELINES.md → Responsive.
|
||||
const vertical = useIsMobile()
|
||||
// Whether a pulse is playing right now; the ring is mounted only while it is.
|
||||
const [firing, setFiring] = useState(false)
|
||||
// The count outlives this component: the store is module-level, and a
|
||||
// snapshot restores what the engine counted before the page even loaded. So
|
||||
// the number we mount with is history, and only a change on top of it is
|
||||
// something that just happened.
|
||||
const seen = useRef(emits)
|
||||
|
||||
useEffect(() => {
|
||||
if (emits === seen.current) return
|
||||
seen.current = emits
|
||||
setFiring(true)
|
||||
const timer = setTimeout(() => setFiring(false), duration.pulse * 1000)
|
||||
return () => clearTimeout(timer)
|
||||
}, [emits])
|
||||
|
||||
const Icon =
|
||||
NODE_ICONS[definition.type as keyof typeof NODE_ICONS] ??
|
||||
// A connector's own type cannot be in the map above, and a device is
|
||||
@@ -141,8 +158,10 @@ function FlowNodeComponent({ data, selected }: NodeProps) {
|
||||
// The pulse ring measures itself from here rather than from the card, so
|
||||
// a border can never land on top of it — see `.node-pulse` in flow.css.
|
||||
<div className="relative rounded-lg">
|
||||
{/* Remounting on each emit is what restarts the animation. */}
|
||||
{emits > 0 ? <span key={emits} className="node-pulse" /> : null}
|
||||
{/* Remounting on each emit is what restarts the animation, so this is
|
||||
gated on the pulse rather than on the count: a node mounting with a
|
||||
count already in the store would otherwise play it once for free. */}
|
||||
{firing ? <span key={emits} className="node-pulse" /> : null}
|
||||
<div
|
||||
className={cn(
|
||||
"relative min-w-[168px] max-w-[220px] rounded-lg border border-border bg-card px-3 py-2.5 shadow-e1 transition-shadow",
|
||||
|
||||
Reference in New Issue
Block a user