fix(web): keep environment themes on reload#23861

open1
main
GitHub fork: fix/theme-flash-on-reload
opened12h agobyCClaudesaul
View on GitHubTwo-way sync enabledPR last imported 12h ago

Problem

With an environment theme selected (a theme file in ~/.t3/userdata/themes that the server publishes), every reload paints the stock theme first: stock black or white, with the blue accent and sidebar header, until the server connects and the real theme replaces it. It happens in light and dark mode, and is most visible in dark.

Change

Environment themes only arrive from the server after the app connects, and the client never stored them, so the boot script in index.html had nothing to paint. After connecting, a theme list that hadn't arrived yet was also treated as empty for a moment.

The web app now caches the server's themes in localStorage, the same way custom themes are already stored for boot. The boot script and the first render paint from that cache. In the server config projection, undefined now means "not received yet" and [] means "no themes", so the cache is only replaced once the server's theme update arrives. Deleting the last theme still clears it.

Scope and approval

Small fix for an obvious bug, extending the existing custom-theme boot storage. No new settings.

Verification

  • New themeBoot.test.ts case fails without the fix. Affected tests pass (63), and vp run check:changed passes.
  • macOS desktop app: reloads in light, dark and System mode paint the environment theme from the first frame.
  • Switching environments, browser build: a different palette under the same theme id replaces the cached one, an environment with no themes empties the cache, and switching back restores the theme.

Reload in the desktop app, dark mode on the left and light mode on the right, both clips starting at the reload.

Before:

External image

After:

External image

Model: Claude Opus 5.5, reviewed by GPT-6.1-Sol. Harness: Claude Code through T3 Code.

Checks

All checks passed

Added 1 commit

  • Keep the environment theme on reload instead of flashing stock dark005ad22
Synced from GitHub

Approvability

Verdict: Approved at 005ad22

Macroscope's review found this PR approvable — This is a focused fix for environment themes flashing to stock colors on reload. It adds a validated localStorage snapshot for first paint and preserves it until the server provides authoritative data, with the remaining changes limited to synchronization semantics and tests.

You can add or adjust custom eligibility rules. Learn more.

Synced from GitHub

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
📥 Commits

Reviewing files that changed from the base of the PR and between 2c8bdf6177159d8fcd12a897d7c9311f6eb93237 and 005ad22101207e347e5b8c91576b77de8cf19996.

📒 Files selected for processing: 7
  • apps/web/index.html
  • apps/web/src/hooks/useEnvironmentTheme.ts
  • apps/web/src/state/server.ts
  • apps/web/src/themeBoot.test.ts
  • apps/web/src/themePalette.ts
  • packages/client-runtime/src/rpc/session.test.ts
  • packages/client-runtime/src/state/serverConfigProjection.ts

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.

📝 Walkthrough

Environment themes now persist as versioned local-storage snapshots, and startup lookup can resolve them after saved custom themes. Server state distinguishes missing or pending theme data from an advertised empty set. Synchronization leaves the stored snapshot unchanged while theme data is null.

Changes: Environment theme flow

Layer / File(s)Summary
Server theme state and projection
packages/client-runtime/src/state/serverConfigProjection.ts, apps/web/src/state/server.ts, packages/client-runtime/src/rpc/session.test.ts
The server projection preserves empty theme arrays. The primary-server atom returns null when configuration or advertised themes are not yet available, and returns an empty array when the server does not support environment themes. Replay tests expect an empty array after deletion.
Environment theme snapshot storage
apps/web/src/themePalette.ts
The palette cache lazily loads a versioned local-storage snapshot. Changed themes are persisted when possible, and environment-theme lookup uses the getter.
Startup theme resolution and synchronization
apps/web/index.html, apps/web/src/hooks/useEnvironmentTheme.ts, apps/web/src/themeBoot.test.ts
The boot script checks saved custom themes before environment themes. The sync effect skips null publications. A test checks startup selection of a dark environment-theme variant.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Bug fix

Sequence Diagram(s)

mermaid
1sequenceDiagram
2 participant ServerConfigProjection
3 participant PrimaryServerEnvironmentThemesAtom
4 participant useEnvironmentThemeSync
5 participant themePalette
6 participant localStorage
7 participant BootScript
8 ServerConfigProjection->>PrimaryServerEnvironmentThemesAtom: Store published themes, including empty arrays
9 PrimaryServerEnvironmentThemesAtom->>useEnvironmentThemeSync: Provide themes or null
10 useEnvironmentThemeSync->>themePalette: Set themes when publication is non-null
11 themePalette->>localStorage: Persist versioned theme snapshot
12 BootScript->>localStorage: Read environment-theme snapshot
13 BootScript->>BootScript: Resolve saved custom theme, then environment theme

Suggested reviewers: maria-rcks, juliusmarminge

Merge Risk: 🔵 Low · up to 005ad

Reloading alongside another environment can briefly show the wrong colors. The palette corrects when the selected environment publishes its themes, so this is a bounded visual risk rather than a merge blocker.

Pre-merge checks: Passed 4
Check nameStatusExplanation
Title checkPassedThe title clearly and concisely describes the main fix: preserving environment themes across reloads.
Description checkPassedThe description includes the problem, implementation, scope rationale, focused test results, manual verification, and before/after evidence. It also explains why prior approval was not required for th…
Linked Issues checkPassedCheck skipped because no linked issues were found for this pull request.
Out of Scope Changes checkPassedCheck skipped because no linked issues were found for this pull request.
Full details: Description check

Explanation

The description includes the problem, implementation, scope rationale, focused test results, manual verification, and before/after evidence. It also explains why prior approval was not required for this obvious bug fix.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autofix · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Comment @coderabbitai help to get the list of available commands.