Skip to content

Follow Ghostty split light/dark themes per system appearance#96

Merged
vaayne merged 1 commit into
mainfrom
feat/split-light-dark-theme
Jun 26, 2026
Merged

Follow Ghostty split light/dark themes per system appearance#96
vaayne merged 1 commit into
mainfrom
feat/split-light-dark-theme

Conversation

@vaayne

@vaayne vaayne commented Jun 26, 2026

Copy link
Copy Markdown
Owner

What

Mori now follows Ghostty's split light/dark themes. Set theme = light:…,dark:… in your Ghostty config — or toggle Sync with system appearance in Settings → Theme and pick a light and a dark theme — and the terminal and Mori's own chrome (sidebar, windows, panels, tmux) switch live when you change the macOS appearance.

Why

Ghostty resolves theme = light:X,dark:Y to a single set of colors at config-finalize time, keyed on a private conditional state that defaults to light, and exposes no C API to query the other variant from a config. Mori never pushed a color scheme to libghostty nor observed the system appearance — so split themes were effectively stuck on the light variant for both the terminal and Mori's chrome.

How

  • Push color scheme to ghostty (GhosttyApp / GhosttyAdapter): new GhosttyColorScheme (resolved from NSApp.effectiveAppearance). setColorScheme(_:) calls ghostty_app_set_color_scheme + per-surface ghostty_surface_set_color_scheme (and on surface creation) so the terminal re-resolves the split theme live.
  • Re-extract chrome colors per appearance (GhosttyApp.extractThemeInfo): since a bare config can't surface the non-active variant, Mori parses the theme string itself, picks the variant for the current appearance, and forces it onto a throwaway extraction config. The terminal's own config keeps the full split string.
  • React to system changes (AppDelegate): KVO on NSApp.effectiveAppearance re-runs theme propagation (factored into propagateGhosttyTheme, shared with config reload).
  • Settings: model gains syncAppearance + darkTheme; read/write parse and emit the light:…,dark:… syntax. Theme UI adds a sync toggle with light/dark variant pickers.
  • Docs (AGENTS.md) + en/zh CHANGELOG + en/zh localization updated.

Verification

  • swift build -c release --product Mori — clean (Swift 6 strict concurrency, incl. the KVO closure)
  • mise run test — all suites pass
  • CI=1 bash scripts/bundle.sh — bundles cleanly

Not done: live GUI launch test (blocked locally by the single-instance guard against an already-running install) and a unit test for the split parser (MoriTerminal has no test target; pure string split). Manual check: set theme = light:rose-pine-dawn,dark:rose-pine, toggle system appearance, confirm terminal + sidebar follow.

Ghostty resolves `theme = light:X,dark:Y` to one set of colors at config
finalize, keyed on a private conditional state that defaults to light, with
no C API to query the other variant. Mori never pushed a color scheme or
observed the system appearance, so split themes were stuck on the light
variant for both the terminal and Mori's own chrome.

- GhosttyApp/GhosttyAdapter: push the color scheme to libghostty (app + every
  surface, and on surface creation) so the terminal re-resolves the split
  theme live; resolve the active variant ourselves and re-extract chrome
  colors via a forced single-theme extraction config.
- AppDelegate: observe NSApp.effectiveAppearance and re-run theme propagation
  (factored into propagateGhosttyTheme) on system dark/light changes.
- Settings: model gains syncAppearance + darkTheme; read/write parse and emit
  the light:…,dark:… syntax. Theme UI adds a "sync with system appearance"
  toggle with light/dark variant pickers.
- Docs + en/zh localization updated.
@vaayne vaayne merged commit 22346b0 into main Jun 26, 2026
5 checks passed
@vaayne vaayne deleted the feat/split-light-dark-theme branch June 26, 2026 01:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant