Matugen from palette generation to application refresh
On this page
Matugen derives a palette from an image or colour and renders templates. Whether an open application changes immediately depends on its own configuration loading. Diagnose generation and refresh separately.
Examples were checked with Matugen 4.1.0. Use an independent directory before connecting production theme paths.
Four stages
| Stage | Output | Typical failure |
|---|---|---|
| Input | Image, seed colour, mode | Missing image or mismatched mode |
| Palette | primary, surface, on_surface | Wrong scheme or role |
| Template | Application-specific file | Syntax or path error |
| Refresh | Running application reads changes | Correct file, stale process state |
surface and on_surface pair a background with its foreground; primary provides emphasis. Check selected, disabled and warning states instead of choosing colours only for their isolated appearance.
Generate in an isolated directory
Create and enter a new directory such as matugen-lab. Save ghostty.template:
background = {{colors.surface.default.hex}}
foreground = {{colors.on_surface.default.hex}}
cursor-color = {{colors.primary.default.hex}}
Save config.toml alongside it:
[config]
[templates.terminal]
input_path = "ghostty.template"
output_path = "generated.theme"
Run from that directory with explicit configuration:
Inspect the output:
Expect three real colour values and no unexpanded template tokens. In 4.1.0, .hex already includes #; adding another produces ##.... Retest formatting after template-engine upgrades. Matugen
A seed colour gives a reproducible exercise. For an existing image, use matugen -c ./config.toml image ./wallpaper.png; choose --mode light or --mode dark explicitly as needed.
Render each application's syntax
A Waybar GTK CSS template uses the same roles in different syntax:
@}};
@}};
@}};
Ghostty expects key/value settings and Zellij expects KDL. First make static application configuration work, then generate the same structure.
Use explicit template input/output paths, writable directories and application validators where available. Inspect temporary output before publishing it to a live theme path.
Hooks and refresh
Start without post_hook; add application refresh only after generation works. A hook does not replace output validation.
| Application | Verify |
|---|---|
| Ghostty | Theme file, config validation, reload action |
| Waybar | Independent config, then a specific instance/service reload |
| GTK apps | Valid CSS; some need a new window or restart |
| Zellij | KDL and selected theme; compare old/new sessions |
| Desktop shell | That shell's public theme interface |
Do not generalise SIGUSR2 support across programs. Historical OSC broadcasting is custom code with instance-selection and forwarding boundaries. Prefer documented application mechanisms.
The current Caelestia connection
The local shell selects wallpaper, mode and scheme, then calls caelestia-sync-app-theme. The script maps scheme names to Matugen, reads wallpaper state and uses a file lock against concurrent generation.
Application templates cover Ghostty, Zellij, GTK, KDE, Fuzzel and Fcitx5. Caelestia maintains its own theme state rather than reading the earlier ii colours file. Shared input does not guarantee pixel-identical colours when role mappings or generation options differ.
Locate a partial update
- Confirm the actual image, mode and config file.
- Reproduce generation in an isolated directory and inspect status/output.
- Compare live path and modification time with the application's input path.
- Run its supported reload operation separately and read logs.
- If only new windows work, investigate runtime refresh; if neither works, inspect template and theme selection.
Restore saved theme/configuration files before reloading. Multi-application generation is not automatically a global transaction; partial updates require individual checks.