From c0b32ff069776dab7f719132fe20f6ddb36c90fb Mon Sep 17 00:00:00 2001 From: Derek Date: Tue, 1 Sep 2026 14:29:51 +1000 Subject: [PATCH] fix(ghostty): use Ghostty's bundled iTerm2 Solarized, drop vendored themes Vendored theme files only resolve from the XDG themes dir. macOS reads its config from ~/Library/Application Support/com.mitchellh.ghostty/ but resolves a bare theme name only from ~/.config/ghostty/themes/ and the app bundle, so themes deployed beside the macOS config were never found and Ghostty silently fell back to its default dark theme. Switch to the iTerm2 Solarized pair that ships inside Ghostty itself on both platforms (app bundle on macOS, /usr/share/ghostty/themes owned by the ghostty package on Linux). No OS package, nothing vendored. The bundled variant is also strictly better: canonical Solarized sets ANSI 8 to the exact dark background (invisible bright-black) and ANSI 7 too close to the light background. The iTerm2 variant lifts both off. Also fixes the Linux heal task, which matched the new theme line and would have reverted it to the vendored names on every run, and removes the now-dead vendored files from existing installs. --- .../roles/developer-gui/files/ghostty/config | 31 +++++++-- .../files/ghostty/themes/solarized-dark | 25 -------- .../files/ghostty/themes/solarized-light | 25 -------- ansible/roles/developer-gui/tasks/ghostty.yml | 63 +++++++++++-------- 4 files changed, 63 insertions(+), 81 deletions(-) delete mode 100644 ansible/roles/developer-gui/files/ghostty/themes/solarized-dark delete mode 100644 ansible/roles/developer-gui/files/ghostty/themes/solarized-light diff --git a/ansible/roles/developer-gui/files/ghostty/config b/ansible/roles/developer-gui/files/ghostty/config index 5b3ee09..f33fc7f 100644 --- a/ansible/roles/developer-gui/files/ghostty/config +++ b/ansible/roles/developer-gui/files/ghostty/config @@ -24,11 +24,32 @@ font-feature = +dlig # ============================================================ # Theme - Auto-switches with system light/dark mode # ============================================================ -# Solarized: vendored theme files in ./themes/ (self-contained - no reliance on -# Ghostty builtin themes or OS packages). Auto-switches light/dark with the OS. -# Upstream renamed/removed the "Builtin Solarized" names once already; vendoring -# means a future rename cannot break us. -theme = light:solarized-light,dark:solarized-dark +# Solarized: Ghostty's own bundled iTerm2 Solarized pair. These ship INSIDE +# Ghostty on every platform - macOS in Ghostty.app/Contents/Resources/ghostty/ +# themes/, Linux in /usr/share/ghostty/themes/ (owned by the ghostty package +# itself, verified with dpkg -S). No OS package, no vendored files, nothing to +# install. The "iTerm2" in the name is just upstream's naming from the +# iterm2-color-schemes collection - iTerm2 is NOT required. +# +# Auto-switches light/dark with the OS desktop appearance. +# +# We do NOT vendor a canonical Schoonover palette any more, for two reasons: +# +# 1. Vendored theme files only resolve from the XDG dir. macOS reads its +# CONFIG from ~/Library/Application Support/com.mitchellh.ghostty/ but +# resolves a bare theme NAME only from ~/.config/ghostty/themes/ and the +# app bundle. Themes deployed next to the macOS config were never found, +# and Ghostty silently fell back to its default dark theme. +# 2. The canonical palette is actively worse. It sets ANSI 8 to #002b36 - +# the exact dark background, so bright-black text was invisible - and +# ANSI 7 to #eee8d5 against the #fdf6e3 light background. The iTerm2 +# variant lifts both off the background (#335e69 / #bbb5a2) and is +# otherwise identical to canonical Solarized. +# +# TRADEOFF: upstream renamed "Builtin Solarized" out from under us once +# before. If the theme stops applying after a Ghostty upgrade, check +# `ghostty +list-themes` for the current name. +theme = light:iTerm2 Solarized Light,dark:iTerm2 Solarized Dark # ============================================================ # Terminal Compatibility diff --git a/ansible/roles/developer-gui/files/ghostty/themes/solarized-dark b/ansible/roles/developer-gui/files/ghostty/themes/solarized-dark deleted file mode 100644 index f3ca9be..0000000 --- a/ansible/roles/developer-gui/files/ghostty/themes/solarized-dark +++ /dev/null @@ -1,25 +0,0 @@ -# Solarized Dark — self-contained Ghostty theme (hyperi-developer) -# Canonical Solarized palette by Ethan Schoonover (https://ethanschoonover.com/solarized) -# Vendored so the install never depends on Ghostty's builtin themes or OS packages. -palette = 0=#073642 -palette = 1=#dc322f -palette = 2=#859900 -palette = 3=#b58900 -palette = 4=#268bd2 -palette = 5=#d33682 -palette = 6=#2aa198 -palette = 7=#eee8d5 -palette = 8=#002b36 -palette = 9=#cb4b16 -palette = 10=#586e75 -palette = 11=#657b83 -palette = 12=#839496 -palette = 13=#6c71c4 -palette = 14=#93a1a1 -palette = 15=#fdf6e3 -background = #002b36 -foreground = #839496 -cursor-color = #839496 -cursor-text = #002b36 -selection-background = #073642 -selection-foreground = #93a1a1 diff --git a/ansible/roles/developer-gui/files/ghostty/themes/solarized-light b/ansible/roles/developer-gui/files/ghostty/themes/solarized-light deleted file mode 100644 index 9450d59..0000000 --- a/ansible/roles/developer-gui/files/ghostty/themes/solarized-light +++ /dev/null @@ -1,25 +0,0 @@ -# Solarized Light — self-contained Ghostty theme (hyperi-developer) -# Canonical Solarized palette by Ethan Schoonover (https://ethanschoonover.com/solarized) -# Vendored so the install never depends on Ghostty's builtin themes or OS packages. -palette = 0=#073642 -palette = 1=#dc322f -palette = 2=#859900 -palette = 3=#b58900 -palette = 4=#268bd2 -palette = 5=#d33682 -palette = 6=#2aa198 -palette = 7=#eee8d5 -palette = 8=#002b36 -palette = 9=#cb4b16 -palette = 10=#586e75 -palette = 11=#657b83 -palette = 12=#839496 -palette = 13=#6c71c4 -palette = 14=#93a1a1 -palette = 15=#fdf6e3 -background = #fdf6e3 -foreground = #657b83 -cursor-color = #657b83 -cursor-text = #fdf6e3 -selection-background = #eee8d5 -selection-foreground = #586e75 diff --git a/ansible/roles/developer-gui/tasks/ghostty.yml b/ansible/roles/developer-gui/tasks/ghostty.yml index 21809b4..a44c34c 100644 --- a/ansible/roles/developer-gui/tasks/ghostty.yml +++ b/ansible/roles/developer-gui/tasks/ghostty.yml @@ -132,14 +132,6 @@ mode: '0644' when: ansible_facts['distribution'] in ['Fedora', 'Ubuntu'] -# Vendored Solarized themes - always refreshed (managed files, not user-edited) -- name: Deploy Ghostty themes (Linux skel) - ansible.builtin.copy: - src: ghostty/themes/ - dest: "{{ ghostty_skel_dir }}/themes/" - mode: '0644' - when: ansible_facts['distribution'] in ['Fedora', 'Ubuntu'] - # Deploy to current user as well (Linux) - name: Create Ghostty config directory for actual user (Linux) ansible.builtin.file: @@ -170,26 +162,37 @@ force: false when: ansible_facts['distribution'] in ['Fedora', 'Ubuntu'] -- name: Deploy Ghostty themes for actual user (Linux) - ansible.builtin.copy: - src: ghostty/themes/ - dest: "{{ user_home }}/.config/ghostty/themes/" - mode: '0644' - owner: "{{ actual_user }}" - group: "{{ actual_user }}" - when: ansible_facts['distribution'] in ['Fedora', 'Ubuntu'] - # Heal existing configs: the base config ships with force:false to preserve user -# edits, so older installs keep their (broken) builtin Solarized theme line. This -# rewrites just that line to the vendored themes without touching anything else. +# edits, so older installs keep a stale theme line - either the long-dead +# "Builtin Solarized *" names or the vendored "solarized-light/dark" ones. This +# rewrites just that line to the bundled themes without touching anything else. +# +# The regexp matches the replacement too, so this is idempotent: a config +# already on the new line is rewritten to the identical string and ansible +# reports ok, not changed. - name: Fix stale Ghostty Solarized theme line in existing user config (Linux) ansible.builtin.replace: path: "{{ user_home }}/.config/ghostty/config" - regexp: '^theme\s*=.*Solarized.*$' - replace: 'theme = light:solarized-light,dark:solarized-dark' + regexp: '^theme\s*=.*[Ss]olarized.*$' + replace: 'theme = light:iTerm2 Solarized Light,dark:iTerm2 Solarized Dark' failed_when: false when: ansible_facts['distribution'] in ['Fedora', 'Ubuntu'] +# Remove the vendored theme files left behind by earlier runs of this role. +# Ghostty now uses its own bundled iTerm2 Solarized pair, so these are dead +# files - and leaving them means a stale theme silently wins if anyone still +# has a "solarized-light" line in a config we did not heal. +- name: Remove retired vendored Ghostty themes (Linux) + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ user_home }}/.config/ghostty/themes/solarized-light" + - "{{ user_home }}/.config/ghostty/themes/solarized-dark" + - "{{ ghostty_skel_dir }}/themes/solarized-light" + - "{{ ghostty_skel_dir }}/themes/solarized-dark" + when: ansible_facts['distribution'] in ['Fedora', 'Ubuntu'] + # ============================================================ # macOS Configuration Deployment # ============================================================ @@ -217,11 +220,19 @@ become: false when: ansible_facts['distribution'] == 'MacOSX' -- name: Deploy Ghostty themes (macOS) - ansible.builtin.copy: - src: ghostty/themes/ - dest: "{{ ghostty_macos_dir }}/themes/" - mode: '0644' +# No theme deployment on macOS. Ghostty resolves a bare theme NAME from +# ~/.config/ghostty/themes/ and the app bundle only - never from the macOS +# Application Support dir where its config lives. Themes copied here were +# therefore never found, which is exactly the bug this change retires. +# +# Clean up the files earlier runs left in that dead directory. +- name: Remove retired vendored Ghostty themes (macOS) + ansible.builtin.file: + path: "{{ item }}" + state: absent + loop: + - "{{ ghostty_macos_dir }}/themes/solarized-light" + - "{{ ghostty_macos_dir }}/themes/solarized-dark" become: false when: ansible_facts['distribution'] == 'MacOSX'