feat(caelestia): pick the scheme in caelestia, recolour the desktop live #4

Merged
hutao merged 17 commits from feat/event-driven-colors into main 2026-09-26 21:24:50 +00:00
Owner

What

The colour scheme is now picked in caelestia's own picker, and the desktop follows the pick live. hu-tao/red is a caelestia scheme like any other, and dynamic works too.

  • Live on a switch: kitty, tmux, Neovim, waybar, mako, Hyprland's borders, Vesktop, starship and the Claude Code status line. caelestia renders each app's config from a template into $XDG_STATE_HOME/caelestia/theme/, and the app's path links there. caelestia-theme-hook then tells each running app to reload.
  • At next launch: GTK and Qt apps, through caelestia's own appliers. caelestia now owns them instead of stylix. Qt runs on qtengine with Darkly, in JetBrainsMono Nerd Font Light.
  • At the next rebuild: grub, plymouth, the tty and SDDM. The hook records the pick in dotfiles/caelestia/current.json, and for dynamic the generated colours in dynamic.txt. palette.nix builds from those. The hook finds the checkout through $XDG_STATE_HOME/hutao/flake-path, which is written from hutao.flakePath.

Also in here

  • fcitx5 drops its Qt 5 frontend, the only thing still pulling in Qt 5 (3cdd346, from main, not yet on origin).
  • Fixes found while testing:
    • The activation's shell restart works again.
    • tmux recolours workmux's windows, through cli-utils, with the lock updated.
    • The current tmux window and the prompt's directory are readable on generated schemes.
    • Links left over from the old whole-directory home.file entries are removed before relinking.
  • kitty and Evolution are more opaque, for light schemes.

Testing

  • Built and switched on hutao-desktop.
  • Switched between hu-tao/red, dynamic, light and dark, and hard; checked recolouring by eye.
  • The rendered templates match the previous build's baked files byte for byte.
  • nix develop -c pre-commit run --all-files passes, including the book.

The handbook's colours chapter (docs/src/architecture/colours.md) describes the design.

## What The colour scheme is now picked in caelestia's own picker, and the desktop follows the pick live. `hu-tao/red` is a caelestia scheme like any other, and `dynamic` works too. - **Live on a switch:** kitty, tmux, Neovim, waybar, mako, Hyprland's borders, Vesktop, starship and the Claude Code status line. caelestia renders each app's config from a template into `$XDG_STATE_HOME/caelestia/theme/`, and the app's path links there. `caelestia-theme-hook` then tells each running app to reload. - **At next launch:** GTK and Qt apps, through caelestia's own appliers. caelestia now owns them instead of stylix. Qt runs on qtengine with Darkly, in JetBrainsMono Nerd Font Light. - **At the next rebuild:** grub, plymouth, the tty and SDDM. The hook records the pick in `dotfiles/caelestia/current.json`, and for `dynamic` the generated colours in `dynamic.txt`. `palette.nix` builds from those. The hook finds the checkout through `$XDG_STATE_HOME/hutao/flake-path`, which is written from `hutao.flakePath`. ## Also in here - `fcitx5` drops its Qt 5 frontend, the only thing still pulling in Qt 5 (`3cdd346`, from `main`, not yet on origin). - Fixes found while testing: - The activation's shell restart works again. - tmux recolours workmux's windows, through cli-utils, with the lock updated. - The current tmux window and the prompt's directory are readable on generated schemes. - Links left over from the old whole-directory `home.file` entries are removed before relinking. - kitty and Evolution are more opaque, for light schemes. ## Testing - Built and switched on hutao-desktop. - Switched between `hu-tao/red`, `dynamic`, light and dark, and hard; checked recolouring by eye. - The rendered templates match the previous build's baked files byte for byte. - `nix develop -c pre-commit run --all-files` passes, including the book. The handbook's colours chapter (`docs/src/architecture/colours.md`) describes the design.
chore(fcitx5): drop the Qt 5 frontend
All checks were successful
CI / Evaluate the installer image (push) Successful in 59s
CI / Format and lint (push) Successful in 1m12s
Pages / Build the handbook (push) Successful in 49s
3cdd346ffc
fcitx5-with-addons bundles libsForQt5.fcitx5-qt unconditionally, and it
was what pulled Qt 5 into the system. There are no Qt 5 apps for it to
serve, and Qt 5 is close to end of life. The Qt 6 and GTK frontends stay.

An overlay rather than i18n.inputMethod.package, because the module also
puts its own copy of the package on QT_PLUGIN_PATH.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The scheme moves to hu-tao/red/dark.txt and ships inside an overridden
caelestia CLI, the only place the CLI lists schemes from, so it finally
shows up in the picker. The shell drops its bundled CLI for the one on
PATH, or every scheme edit would be a C++ rebuild.

caelestia-theme-hook, as theme.postHook, records each pick in
dotfiles/caelestia/current.json, and dynamic's generated colours in
dynamic.txt, in the checkout named by hutao.flakePath. palette.nix builds
from that, so a rebuild brings grub, plymouth, the tty and SDDM to the
scheme on screen.

Every dotfile with a colour in it becomes a caelestia template, rendered
on each switch and at activation, with its path linked to the rendered
file; the hook reloads kitty, waybar, mako, tmux and Hyprland. caelestia
owns GTK and Qt: stylix's gtk and qt targets are off, Qt is qtengine with
Darkly, and kdeglobals is a template too.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
utils/colors.lua reads $XDG_STATE_HOME/caelestia/scheme.json, falling
back to its literals, and watches it; catppuccin re-applies on a switch,
recompiling off its own options hash. lualine's theme is a function so
its ColorScheme autocmd rebuilds it, and Alpha's groups move into the
catppuccin overrides so the reload does not clear them.

The build-time recolour in neovim.nix goes, and with it the cache purge
that only existed because that recolour kept file sizes and mtimes still.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Rewrites the colours chapter around the picker, the templates and the
hook, and says what follows a switch at once, at next launch and only
after a rebuild. The user-layer chapter and the README follow.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
caelestia's qtengine template hardcodes Sans Serif and Monospace at 12,
and it rewrites qtengine's config.json from that template on every
switch, so the CLI override patches the template itself: stylix's
monospace font at the applications size, weight 300, which qtengine hands
to QFont as QFont::Light.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Making kitty, waybar, mako and the rest templates expands their
directories, which the old generation linked whole. home-manager keeps
such a link, since the new generation has a directory at that path, and
then fails writing entries into the read-only store directory under it.
Activation removes those links first, and only ones into a home-manager
generation.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The shell package no longer bundles the CLI, so the activation's restart
called a caelestia binary that does not exist and the failure was
swallowed: the old shell kept running with the old CLI, whose scheme list
has no hu-tao. The standalone CLI runs it instead, with the shell package
on PATH, where the CLI looks for caelestia-shell.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Vencord re-reads its themes on any event in their directory, and a switch
rewrites the file behind the symlinks, outside it, so it only caught up
when the theme was toggled or Vesktop restarted. The hook now touches the
links themselves, which is the event it waits for.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
On a light scheme the wallpaper showing through washes their text out:
kitty goes from 0.93 to 0.98, Evolution from 0.85 to 0.95.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
ccstatusline takes custom gradient stops and re-reads its settings on
every render, so its settings are now a caelestia template running from
primary to tertiary, in place of the fixed morning preset. The rendered
file is writable, so the picker can save over it again, but only until
the next switch.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix(home): draw the prompt's directory in onPrimary
All checks were successful
CI / Evaluate the installer image (pull_request) Successful in 59s
CI / Format and lint (pull_request) Successful in 1m9s
061312e489
It sat in the scheme's text colour on primary, and a generated scheme
makes those two the same lightness in either mode, so the path washed out
on every dynamic pick. onPrimary is the colour the scheme makes for use on
primary.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The CaelestiaFox extension recolours the browser from what its native app
sends, scheme.json on start and on every switch, and does nothing without
it. Upstream ships that app only in an Arch package, so it is packaged
here from their repo, and Floorp's wrapper registers it through
nativeMessagingHosts, linking the manifest into
~/.mozilla/native-messaging-hosts when Floorp starts.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The restart ran `caelestia shell -d` from the activation service, whose
environment has QT_QPA_PLATFORM=offscreen, so the new shell had no display,
failed to load its services and exited, leaving no shell until it was
started by hand. Hyprland starts it now, over `hyprctl eval`, with the
session's environment and the same command as autostart, which moves into
programs.lua as programs.shell so the two cannot drift. dofile rather than
require, so the eval reads the new file rather than a cached module.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
The shell stopped reading perMonitorWorkspaces, useFahrenheit,
useFahrenheitPerformance and useTwelveHourClock and warned on every
start. They are perMonitor, weatherUnits, sensorUnits and clockFormat now,
set to what the old ones meant: Celsius and a 24-hour clock. The first VPN
provider was an empty entry, all nulls, that failed to decode; Tailscale
is the only real one.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Colours now says the extension comes from AMO by hand and that Floorp needs a
restart to pick up the native app, and lists Floorp and ccstatusline among
what follows a switch. The user layer explains why a rebuild has Hyprland
start the shell again, a gotcha from 2026-09-26. The README's pkgs line names
CaelestiaFox.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
style(caelestia): format the shell restart for nixfmt
All checks were successful
CI / Evaluate the installer image (pull_request) Successful in 57s
CI / Format and lint (pull_request) Successful in 1m11s
78cdb02ef3
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
fix(caelestia): tint the lock screen's distro logo with the scheme
All checks were successful
CI / Evaluate the installer image (pull_request) Successful in 59s
CI / Format and lint (pull_request) Successful in 1m11s
2f63b7b1b6
lock.recolourLogo was off, so the NixOS logo on the lock screen kept its
own blues through every switch. On, the shell draws it in primary, like
the rest of the lock screen.

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
hutao merged commit b3acbe1d14 into main 2026-09-26 21:24:50 +00:00
hutao deleted branch feat/event-driven-colors 2026-09-26 21:24:50 +00:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
hutao/nixos-dotfiles!4
No description provided.