Skip to content

docs(demos): record demos with the site's current palette - #4069

Merged
max-sixty merged 9 commits into
mainfrom
nightly/clean-34678483266-theme-comments
Sep 12, 2026
Merged

max-sixty merged 9 commits into
mainfrom
nightly/clean-34678483266-theme-comments

Conversation

@worktrunk-bot

@worktrunk-bot worktrunk-bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

The demo recorder kept its own copies of the docs site's colors, and they had drifted from docs/src/styles/custom.css:

  • The VHS terminal's ANSI colors were still the Zola site's palette, which docs: rebuild the site with Astro and Starlight #3866 deleted and Polish mobile docs terminals #3936 replaced with --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.
  • Zellij, the starship prompt, and Claude Code were configured for light in both themes, so dark GIFs showed a light Zellij tab bar and Claude Code's light theme.

docs/demos/shared/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 (terminal_color_class and terminal_background_class in tests/integration_tests/readme_sync.rs). DemoEnv carries 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 inherited CLAUDE* variables except CLAUDE_CODE_OAUTH_TOKEN, and the demo's settings.json sets remoteControlAtStartup to false.

The GIFs are re-recorded locally (./docs/demos/build docs and ./docs/demos/build social) and published in max-sixty/worktrunk-assets@bb3bfba.

Verification

  • pytest docs/demos/tests: 4 passed.
  • OCR of every 20th frame of the Claude demos (354 frames) finds no remote-control, claude.ai/code, rc active, or Transcript saving. The same check matches on frames from the leaking recording.
  • Frames checked by eye in both themes for the ANSI colors, the gutter, and the Zellij chrome.
  • TUI validation passes for all nine Claude demo GIFs. The wt-switch and wt-statusline checkpoints 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 closing wt list uses the same anchoring. The previously published GIFs pass the new checkpoints too. Those checkpoints also forbid claude.ai/code and Transcript saving, and a GIF made from a leaking frame fails on them.

🤖 Generated with Claude Code

https://claude.ai/code/session_01TQPDxs2csvGFGBr2j4MtrA

This was written by Claude Code on behalf of max-sixty

@worktrunk-bot worktrunk-bot added the nightly-cleanup Issues found by nightly code quality sweep label Sep 12, 2026

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docs/demos/shared/themes.py Outdated
Comment thread docs/demos/shared/themes.py Outdated
Comment thread docs/demos/shared/themes.py Outdated
…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 worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread docs/demos/shared/themes.py Outdated
Comment thread docs/demos/shared/themes.py Outdated
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.
max-sixty and others added 3 commits September 12, 2026 13:30
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 worktrunk-bot changed the title docs(demos): repoint the VHS theme comments at the site's current custom properties docs(demos): derive the demo recording palettes from the site's stylesheet Sep 12, 2026

@worktrunk-bot worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.
@max-sixty max-sixty changed the title docs(demos): derive the demo recording palettes from the site's stylesheet docs(demos): record demos with the site's current palette Sep 12, 2026
…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 worktrunk-bot left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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
@max-sixty
max-sixty merged commit ee52834 into main Sep 12, 2026
37 checks passed
@max-sixty
max-sixty deleted the nightly/clean-34678483266-theme-comments branch September 12, 2026 22:42
worktrunk-bot added a commit that referenced this pull request Sep 13, 2026
…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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

nightly-cleanup Issues found by nightly code quality sweep

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants