Skip to content

Give the memory librarian three rising Z's with a brightness wave - #461

Merged
sysread merged 1 commit into
mainfrom
claude/memory-worker-spinner-17uqm1
Jul 29, 2026
Merged

Give the memory librarian three rising Z's with a brightness wave#461
sysread merged 1 commit into
mainfrom
claude/memory-worker-spinner-17uqm1

Conversation

@sysread

@sysread sysread commented Jul 29, 2026

Copy link
Copy Markdown
Owner

SYNOPSIS

The memory strip's growing zzz becomes three Z's climbing a staircase with peak brightness travelling up them on a ~1s cycle. Wiki keeps the bar.

PURPOSE

The growing zzz worked, but read as a text spinner rather than as sleeping. The comic-book shorthand is rising Z's, and the memory librarian's two passes are named after sleep stages (deep-sleep for slow-wave, rem for REM), so the motif is worth getting right.

DESCRIPTION

How it behaved. AsciiSpinner grew a z / zZ / zZZ sequence on a 100ms JS interval, selected by a variant prop, with a frame table in src/lib/ui/ascii-spinner.ts.

What this changes. A new <SleepSpinner> owns the memory strips; AsciiSpinner and its frame module revert to the bar-only shape they had before the variant table. Wiki is untouched.

Its own component, not another variant. The two now share no implementation at all - CSS keyframes over three spans vs a JS interval over one text node - so a variant prop would have been two disjoint render paths behind one name. The variant table would also have collapsed to a single entry.

This is a chase, not the pulse #454 removed. That one modulated a single glyph's opacity uniformly, so nothing moved and the eye read it as static. Here the bright spot changes position, which carries motion the way marquee bulbs do even though no glyph ever changes shape. Uniform fading is what failed; travelling brightness is a different mechanism. Worth stating plainly because "we removed a fade, this is a fade" is the obvious wrong read.

Notes:

  • transform: translateY() for the staircase, not sub/sup markup. <sub>/<sup> shrink the font (~0.83em) and use vertical-align, changing glyph advance width and growing the line box - which would break the ch grid and make the rows taller. Transforms don't participate in layout.
  • Negative animation-delays in an order that looks wrong (0, -2D/3, -D/3). An element started x seconds in next peaks at duration - x, so those offsets put the peaks at 0, D/3, 2D/3 - travelling upward. Negative because a positive delay parks each Z at base opacity until it elapses, visible as a settling-in on cycle one.
  • Opacity against the inherited colour, never literal greys - the cascade hands the spinner full text contrast when pending and the accent/danger tints when settled, so greys would flatten dark mode.
  • The empty <script lang="ts"> block is required. svelte-check can't synthesise a component declaration for a script-less .svelte file; importers fail with implicit-any. It's the only script-less component in the repo, so it's commented against deletion.
  • vite.config.ts gains one environmentMatchGlobs entry. The new test mounts a component, and the jsdom list enumerates files by name - a file added after that sweep defaults to node and dies on document is not defined. Verified by removing the entry: all 4 tests fail that way.
  • Behavioural change to flag: .librarian-step-icon stays at 3ch, so memory step labels sit slightly right of where they did pre-zzz. Intentional - it keeps the settled check/cross rows sharing one gutter with the spinner.

Gate green: 2147 vitest, svelte-check, lint, build, knip.

Not verified in a browser - cloud session. The opacity swing (0.45 -> 1.0) reading as a travelling wave at 0.9rem, and the +/-0.15em staircase clearing the row leading, both want a look.


Generated by Claude Code

The growing zzz worked but read as a text spinner. Three Z's climbing a
staircase, with peak brightness travelling up them on a ~1s cycle,
reads as the comic-book shorthand for sleeping instead.

This is a chase, not the pulse the strip removed earlier. That one
modulated a single glyph's opacity uniformly, so nothing moved and the
eye read it as static. Here the bright spot changes POSITION, which
carries motion the way marquee bulbs do even though no glyph ever
changes shape. Uniform fading is what failed; travelling brightness is
a different mechanism.

Its own component rather than another AsciiSpinner variant. The two now
share no implementation at all - CSS keyframes over three spans against
a JS interval over one text node - so a variant prop would have been
two disjoint render paths behind one name. AsciiSpinner and its frame
module revert to the bar-only shape they had before the variant table,
and the wiki strip is untouched: the contrast is how you tell which
librarian a strip belongs to.

Three details that will look arbitrary later, so they carry comments.
The staircase is `transform: translateY()` and not sub/sup markup,
which would shrink the font, change the glyph advance width, and grow
the line box - breaking the ch grid the column depends on. The
animation delays are negative and in an order that looks wrong (0,
-2D/3, -D/3), because an element started x seconds in next peaks at
duration - x, and a positive delay would park each Z at its base
opacity until it elapsed. And the wave is opacity against the inherited
colour rather than literal greys, so it respects dark mode and the
accent and danger tints the settled rows use.

The empty script block is required: svelte-check cannot synthesise a
component declaration for a script-less .svelte file, and importers
fail with an implicit-any error without it.
@sysread
sysread merged commit ad1e5eb into main Jul 29, 2026
1 check passed
@sysread
sysread deleted the claude/memory-worker-spinner-17uqm1 branch July 29, 2026 21:32
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants