Repository navigation
Light/dark/system theme mode + custom themes from disk - #47
Conversation
Phase 1 — Mode model. Replaces the single `theme: string` setting with
`themeMode` ('light' | 'dark' | 'system') plus `themeLight` and
`themeDark` slots. With mode=system, the renderer follows the OS color
scheme live via `matchMedia('(prefers-color-scheme: dark)')`. Migration
v6→v7 maps the old `theme` string into the new slots, preserving the
user's explicit mode (not forcing 'system').
Phase 2 — User-authored themes from `<userData>/themes/*.json`. The
loader validates `name`/`mode`/optional `colors` and silently skips
malformed files. Custom themes show up in the mode-filtered pickers
alongside built-ins; the apply helper sets only the keys the JSON
overrides as inline `--color-*` properties on documentElement, with the
matching mode's default theme as the [data-theme] base so missing keys
inherit sensibly. Inline keys are tracked per apply so a switch back to
a built-in cleanly clears leftovers (inline styles would otherwise
outrank attribute selectors).
- New slice fields + 4 events; one reducer test per event variant
- IPC: setThemeMode / setThemeLight / setThemeDark + setLastEffectiveAppBg
(fire-and-forget) + reloadCustomThemes + openThemesFolder
- New `useSystemColorScheme` + `useActiveTheme` hooks + `applyTheme` /
`effectiveAppBg` helpers; ResolvedTheme is a tagged union so callers
don't branch on origin
- Settings UI: native-radio Mode segmented control inside fieldset/
legend, filtered Light/Dark pickers (built-ins + customs), Custom
themes section with Open / Reload buttons
- Onboarding: dark-only picker, less judgmental copy, writes themeDark
- Window first paint uses `nativeTheme.shouldUseDarkColors` when mode
is 'system', falls back through `lastEffectiveAppBg` then `#0a0a0a`
- IPC validation accepts any currently-loaded custom-theme id of the
matching mode (not just AVAILABLE_THEMES)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The new files and the reformatted build-initial-state.ts landed with tabs + double quotes + semicolons from a Biome-with-defaults editor. Bring them in line with the rest of the codebase (2-space indent, single quotes, no trailing semicolons, plain 'fs'/'path' imports without the node: prefix). No behavior changes. Drift-prevention follow-up tracked in #48. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pushed a follow-up commit (0bc6b17) reformatting the five files that landed with tabs + double quotes + trailing semicolons to match the rest of the codebase (2-space indent, single quotes, no semis, plain
No behavior changes — pure style. Verified To prevent this kind of drift going forward, opened #48 to add a code formatter (Prettier or Biome) with CI enforcement. |
The strings 'solarized-light' and 'dark' as the per-mode defaults were sprinkled across six call sites (build-initial-state, desktop-shell, index.ts IPC handlers, useActiveTheme local consts, theme-apply data-theme fallback). Replace with two shared constants in src/shared/state/settings.ts so main and renderer agree on the default through the same import. Drops the now-redundant `DEFAULT_THEME` from persistence.ts (only one caller; replaced with `DEFAULT_DARK_THEME`). No behavior changes — same string values, just one source of truth. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Also pushed b16ce0e to address review point #2 — extracted the per-mode default theme ids into shared constants.
No behavior changes — identical string values, just one source. Verified |
|
this is awesome thank you @pgodschalk for becoming a contributor! |
Phase 1 — Mode model. Replaces the single
theme: stringsetting withthemeMode('light' | 'dark' | 'system') plusthemeLightandthemeDarkslots. With mode=system, the renderer follows the OS color scheme live viamatchMedia('(prefers-color-scheme: dark)'). Migration v6→v7 maps the oldthemestring into the new slots, preserving the user's explicit mode (not forcing 'system').Phase 2 — User-authored themes from
<userData>/themes/*.json. The loader validatesname/mode/optionalcolorsand silently skips malformed files. Custom themes show up in the mode-filtered pickers alongside built-ins; the apply helper sets only the keys the JSON overrides as inline--color-*properties on documentElement, with the matching mode's default theme as the [data-theme] base so missing keys inherit sensibly. Inline keys are tracked per apply so a switch back to a built-in cleanly clears leftovers (inline styles would otherwise outrank attribute selectors).useSystemColorScheme+useActiveThemehooks +applyTheme/effectiveAppBghelpers; ResolvedTheme is a tagged union so callers don't branch on originnativeTheme.shouldUseDarkColorswhen mode is 'system', falls back throughlastEffectiveAppBgthen#0a0a0a