diff --git a/frontend/src/components/Flow/BrainNode.tsx b/frontend/src/components/Flow/BrainNode.tsx index 7645435..231a8b9 100644 --- a/frontend/src/components/Flow/BrainNode.tsx +++ b/frontend/src/components/Flow/BrainNode.tsx @@ -1,5 +1,5 @@ import { Handle, type NodeProps, Position } from "@xyflow/react" -import { memo, useEffect, useRef, useState } from "react" +import { type CSSProperties, memo, useEffect, useRef, useState } from "react" import { duration } from "@/lib/motion" import { cn } from "@/lib/utils" @@ -72,14 +72,16 @@ function BrainNodeComponent({ data }: NodeProps) { return () => clearTimeout(timer) }, [emits]) + // The neuron's two bands in pixels: the ring, and the gap the arc runs in. + const ring = Math.round(size * RING) + const gap = Math.round(size * GAP) + return ( - // The pulse ring measures itself from here rather than from the neuron, so - // the thick status ring stays clear of it — see `.node-pulse` in flow.css. + // The pulse ring measures itself from here rather than from the neuron: + // `inset` on an absolute child resolves against the padding box, so an arc + // measured from the neuron would start inside its border rather than at the + // rim — see `.node-pulse` in flow.css. - {/* Remounting on each emit is what restarts the animation, so this is - gated on the pulse rather than on the count: a neuron mounting with a - count already in the store would otherwise play it once for free. */} - {firing ? : null} @@ -153,6 +155,23 @@ function BrainNodeComponent({ data }: NodeProps) { ) : null} + {/* Remounting on each emit is what restarts the animation, so this is + gated on the pulse rather than on the count: a neuron mounting with a + count already in the store would otherwise play it once for free. + After the neuron rather than before it: the arc runs in the gap the + neuron paints, and an earlier sibling would be painted over. */} + {firing ? ( + + ) : null} ) } diff --git a/frontend/src/components/Flow/flow.css b/frontend/src/components/Flow/flow.css index a20575c..faef60b 100644 --- a/frontend/src/components/Flow/flow.css +++ b/frontend/src/components/Flow/flow.css @@ -133,13 +133,15 @@ * is what lets one rule fit both a rounded card and a circle. * * The band is the padding: the content box is cut back out of the mask, which - * leaves a ring rather than the filled shape the gradient paints. + * leaves a ring rather than the filled shape the gradient paints. Both are + * overridable, for a node whose own bands say where the arc belongs — the + * brain's neuron runs it in the gap between its ring and its disc. */ .node-pulse { position: absolute; - inset: -4px; + inset: var(--node-pulse-inset, -4px); border-radius: inherit; - padding: 2px; + padding: var(--node-pulse-band, 2px); pointer-events: none; background: conic-gradient( from var(--node-pulse-angle), @@ -243,7 +245,7 @@ */ .react-flow.brain-flat { --xy-background-color: transparent; - --brain-decay: 90s; + --brain-decay: 150s; /* * Thicker than the editor's, to match the weight the mark draws them at — and * the pulse widens with it, or a message would leave its edge looking thinner @@ -381,7 +383,7 @@ * from another. */ .brain-wire { - --brain-ink: color-mix(in srgb, var(--foreground) 32%, transparent); + --brain-ink: color-mix(in srgb, var(--foreground) 16%, transparent); } .brain-wire.brain-idle {