Palette-driven theming engine for the equisdots
desktop. It reads the active palette from settings.json and syncs a set of
applications with it — one module per application, and a failing target never
aborts the rest.
- Palettes are plain JSON files: base16 (
color0..color15) +background/foreground+ optionalrolesoverrides. The data and its schema live in equisdots/palettes. - The active palette slug comes from
settings.json→dock.palette(fallbackx). - Each target under
themesync/targets/regenerates its application's config: per-palette artifacts (kitty, starship, xtop, nvim, opencode, Qt, xfetch) or active-only config (VS Code, browsers, rofi, cava, GTK).
./theme-sync.sh --list # targets + availability
./theme-sync.sh --dry-run # show what would change, write nothing
./theme-sync.sh --targets kitty,xfetch # only these apps
./theme-sync.sh --palettes DIR --settings FILEDefaults target the equisdots desktop layout:
- palettes:
~/.config/hypr/scripts/quickshell/dock/palettes - settings:
~/.config/hypr/settings.json
| Target | What it writes |
|---|---|
kitty |
~/.config/kitty/themes/<slug>.conf + include and active border in kitty.conf |
starship |
per-palette themes + fixed ~/.config/starship.toml + STARSHIP_CONFIG in .zshrc/.bashrc |
xtop |
~/.config/xtop/themes/<slug>.jsonc + active theme (xtop --ct) |
vscode |
workbench.colorTheme / workbench.iconTheme for palettes bundled in the xscriptor-themes extension; any other palette (community/, user-created) gets an intelligent live tint via generated colorCustomizations + token/semantic colors, in Code / Code - Insiders. Creates User/settings.json when the editor is installed but the file is missing (fresh profile). |
nvim |
lua/themes/palettes.lua + active-theme bootstrap |
browsers |
Brave/Beta prefs (color_scheme2, accent) + Firefox user.js |
opencode |
~/.config/opencode/themes/<slug>.json + active theme in tui.json |
rofi |
colors.rasi + config.rasi (dmenu included) |
cava |
managed [color] block with a palette gradient |
qt |
qt6ct/qt5ct color schemes + active config |
gtk |
gtk-3.0/gtk-4.0 gtk.css overrides + system color-scheme |
xfetch |
~/.config/xfetch/themes/<slug>.jsonc (hex) + active theme |
Minimal example (see the palettes schema for the full contract):
{
"name": "X",
"slug": "x",
"author": "xscriptor",
"base16": { "color0": "#0a0a0a", "color1": "#fc618d", "color7": "#f7f1ff", "color15": "#f7f1ff" },
"background": "#0a0a0a",
"foreground": "#f7f1ff",
"roles": { "workspaceActive": "#eab308" }
}x is special: it is the fallback palette (used when the active palette file is
missing), so it must always exist. index.json in the
palettes repo is the ordered list the
desktop panel shows.
Create themesync/targets/<app>.py:
NAME = "app"
DESCRIPTION = "one line for --list"
def available(env) -> bool:
...
def apply(env) -> list:
...and add it to the registry in themesync/targets/__init__.py.
There is no CI. The local gate is:
scripts/check.shIt runs:
python3 -m compileall -q themesync.python3 -m unittest discover -s tests -t .(104 tests: colour maths, palette loading and selection, atomic writes, block stripping, binary lookup,Envhelpers, and the runner's availability, selection, failure isolation and listing contracts).- the CLI smoke test (
--listand--dry-run). - the VS Code mapping over every palette, including the fresh-profile path.
The unit suite can also be run directly:
python3 -m unittest discover -s tests -t . -vPalette validation lives with the data:
equisdots/palettes →
python3 tools/validate_palettes.py.
MIT — see LICENSE.