[diffshub] Add a Shiki theme switcher#745
Open
necolas wants to merge 7 commits into
Open
Conversation
Some themes ship a list.hoverBackground designed for their editor surface, whose palette can be the inverse of the sidebar's. Slack-ochin is the worst case I've seen: editor.background is white, but sideBar.background is dark navy with light gray text, and list.hoverBackground is #d5e1ea — a near-white wash. Applied to the sidebar that overlay lands on the same luminance band as sideBar.foreground and the hovered row's text disappears. Add a luminance-based heuristic to themeToTreeStyles: parse the hover bg, sidebar bg, and sidebar fg as hex (the dominant format in @shikijs/themes), compute relative luminance, and drop the hover bg when it sits closer in luminance to the fg than to the bg. Non-hex formats fall through unchanged so themes that use modern color syntax keep their intended hover. Also pick the rgba fallback's tint from the sidebar's actual luminance rather than `theme.type`. Otherwise slack-ochin (declared `light` for the editor) would land on `rgba(0,0,0,0.06)` over a dark navy surface — invisible. Reading the bg directly picks `rgba(255,255,255,0.08)` for the dark sidebar so users still get hover feedback. Cover the new behavior with focused unit tests for the slack-ochin shape, a typical light theme, the equal-color short-circuit, and a non-hex hover bg.
Add usePersistedState, which mirrors a string-valued useState to localStorage and rehydrates from it on mount (in an effect, not the useState initializer, so the SSR markup stays identical to the first client render and hydration doesn't mismatch). Rehydration only accepts values present in a `validValues` allowlist, so a stale or hand-edited key can't push a consumer into an unknown value. A hydrated-ref guard skips the first write so the initial commit's default can't clobber the stored value before the read effect runs. The hook also returns a `hydrated` flag so callers can wait for the real value before pushing it to long-lived singletons.
List the light/dark Shiki theme names available to the diffshub theme pickers, along with the color-mode type and default light/dark picks. Mirrors the catalog shown in the diffs docs "Adapts to any Shiki theme" example so users can browse the same set from the diffshub UI.
Add useResolvedTreeThemeStyles / useThemeChromeStyle, which resolve the selected light/dark Shiki theme (cached after first use) and derive a set of CSS custom properties for the diffshub chrome: sidebar/header surface, popover and control colors, comment-card and inline-annotation tokens. Foreground and muted-foreground are picked by contrast against the resolved surface rather than trusting the theme's declared values, so chrome text stays legible on mixed-palette themes. The muted threshold (4.5:1) is split from the primary one because muted sidebar labels render at body-text size; when a theme's descriptionForeground fails the bar, deriveMutedFg blends primaryFg into the background until it clears 4.5:1.
Add a theme picker next to the gear icon in the diffshub header: a light-theme list, a dark-theme list, and an Auto/Light/Dark color-mode toggle, all in a single dropdown that switches between views in place so it fits narrow viewports. The list auto-scrolls to the active row and uses the shared mini scrollbar. ReviewUI owns the light/dark/color-mode state (light and dark picks are persisted) and drives the CodeView's `themeType` from the shared ThemeProvider color mode so Auto/Light/Dark flips both the diff and the app's light/dark class.
Source the sidebar, file tree, header dropdowns, comment cards, and inline annotations from the resolved Shiki chrome tokens so the whole UI tracks the selected theme instead of the global light/dark palette. Addition/deletion comment labels and the tab count badge are tinted from the active surface so they stay legible on mixed-palette themes; the file tree now follows the picked theme rather than the fixed pierre-soft pair. Keep diff and chrome repaints in the same frame on a theme swap: push theme changes to the WorkerPool from a useLayoutEffect (gated on theme hydration so client-side navigation doesn't tokenize the first batch against the default palette), and drop the color transitions on comment cards and inline links that otherwise interpolated through the old palette while the rest of the UI snapped instantly.
Add a button to the System Monitor panel that sweeps through every Shiki theme so reviewers can preview the full set without picking each one by hand. useThemeCycle captures the user's current pick when cycling starts so the visible theme anchors the rotation, and drives the same light/dark and color-mode setters the header picker uses.
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
4 tasks
Contributor
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.


Clean history version of #731
Summary
Adds a theme switcher to the diffshub code view so reviewers can render diffs
and the surrounding chrome in any Shiki theme, independent of the app's
global light/dark palette.
dark-theme list, and an Auto/Light/Dark color-mode toggle, all in one
in-place dropdown that fits narrow viewports. Picks persist across reloads.
annotations all source their colors from the resolved Shiki theme. Foreground
and muted-foreground are chosen by contrast against the resolved surface
(not the theme's declared values) so text stays legible on mixed-palette
themes (e.g. slack-ochin, ayu-dark).
previewing.
theme hydration so client-side navigation doesn't tokenize against the
default palette.
themeToTreeStylesnow rejects a theme'slist.hoverBackgroundwhen its luminance would erase the hovered row's text, and derives the rgba
hover fallback from the sidebar's actual luminance rather than the declared
theme type.
Commits
Organized into 7 focused commits (foundations first, then UI):
[trees]Rejectlist.hoverBackgroundwhen it would erase row text[diffshub]Add a localStorage-backed persisted state hook[diffshub]Add the Shiki theme catalog[diffshub]Resolve Shiki themes into themed chrome tokens[diffshub]Add a theme switcher to the code view header[diffshub]Apply the resolved theme across the chrome[diffshub]Add a theme-cycle button to the System MonitorTest plan
wrong-palette flash
labels / comment labels stay legible