Repository navigation
docs(demos): record demos with the site's current palette - #4069
Conversation
worktrunk-bot
left a comment
There was a problem hiding this comment.
Two claims in the reworded docstring don't hold against the file below it, and the second one leaves the light block pointing at a value nothing in it resembles — the same dead-end the PR set out to remove.
"None is a verbatim copy" is false for the dark block. red #f87171, cyan #67d4d4, and black/brightBlack #6b7280 are byte-identical to --wt-terminal-red, --wt-terminal-cyan, and --wt-terminal-gutter under :root[data-theme='dark']. The diff itself relies on that — those are the only dark ANSI entries it leaves without a (… from the site's #…) parenthetical — so the sentence contradicts the convention it introduces, and a reader asking which values can be resynced freely is told none of them can.
"expect small deltas" understates the light block by an order of magnitude. --wt-terminal-gutter is #a8a29e in :root; light black/brightBlack is #6b7280, which is the dark-mode gutter. Light yellow is #ca8a04 against #8a5a00 and red is #dc2626 against #b42318 — 61, 64, and 40 per-channel units, next to 2–6 for the surfaces the same sentence covers. The cause is that the light ANSI values predate the Starlight rebuild: #6b7280, #dc2626, #ca8a04, #2563eb, and #9333ea are byte-identical to light --bright-black, --red, --yellow, --blue, and --magenta in docs/templates/_variables.html, deleted in #3866. So they were verbatim copies that went stale, not values nudged for terminal capture, and re-anchoring the two parentheticals on today's hexes asserts a derivation that never happened — #357a59 was desaturated from the old --green: #1b7f4b, not from #256b4a, and #3d7f7f from --cyan: #0a8080, not #0f6f73.
The inline suggestions annotate the gap rather than close it, which keeps the PR comments-only. Whether the light ANSI block should instead be resynced to the current properties is the live question — it's a stronger case than the background/cursor deltas the description raises, since those are a few units and deliberate-looking while these are a whole superseded palette. That changes recorded GIFs, so it wants a maintainer call.
Per-entry deltas against custom.css (max per-channel difference)
| entry | themes.py | custom.css | Δ |
|---|---|---|---|
light black / brightBlack |
#6b7280 |
--wt-terminal-gutter #a8a29e |
61 |
light red |
#dc2626 |
--wt-terminal-red #b42318 |
40 |
light green |
#357a59 |
--wt-terminal-green #256b4a |
16 |
light yellow |
#ca8a04 |
--wt-terminal-yellow #8a5a00 |
64 |
light blue |
#2563eb |
--wt-terminal-blue #1d4ed8 |
21 |
light magenta |
#9333ea |
--wt-terminal-magenta #7e22ce |
28 |
light cyan |
#3d7f7f |
--wt-terminal-cyan #0f6f73 |
46 |
light cursor |
#d85d22 |
--wt-copper #9f4f2a |
57 |
light selection |
#f7d6c1 |
--wt-gold-wash #ead9bb |
13 |
light background / foreground |
#f7f3eb / #27231f |
--wt-paper / --wt-ink |
2 / 6 |
dark black / brightBlack |
#6b7280 |
--wt-terminal-gutter #6b7280 |
0 |
dark red |
#f87171 |
--wt-terminal-red #f87171 |
0 |
dark cyan |
#67d4d4 |
--wt-terminal-cyan #67d4d4 |
0 |
dark green |
#4ade80 |
--wt-terminal-green #6ee7a2 |
36 |
dark yellow |
#fbbf24 |
--wt-terminal-yellow #facc15 |
15 |
dark blue |
#60a5fa |
--wt-terminal-blue #93c5fd |
51 |
dark magenta |
#c084fc |
--wt-terminal-magenta #d8b4fe |
48 |
dark cursor / selection |
#ef8a50 / #49200f |
--wt-copper / --wt-gold-wash |
18 / 17 |
dark background / foreground / brightWhite |
#1d1a18 / #eee8de |
--wt-paper / --wt-ink |
2 / 6 |
Every property name the diff introduces exists in docs/src/styles/custom.css, and every hex it cites in a parenthetical matches that property's current value. The old names (--red, --green, --yellow, --blue, --magenta, --cyan, --bright-black, --wt-orange) appear nowhere under docs/ or src/, so the description's premise checks out.
…ght ANSI values The docstring said no value is a verbatim copy and to expect small deltas. Neither holds: dark black/red/cyan are byte-identical to --wt-terminal-gutter, --wt-terminal-red, and --wt-terminal-cyan, while the light ANSI hues sit 21-64 per-channel units from theirs because they still carry the light palette from the pre-Starlight _variables.html (deleted in #3866). Split the docstring's claim by group so it is true of every entry, and give each light ANSI entry the site's current hex. The two parentheticals that said 'desaturated/muted from' lose that wording: #357a59 and #3d7f7f were derived from the old --green #1b7f4b and --cyan #0a8080, not from today's values. Whether to resync the light values themselves is left open - that would change recorded GIFs.
worktrunk-bot
left a comment
There was a problem hiding this comment.
The light block's fix is right, but the same mis-attribution is still in the dark block, and the new docstring describes a convention the file doesn't use.
The four dark parentheticals assert a derivation that never happened. #4ade80, #fbbf24, #60a5fa, and #c084fc are byte-identical to dark --green, --yellow, --blue, and --magenta in the docs/templates/_variables.html that #3866 deleted — the same stale palette the light block carries. They can't have been "saturated"/"warmed"/"deepened" from --wt-terminal-*, because those properties did not exist until #3936, which authored #6ee7a2/#facc15/#93c5fd/#d8b4fe fresh five days after the rebuild. #3866's own themes.py diff rewrote the surfaces and accents and left every ANSI entry untouched, which is why the dark block is stale in exactly the way the light one is; dark red, cyan, and gutter read as verbatim only because #3936 happened to carry those three values over unchanged. The wording this round removed from the light block is still live in the dark one.
The docstring's rule doesn't match either block. "A parenthetical says the value was nudged from the site's for legible terminal captures" is the opposite of what all eight light parentheticals say (pre-Starlight; site is #…), and the next sentence files "the light ANSI hues" under "Without one", where every one of them carries one. The suggested rewrite states the rule the file actually follows once the dark parentheticals are fixed: a quoted hex means the site moved and this value didn't.
Both suggestions are comments-only, so the recorded GIFs are unaffected and the description's open question — whether to resync the stale hues instead of annotating them — still stands. It now covers the dark block's four as well, not just the light block.
Separately, #4068 rewrites docs/demos/CLAUDE.md to say the theme definitions match the --wt-* custom properties in custom.css. This PR's evidence is that the ANSI hues don't, by 15–64 per-channel units, so that line wants the same scoping.
The dark green, yellow, blue, and magenta values are byte-identical to the dark --green/--yellow/--blue/--magenta in the docs/templates/_variables.html #3866 deleted, and --wt-terminal-* did not exist until #3936 authored today's hexes. They are the same stale palette the light block carries, not values derived from the current properties, so they take the same annotation. The docstring now states the rule the file follows: an ANSI hue quotes the site's current hex where the site moved and the theme did not.
…3266-theme-comments
The recorder's VHS theme still carried the Zola site's ANSI palette, which #3866 deleted and #3936 replaced with --wt-terminal-*. Zellij, the starship prompt, and Claude Code were configured light for both themes. Dark GIFs drew Worktrunk's gutter as a near-white bar under a light Zellij tab bar, and the light GIFs' yellow had 2.65:1 contrast against the page. themes.py now reads the hex --wt-* properties from custom.css when the build runs. The VHS theme maps ANSI colors the way the site renders snapshot output, and DemoEnv carries the recording's theme so Zellij, starship, and Claude Code follow it. This replaces the per-value "pre-Starlight" annotations. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQPDxs2csvGFGBr2j4MtrA
…ings A build started from inside Claude Code passed that session's CLAUDE* variables to the demo's Claude Code, which then warned that transcript saving was off. The demo's Claude Code also started Remote Control, printing a live claude.ai session URL into the Claude demos. The recorder now drops inherited CLAUDE* variables except CLAUDE_CODE_OAUTH_TOKEN, and the demo config sets remoteControlAtStartup to false. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQPDxs2csvGFGBr2j4MtrA
worktrunk-bot
left a comment
There was a problem hiding this comment.
remoteControlAtStartup: false goes into ~/.claude.json, but that file is only a backward-compatibility source for this key — the settings reference gives its scope as any settings file and notes separately that "Claude Code also reads a value left in ~/.claude.json by older versions". Its documented home is settings.json, which setup_claude_code_config writes about thirty lines further down. When that compat read goes away, the demo's Claude Code connects Remote Control again and prints a live claude.ai session URL into the GIF, with nothing between that and a published asset. I'll move the key into the settings dict.
Worth recording a demo before merging, and looking at a dark one. Nothing in CI records or checks a GIF and docs/demos/tests is wired into no workflow and no Taskfile target, so a hand-run ./docs/demos/build docs is the only thing that surfaces a regression here. The dark recordings are where the risk concentrates: the zellij theme, the starship prompt palette, and Claude Code's own theme follow DemoEnv.theme now, where all three were previously hardcoded to their light variants, so the dark GIFs are not a recolor of anything anyone has looked at. The specific thing to check is how muted fish's grays still read — fish_color_autosuggestion and fish_color_comment are brblack, which now resolves to --wt-ink-muted, #b9ab9c in dark against a #dedbd7 foreground rather than the #6b7280 it was.
…de.json `~/.claude.json` is only a backward-compatibility source for this key; its documented home is a settings file, which setup_claude_code_config writes a few lines further down.
…t frame The wt-switch and wt-statusline checkpoints named absolute frames near the end of the recording, but a tape's frame count varies with the recording machine's speed: the published wt-statusline GIFs have 344 and 419 frames, a fresh recording 275. Frames 280-410 fell past that end and validation failed on a correct GIF. A negative checkpoint frame now counts back from the last frame, and the Claude checkpoints also forbid the claude.ai/code URL and transcript-saving warning a leaked parent session prints. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQPDxs2csvGFGBr2j4MtrA
worktrunk-bot
left a comment
There was a problem hiding this comment.
The two checkpoints this round converted are the two that broke, but wt-zellij-omnibus has a third of the same shape and it keeps its absolute frames: Checkpoint(start=1650, end=1850, expected=["Branch", "main"], ...), under the comment # Near end — wt list --full showing all worktrees. in docs/demos/shared/validation.py. That is an end-state checkpoint, so it fails exactly the way wt-switch and wt-statusline just did as soon as a recording stops short of frame 1851 — failed to extract frames 1650-1850, with nothing in the message pointing at recording speed.
The margin is small against the variance the description measures: 274–318 frames locally versus 344–465 published for the same two tapes, and the omnibus waits on Claude the same way those do. Its mid-recording windows drift under the same effect, but the end-state one is the one the new negative anchoring is for, and leaving it out means the next fast recording reproduces the bug this PR fixes.
I haven't converted it here because choosing the window needs the omnibus GIF's frame count, and nothing in CI records a GIF. The overlap between the commit-generation window (1400–1750) and the list window (1650–1850) also means how much recording follows the list is something only the GIF answers — the recordings made locally have both numbers.
… end The omnibus recording ends on wt list --full, and its checkpoint named frames 1650-1850 like the two end-state checkpoints the previous commit converted. Recordings run 1736-1889 frames, so a faster one would fail the same way. The last 100 frames hold the list in every current and previously published omnibus GIF. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01TQPDxs2csvGFGBr2j4MtrA
…rigger The rewrite widened the heading and the trigger but left the gate keyed on "before opening a docs-only PR" and the closing sentence on "a config change". The section's own example is neither: #4069 annotated values in docs/demos/shared/themes.py and recommended no config change, so a comments-only source change could read the clause literally and conclude the gate doesn't bind — the same literal-scope miss the promotion out of "## Issue Triage" exists to fix.
The demo recorder kept its own copies of the docs site's colors, and they had drifted from
docs/src/styles/custom.css:--wt-terminal-*. Light yellow had 2.65:1 contrast against the page background; the site's browser test requires 4.5:1 for its own dim terminal text.brightWhite, the background of Worktrunk's gutter, used the ink color, so dark GIFs drew the gutter as a near-white bar.docs/demos/shared/themes.pynow reads the hex--wt-*properties fromcustom.csswhen the build runs. The VHS theme maps ANSI colors the way the site renders snapshot output (terminal_color_classandterminal_background_classintests/integration_tests/readme_sync.rs).DemoEnvcarries the recording's theme, so Zellij, the starship palette, and Claude Code follow it. This replaces the(pre-Starlight; site is #…)comments the PR first added.Recording from inside a Claude Code session also leaked into the Claude demos. The demo's Claude Code inherited that session's
CLAUDE*variables and warned that transcript saving was off, and it started Remote Control, which printed a live claude.ai session URL into the GIF. The recorder now drops inheritedCLAUDE*variables exceptCLAUDE_CODE_OAUTH_TOKEN, and the demo'ssettings.jsonsetsremoteControlAtStartupto false.The GIFs are re-recorded locally (
./docs/demos/build docsand./docs/demos/build social) and published in max-sixty/worktrunk-assets@bb3bfba.Verification
pytest docs/demos/tests: 4 passed.remote-control,claude.ai/code,rc active, orTranscript saving. The same check matches on frames from the leaking recording.wt-switchandwt-statuslinecheckpoints named absolute frames past the end of these faster recordings (274–318 frames, against 344–465 in the previously published GIFs), so a negative checkpoint frame now counts back from the last frame; the omnibus checkpoint on its closingwt listuses the same anchoring. The previously published GIFs pass the new checkpoints too. Those checkpoints also forbidclaude.ai/codeandTranscript saving, and a GIF made from a leaking frame fails on them.🤖 Generated with Claude Code
https://claude.ai/code/session_01TQPDxs2csvGFGBr2j4MtrA