Brain: hover labels, activity falloff, rim-anchored directed edges
A name under every circle was most of what the graph drew, and none of it was what someone looks at the brain for. Only the hovered or keyboard-focused neuron names itself now — a failing one always does, since colour is never the only carrier of a status. The label is absolute and the collide radius already held that space, so revealing one moves nothing. Taking it out of flow also fixed the off-centre edges: the node box was as wide as its widest label, so a 32px circle inside a 140px box sat 54px left of where the layout put it and every edge aimed at the box rather than at the circle. Neurons are filled discs rather than `bg-card` outlines, which were a hairline against `--background` in light and close to nothing in dark. The fill is a neutral `--muted-foreground` alpha and stays neutral: status still speaks only through the border and the word beneath. Its lightness carries how recently the open page saw that neuron publish — brightest just after it fires, decaying over 90s to a floor it never drops below, so a graph left open sorts itself into what is busy and what is not. Edges do the same on their stroke. The signal is only what this session has seen on the socket, so a fresh page starts everything at a neutral middle rather than claiming a history it does not have. Both are CSS transitions gated on `prefers-reduced-motion`, decayed out of and snapped into, never the other way round. `BrainEdge` now carries both circle radii and trims its path back to the rims, with an arrowhead at the end it flows into. The barbs are two more segments of the same stroked path rather than an SVG marker, so they dim with the line they belong to. Neither trim reaches past the midpoint, so neurons closer together than their radii still get a line pointing the right way. `edge-pulse` reads its landing colour from `--edge-rest` instead of naming `--muted-foreground`, so a selected edge — which rests in blue — decays into its own colour instead of crossing to grey and snapping back at the end. Obsidian's graph view is the reference for the first three: filled nodes sized by degree, labels on a fade threshold with hover to recover them, and arrows as an explicit affordance. The falloff-by-opacity encoding is the standard one in the dynamic-graph literature. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XC2jX6Hdj7pxGGKzBTrbqB
This commit is contained in:
@@ -31,6 +31,19 @@
|
||||
--xy-attribution-background-color: transparent;
|
||||
}
|
||||
|
||||
/*
|
||||
* Where a pulse lands: the opaque twin of the stroke the edge rests in, so the
|
||||
* last step back is invisible. A selected edge rests in blue and so decays into
|
||||
* it rather than crossing to grey and snapping back at the end.
|
||||
*/
|
||||
.react-flow__edge {
|
||||
--edge-rest: var(--muted-foreground);
|
||||
}
|
||||
|
||||
.react-flow__edge.selected {
|
||||
--edge-rest: var(--primary);
|
||||
}
|
||||
|
||||
/* A message arriving lights its edge, then decays back to rest. */
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.react-flow__edge-path.edge-live {
|
||||
@@ -41,8 +54,7 @@
|
||||
* Both ends are plain colours. The implied `to` would be the edge's resting
|
||||
* stroke, which is a `color-mix()` — and interpolating a hex against one of
|
||||
* those goes through oklab and leaves the gamut on the way, which turned
|
||||
* every pulse fluorescent yellow. Landing on `--muted-foreground` instead is
|
||||
* the same hue the edge rests in, so the last step back is invisible.
|
||||
* every pulse fluorescent yellow.
|
||||
*/
|
||||
@keyframes edge-pulse {
|
||||
from {
|
||||
@@ -50,7 +62,7 @@
|
||||
stroke-width: 2.5;
|
||||
}
|
||||
to {
|
||||
stroke: var(--muted-foreground);
|
||||
stroke: var(--edge-rest);
|
||||
stroke-width: var(--xy-edge-stroke-width);
|
||||
}
|
||||
}
|
||||
@@ -102,8 +114,8 @@
|
||||
}
|
||||
|
||||
/*
|
||||
* Brain graph. Both ends of a connection sit at the neuron's centre, so an
|
||||
* edge runs straight from circle to circle and disappears under them.
|
||||
* Brain graph. Both ends of a connection sit at the neuron's centre, which is
|
||||
* what `BrainEdge` measures its rim trim and its arrowhead from.
|
||||
*/
|
||||
.brain-handle {
|
||||
opacity: 0;
|
||||
@@ -115,16 +127,110 @@
|
||||
/*
|
||||
* Embedded on Home rather than on a canvas of its own: the graph paints no
|
||||
* surface, so the page's own background shows through behind the neurons.
|
||||
*
|
||||
* `--brain-decay` is how long a neuron or a connection takes to dim from its
|
||||
* brightest back to the floor. Not a UI transition but the falloff of a signal,
|
||||
* so it is measured in minutes rather than in the `--duration-*` tokens.
|
||||
*/
|
||||
.react-flow.brain-flat {
|
||||
--xy-background-color: transparent;
|
||||
--brain-decay: 90s;
|
||||
}
|
||||
|
||||
/*
|
||||
* A neuron is a filled disc rather than an outline: a `--card` circle on
|
||||
* `--background` is a hairline in light and all but nothing in dark.
|
||||
*
|
||||
* The fill is neutral, and stays neutral — status still speaks only through the
|
||||
* border and the word under it. What its lightness carries is how recently this
|
||||
* page saw the neuron publish: brightest just after it fires, decaying to a
|
||||
* floor it never drops below, so a graph left open sorts itself into what is
|
||||
* busy and what is not. A fresh page has seen nothing either way, so everything
|
||||
* starts at `.brain-idle`, halfway, claiming no history it does not have.
|
||||
*/
|
||||
.brain-cell {
|
||||
cursor: pointer;
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--muted-foreground) 28%,
|
||||
transparent
|
||||
);
|
||||
transition: border-color var(--duration-base) var(--ease-standard);
|
||||
}
|
||||
|
||||
.brain-cell:hover {
|
||||
.brain-cell.brain-idle {
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--muted-foreground) 42%,
|
||||
transparent
|
||||
);
|
||||
}
|
||||
|
||||
/* Snapped into, decayed out of — never the other way round. */
|
||||
.brain-cell.brain-hot {
|
||||
background-color: color-mix(
|
||||
in srgb,
|
||||
var(--muted-foreground) 65%,
|
||||
transparent
|
||||
);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.brain-cell {
|
||||
transition:
|
||||
border-color var(--duration-base) var(--ease-standard),
|
||||
background-color var(--brain-decay) linear;
|
||||
}
|
||||
}
|
||||
|
||||
.brain-cell:hover,
|
||||
.react-flow__node:focus-visible .brain-cell {
|
||||
border-color: var(--primary);
|
||||
}
|
||||
|
||||
/*
|
||||
* A name under every circle is what makes the graph unreadable, so only the
|
||||
* hovered or focused neuron carries one. Absolutely positioned: the layout's
|
||||
* collide radius already holds this space clear, and revealing a label must not
|
||||
* move anything.
|
||||
*/
|
||||
.brain-label {
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
margin-top: 6px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.brain-cell:hover .brain-label,
|
||||
.react-flow__node:focus-visible .brain-label,
|
||||
.brain-label.brain-label-on {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
/* The same falloff as the neurons, on the line instead of the disc. */
|
||||
.react-flow__edge-path.brain-edge {
|
||||
stroke: color-mix(in srgb, var(--muted-foreground) 38%, transparent);
|
||||
stroke-linecap: round;
|
||||
stroke-linejoin: round;
|
||||
}
|
||||
|
||||
.react-flow__edge-path.brain-edge.brain-idle {
|
||||
stroke: color-mix(in srgb, var(--muted-foreground) 55%, transparent);
|
||||
}
|
||||
|
||||
.react-flow__edge-path.brain-edge.brain-hot {
|
||||
stroke: color-mix(in srgb, var(--muted-foreground) 85%, transparent);
|
||||
transition: none;
|
||||
}
|
||||
|
||||
@media (prefers-reduced-motion: no-preference) {
|
||||
.react-flow__edge-path.brain-edge {
|
||||
transition: stroke var(--brain-decay) linear;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user