--- name: noctalia-custom-palette-recolor description: "Recolor a live Noctalia desktop (accent, terminal palette, terminal foreground) by cutting over to a custom palette instead of editing the community-palette cache, then re-rendering and reloading templates." --- # Noctalia custom-palette recolor Recolor the running Noctalia shell + all themed apps without restarting the session. ## Why custom, never the community cache - `~/.local/state/noctalia/community-palettes/*.json` is a **sync cache**. `~/.local/state/noctalia/community-palettes/.catalog/palettes.json` carries an `md5` per palette and noctalia re-fetches from `https://api.noctalia.dev/palettes`. Edits there get reverted. - Custom palettes live at `~/.config/noctalia/palettes/.json` (`NOCTALIA_CONFIG_HOME` then `XDG_CONFIG_HOME` honored) and are never synced. Missing/invalid file → silent fallback to the builtin palette. - Palette JSON shape = the community file's shape: `dark`/`light` maps of `mPrimary`, `mOnPrimary`, `mSecondary`, `mTertiary`, `mError`, `mSurface`, `mSurfaceVariant`, `mOutline`, `mShadow`, `mHover` plus a `terminal` block (`foreground`, `background`, `normal.{black..white}`, `bright.{...}`, `cursor`, `cursorText`, `selectionFg`, `selectionBg`). NOTE: `noctalia theme -o` emits M3 snake_case tokens (`on_primary_container`, …) — that is the *generator* format, not this one. Copy an existing community JSON and mutate it with `jq`. ## Procedure 1. Read the live scheme first — never assume: `noctalia msg color-scheme-get` → e.g. `community Gruber Darker`; `noctalia msg theme-mode-get`. 2. Back up settings: `cp -a ~/.local/state/noctalia/settings.toml /tmp/noctalia-settings-backup-$(date +%s).toml`. 3. Build the palette from the current one (jq keeps structure; no hand-editing 2 KB of JSON): ```sh mkdir -p ~/.config/noctalia/palettes jq '.dark.mPrimary = "#f43841" | .dark.terminal.normal.yellow = "#f43841" | .dark.terminal.bright.yellow = "#ff4f58" | .dark.terminal.foreground = "#f43841"' \ "$HOME/.local/state/noctalia/community-palettes/Gruber%20Darker.json" \ > ~/.config/noctalia/palettes/"Gruber Red.json" ``` Prefer a hex already present in the palette (its own `mError` red / accent) so the theme stays internally consistent. Set BOTH `normal` and `bright` variants, and both `dark` and `light` if the user toggles modes. 4. Switch (persists `theme.source = custom` + `theme.custom_palette`): `noctalia msg color-scheme-set custom "Gruber Red"` → `ok`; confirm with `noctalia msg color-scheme-get`. 5. Re-render every configured template: `noctalia msg templates-apply`. Regenerates `~/.config/ghostty/themes/noctalia`, `~/.config/gtk-{3,4}.0/noctalia.css`, kcolorscheme, foot, wezterm, starship, etc. Some of those paths are symlinked into the dotfiles repo → they show up as modified files worth committing. 6. Reload surfaces: - Compositor (focus border, `noctalia.kdl`): `niri msg action load-config-file`. `reload-config` is NOT a valid niri subcommand. - Ghostty: see the reload trap below. ## Verification - `ghostty +show-config | grep -E '^(foreground|palette)'` — proves the theme file resolves into the config (e.g. `foreground = #f43841`, `palette 3/11` red). - `noctalia config validate` → `✓ Config is valid`. - Visual: `noctalia msg screenshot-fullscreen` saves to `~/Pictures/screenshot_.png` (it ALSO overwrites the clipboard with the PNG — warn the user). `read` that PNG to confirm accent/toggle/border color. - `grep -c ~/.config/noctalia/palettes/*.json` → 0 for no leftovers. ## Track it in the dotfiles repo (command-center `~/.omp`) `scripts/deploy.sh` is a stow-equivalent: packages are home-relative trees under `dotfiles//`, and deploying replaces live files with symlinks into the repo. Mirror that shape for the new palette so `git pull` + `deploy.sh` is the whole propagation story: ```sh mkdir -p ~/.omp/dotfiles/noctalia/.config/noctalia/palettes mv ~/.config/noctalia/palettes/".json" ~/.omp/dotfiles/noctalia/.config/noctalia/palettes/ ln -s "$HOME/.omp/dotfiles/noctalia/.config/noctalia/palettes/.json" \ ~/.config/noctalia/palettes/".json" noctalia msg color-scheme-set custom "" # re-reads through the symlink; expect ok ``` Do the link by hand for one file instead of running `deploy.sh` — sync mode quarantines and replaces every divergent live file, which is broader than a single-palette change. ## Traps - **NEVER `kill -USR1` ghostty** to reload. It does not reload config on this fleet — it kills/respawns the terminal (took out Frank's window 2026-09-13; he said "stop killing the terminal"). Correct: the default keybind `ctrl+shift+,` = `reload_config`, per `ghostty +show-config --default`. Existing windows keep old colors until reloaded; new windows inherit immediately. - Palette hex values are NOT a proxy for what the user sees: in `Gruber Darker` the *accent* (`mPrimary`) is yellow `#ffdd33`, so "change the yellow" means the accent too, not just terminal index 3. - Terminal text turned into a saturated color makes ANSI 1/9 indistinguishable from default text (grep highlights flatten) — flag it and offer a lighter red. - Contrast sanity: `#f43841` on `#181818` ≈ 4.6:1 (dark-on-color beats white-on-color here, ~3.9:1, so keep `mOnPrimary` dark). - Revert = `noctalia msg color-scheme-set community ""`; the community cache is untouched by this whole flow. - `ghostty +show-config` prints only the delta from Ghostty defaults — a key whose value equals the default (e.g. `font-size = 12`) simply does not appear. Cross-check with `ghostty +show-config --default` before concluding a key is unset. - Generated theme files that are symlinked into the dotfiles repo (`gtk-3.0`/`gtk-4.0` `noctalia.css`, ghostty theme) show up as tracked modifications after `templates-apply`; commit them together with the new palette JSON so every machine gets the same recolor.