3D replay: dark / light / auto theme with an off-white light mode#230
Merged
Conversation
A Theme switch (Dark / Light / Auto) at the top of the View section,
stored as 'theme' in the shared glidecomp:preferences record ('system'
= auto, following prefers-color-scheme live via matchMedia).
- Light mode uses an off-white background (#f2f0e9), not full white.
- main.ts toggles a 'light' class on <html>; the page's own <style>
defines --rp-* theme tokens plus scoped remaps of the dark slate
utilities the standalone page uses. The gauge canvas reads the same
tokens (needle/dial colours re-read + dial repainted on switch).
- In-scene furniture follows the theme only on the abstract backdrop
(clear colour, grid, ground + gaggle label halos, and the vario-ramp
zero via a new uVarioZero uniform so near-level trail segments don't
vanish into the off-white). The scene rebuilds in place on switch;
on terrain the backdrop is map imagery so the scene stays map-styled.
- The vario colour-scale legend mirrors the themed ramp; the meta
theme-color and the saved theme are applied before load so light
users don't get a dark flash.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DZpFYsCEyR7ck3bWmQNhCr
The dark-backdrop pastels washed out on the off-white: - Turnpoint rings/walls use deeper shades in light mode (emerald-700 start, red-600 goal, amber-700 turnpoints); wall opacity drops a notch to compensate for the darker colour stacking across DoubleSide layers. - Every pilot cone gets a dark inverted-hull silhouette (a ~16% larger back-face-only InstancedMesh mirroring the marker matrices), so pale pilot colours — light lime, yellow — read crisply without changing the identity colour that matches the legend chips and badges. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DZpFYsCEyR7ck3bWmQNhCr
…works In map mode on a phone, pan and orbit were impossible while following a pilot: the follow calls map.setCenter every frame, setCenter wraps jumpTo, and jumpTo's first act is stop() — which cancels the active gesture handlers. Mouse drags mostly survived (they re-establish per mousemove); touch gestures are stateful and died continuously. It even fired with playback paused, since the zero-delta case still called setCenter. followTo now keeps the follow anchor fresh every frame but skips setCenter when (a) the pilot hasn't moved, (b) any pointer is down on the map (container pointerdown / window pointerup+pointercancel counter, so a finger released off-map can't leak), or (c) map.isEasing() — so the orientation presets' easeTo survives too (isEasing, not isMoving, which could report the follow's own jumpTo and starve it). Because the anchor keeps tracking during a gesture the follow resumes from the pilot's live position with no jump. The abstract backend is untouched — OrbitControls deltas compose with the follow shift naturally. Unit-tested with a mocked mapbox-gl in terrain-follow.test.ts (anchor, zero-delta, gesture yield + resume, multi-touch, easing, re-anchor cases). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01DZpFYsCEyR7ck3bWmQNhCr
|
Preview Deployment |
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.
Follow-up to #229. The replay gets a light mode with a Dark / Light / Auto switch at the top of the View section. Light mode uses an off-white background (
#f2f0e9), deliberately not full white.Behaviour
themein the sharedglidecomp:preferencesrecord ('light' | 'dark' | 'system'— the field already existed inUserPreferences), so it persists and cloud-syncs like the units.'system') followsprefers-color-schemelive viamatchMedia— flipping the OS scheme re-themes the page without a reload.main()(and themeta[name=theme-color]updated), so a light-mode user never sees a dark flash while the tracks load.Mechanics
main.tstoggles alightclass on<html>. The page's own<style>defines--rp-*theme tokens plus scoped remaps of the dark slate utilities the markup uses — the page is standalone, so remapping ~20 utilities in its own style block beats duplicating a light class next to every one of them (no other page is affected).--rp-*tokens (needle/dial colours re-read and the dial repainted on switch), and the climb digit colours pick up higher-contrast lime/sky shades in light mode via the same remap.uVarioZeroshader uniform, darkened in light mode so near-level trail segments don't vanish into the off-white. The scene rebuilds in place on switch (same path as backdrop switching). On the terrain backdrop the scene always builds dark-styled — there the backdrop is map imagery, not the UI theme.Light-mode contrast pass (from device feedback)
InstancedMesh(~16% larger, back-face-only, matrices mirroring the markers) — so pale pilot colours (light lime, yellow) read crisply without changing the identity colour that matches the legend chips and badges.Bug fix: touch pan/orbit while following on the map backdrop
In map mode on a phone, pan and orbit were impossible while following a pilot. Root cause: the follow calls
map.setCenterevery frame;setCenterwrapsjumpTo, whose first act isstop()— which cancels Mapbox's active gesture handlers. Mouse drags mostly survived (they re-establish per mousemove); touch gestures are stateful and died continuously. It even fired with playback paused, since the zero-delta case still calledsetCenter.followTonow keeps its anchor fresh every frame but skipssetCenterwhen (a) the pilot hasn't moved, (b) any pointer is down on the map (containerpointerdown/ windowpointerup+pointercancelcounter, leak-proof for fingers released off-map), or (c)map.isEasing()— so the orientation presets'easeTosurvives too (isEasing, notisMoving, which could report the follow's own jumpTo and starve it). The anchor keeps tracking during a gesture, so the follow resumes from the pilot's live position with no jump. The abstract backend needed no change — OrbitControls deltas compose with the follow shift naturally. Unit-tested with a mocked mapbox-gl (terrain-follow.test.ts: anchor, zero-delta, gesture yield/resume, multi-touch, easing, re-anchor).Verification
bun run typecheck:allpasses;bun test web/frontend/src/replay/terrain-follow.test.ts— 6/6 pass.rgb(242, 240, 233), updatesmeta[theme-color], storestheme: "light", and the rendered WebGL scene goes off-white (screenshot-sampled luma 238); badges and the callout still work after the in-place scene rebuild; the theme persists across reload; Auto resolves an emulated dark OS scheme to dark and follows a live flip to light (stored as"system"); the full dark-mode regression suite still passes with no console errors.docs/3d-flight-replay-notes.md§5.12 + §5.15).🤖 Generated with Claude Code
https://claude.ai/code/session_01DZpFYsCEyR7ck3bWmQNhCr