fix(webui): match the TUI's per-character playbook run shimmer - #1297
Merged
Conversation
The TUI paints a pending playbook block by re-styling every character with a brightness taken from a travelling sine wave (crates/cli/src/ui.rs, `apply_playbook_shimmer`): PLAYBOOK_SHIMMER_SPEED 4.2 rad/s, PLAYBOOK_SHIMMER_DENSITY 0.18 rad/char, foreground blended muted -> text through a smoothstep so the region rests dim and the crest pops, with one character index running across every pending line so the band stays continuous down the block. The web editor instead swept a translucent accent band across each pending row, on its own 1.6s cadence — a different effect entirely. Reproduce the TUI wave: a 34.907ch (2*pi/0.18) repeating gradient sampling the same smoothstep, clipped to the glyphs and translated one wavelength per 1.496s (2*pi/4.2). playbookApplyShimmer sets a per-line negative animation-delay from the running character offset, so the crest carries on across lines the way the TUI's `gidx` does, and lines joining a run late stay in phase. Still driven by the same source of truth as before — the daemon's run pending set — so nothing shimmers once blocks settle, and the animation is pure CSS: no per-character DOM, no per-frame JS, no idle repaint loop. Notes: - Chips and inline code carry their own background, which the TUI overlay skips; their colors are pinned back against the inherited transparent text fill. Fenced-code rows re-issue their block background as a border-box layer so `background-clip: text` cannot clip it away. - The TUI also bolds the crest, which CSS cannot express per character; the crest peaks at full foreground instead. - prefers-reduced-motion gets a static accent tint at full contrast.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
The web Playbook editor now shimmers pending blocks with the same wave the TUI paints, instead of its own unrelated highlight band.
The TUI behavior I matched
crates/cli/src/ui.rs→apply_playbook_shimmerre-emits every character of a pending block with a brightness drawn from a travelling sine wave:PLAYBOOK_SHIMMER_SPEED= 4.2 rad/sPLAYBOOK_SHIMMER_DENSITY= 0.18 rad/charsmoothstep(0.5 + 0.5·sin(phase − gidx·D)), blendedtheme.muted → theme.textgidx) advances across all pending lines, so the band runs continuously down the blockThat works out to a 34.907ch wavelength (2π/0.18) travelling one wavelength per 1.496s (2π/4.2) — ~23.3 chars/sec, left to right and on down the block.
The web editor was doing something else entirely: a translucent
--accentband swept across each pending row independently on a 1.6s cadence, restarting per line.The change
crates/daemon/assets/index.htmlonly (CSS + the existingplaybookApplyShimmer):.playbook-line.is-runningpaints a 17-stop repeating gradient sampling that same smoothstep, sized34.907ch, clipped to the glyphs withbackground-clip: text, and translated one wavelength per1.496s.playbookApplyShimmersets a per-line--pb-shim-lag(negativeanimation-delay) from the running character offset. The wave is periodic, so lagging a line bychars / 23.3seconds reproduces the TUI's singlegidx— the crest carries on across lines instead of restarting per row. It is anchored to the run's own start time, so a block that joins a run late stays in phase.border-boxlayer sobackground-clip: textcan't clip it away.prefers-reduced-motiongets a static accent tint at full contrast instead.Source of truth is unchanged: the daemon's run pending set (
pending_block_refs), exactly as before. No local timers, no heuristics.No per-frame JS and no idle repaint loop — it's a CSS animation that only exists while a line carries
.is-running. With everything settled: 0 running lines, 0 leftover custom properties,document.getAnimations()reports 0 shimmer animations.Known deviation
The TUI also sets
BOLDwheneased > 0.85. CSS can't vary font weight per character inside one text run, so the crest peaks at full--fgwithout the extra weight.Evidence
Isolated daemon from this worktree (
CONSTRUCT_ASSETS_DIRhot-reload), a real playbook run with 4 pending blocks declared over IPC, driven in Chrome over CDP.TUI (the reference, unchanged):
Web, dark theme:
Web, light theme:
Two frames ~370ms apart, showing the crest travelling right and carrying across the two
Notes:lines:Also verified in the live page:
ZQXmid-line: text lands at the caret, caret offset advances correctly, line keeps.is-runningand its animation; backspacing restores it.playbook.updatecleared.is-runningand--pb-shim-lagon exactly those lines and re-phased the rest; settling all cleared everything.animation-name: none, no gradient,-webkit-text-fill-colorback torgb(236,236,241).Testing
cargo build— clean.cargo test --workspace --no-fail-fast— all green, includingcrates/e2e/tests/web_smoke.rsagainst a freshly builttarget/debug/construct(verified the new CSS is baked into the binary). One earlier run trippedpty_render::tests::many_small_events_stay_fast_as_history_grows, a load-sensitive perf test unrelated to this diff; it passes in isolation and in the full re-run.Note for the reviewer
Another branch is concurrently editing this same file (C-space selection fix). This diff is confined to the shimmer CSS block and the body of
playbookApplyShimmer.The media above lives in a temporary
.pr-media/commit that is removed by the next commit on this branch; the links are pinned to65ab37db5ed19b26aef683c2b333e2dfb0cb5a97and stay resolvable viarefs/pull.