* feat(ui): expand theme system to 49 themes with matched syntax highlighting
Add 25 new themes sourced from shiki's bundled TextMate themes (Everforest,
Nord, Solarized, GitHub, One Dark Pro, Night Owl, Material, Vitesse, Vesper,
Poimandres, Ayu Dark, Houston, Laserwave, Andromeeda, Aurora X, Dark+/Light+,
Min, One Light, Plastic, Red, Slack, Snazzy Light, Vitesse Black). Each theme
was audited against the shiki source JSON to ensure correct color mapping.
Wire pierre/diffs syntax highlighting to match the active UI theme via a
SHIKI_THEME_MAP (35 of 49 themes get matched syntax token colors in diffs).
Add theme preview mode — a compact bottom-docked picker that lets users
browse themes while seeing the page update live behind it.
Settings panel enhancements:
- Syntax highlighting badge on themes with diff color matching
- Mode unavailability dimming (dark-only themes dim in light mode)
- Plannotator pinned first, rest alphabetical
* fix(themes): address review feedback — contrast, stale CSS, syntax mode safety
1. Material light: foreground #90A4AE → #546E7A (Blue Grey 600) for readable
UI text (~7:1 contrast on white). Primary-foreground #FAFAFA → #263238
for contrast on teal buttons.
2. DiffHunkPreview: add colorTheme to useEffect deps so unsafeCSS (bg/gutter
injection) recomputes when themes change, preventing stale mixed-theme
state in mounted tooltips during preview mode.
3. SHIKI_THEME_MAP: make mode-aware with null entries for modes lacking a
shiki counterpart. resolveSyntaxTheme() returns undefined when the current
mode has no match, so pierre falls back to its defaults rather than
applying dark token colors on a light background (fixes Tokyo Night light).
* fix(review): self-review cleanup — memo, double-call, escape key
- DiffHunkPreview: extract resolveSyntaxTheme to a variable instead of
calling it twice in the spread expression
- PierreDiffContent memo: compare syntaxTheme by dark/light string values
instead of reference equality (resolver returns new objects each call)
- Preview mode: add Escape key handler to return to full settings
* fix(ui): consume Escape event in theme preview to prevent leaking to page