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

StageOutputTypical failure
InputImage, seed colour, modeMissing image or mismatched mode
Paletteprimary, surface, on_surfaceWrong scheme or role
TemplateApplication-specific fileSyntax or path error
RefreshRunning application reads changesCorrect 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:

matugen -c ./config.toml color hex '#6750a4'

Inspect the output:

cat generated.theme

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:

@define-color main-bg {{colors.surface.default.hex}};
@define-color main-fg {{colors.on_surface.default.hex}};
@define-color accent {{colors.primary.default.hex}};

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.

ApplicationVerify
GhosttyTheme file, config validation, reload action
WaybarIndependent config, then a specific instance/service reload
GTK appsValid CSS; some need a new window or restart
ZellijKDL and selected theme; compare old/new sessions
Desktop shellThat 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

  1. Confirm the actual image, mode and config file.
  2. Reproduce generation in an isolated directory and inspect status/output.
  3. Compare live path and modification time with the application's input path.
  4. Run its supported reload operation separately and read logs.
  5. 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.