#!/usr/bin/env python3 """Convert old-distro palettes (colors.toml) into Nomarchy theme JSON presets. Usage: tools/import-palettes.py Old format (per theme directory): colors.toml kitty-style keys: background, foreground, accent, selection_background, color0..color15 light.mode marker file: theme is light icons.theme icon theme name (informational, ignored for now) backgrounds/ wallpapers apps/ hand-made per-app theme files (btop.theme, ...) Output, per theme: .json the palette (Nomarchy theme schema) /backgrounds/ wallpapers, copied verbatim /btop.theme copied from apps/ when present """ import json import shutil import sys import tomllib from pathlib import Path def darken(hex_color: str, factor: float = 0.85) -> str: """Scale a #rrggbb color toward black (used to derive 'mantle').""" h = hex_color.lstrip("#") r, g, b = (int(h[i:i + 2], 16) for i in (0, 2, 4)) return "#{:02x}{:02x}{:02x}".format(*(round(v * factor) for v in (r, g, b))) def convert(theme_dir: Path) -> dict: toml = tomllib.loads((theme_dir / "colors.toml").read_text()) slug = theme_dir.name ansi = [toml[f"color{i}"] for i in range(16)] return { "version": 1, "name": slug.replace("-", " ").title(), "slug": slug, "mode": "light" if (theme_dir / "light.mode").exists() else "dark", # Empty = auto: nomarchy-theme-sync falls back to the first file in # the theme's backgrounds/ directory. "wallpaper": "", "colors": { "base": toml["background"], "mantle": darken(toml["background"]), "surface": toml["color0"], "overlay": toml["color8"], "text": toml["foreground"], "subtext": toml["color7"], "muted": toml["color8"], "accent": toml["accent"], "accentAlt": toml["color5"], "good": toml["color2"], "warn": toml["color3"], "bad": toml["color1"], }, "ansi": ansi, } def main() -> None: if len(sys.argv) != 3: sys.exit(__doc__) src, out = Path(sys.argv[1]), Path(sys.argv[2]) out.mkdir(parents=True, exist_ok=True) converted = 0 for theme_dir in sorted(src.iterdir()): if not (theme_dir / "colors.toml").is_file(): continue theme = convert(theme_dir) slug = theme["slug"] (out / f"{slug}.json").write_text(json.dumps(theme, indent=2) + "\n") # Assets: wallpapers and hand-made per-app theme files. extras = [] if (theme_dir / "backgrounds").is_dir(): shutil.copytree(theme_dir / "backgrounds", out / slug / "backgrounds", dirs_exist_ok=True) extras.append(f"{len(list((theme_dir / 'backgrounds').iterdir()))} wallpapers") if (theme_dir / "apps" / "btop.theme").is_file(): (out / slug).mkdir(parents=True, exist_ok=True) shutil.copyfile(theme_dir / "apps" / "btop.theme", out / slug / "btop.theme") extras.append("btop") print(f" {slug:<20} ({theme['mode']}{', ' + ', '.join(extras) if extras else ''})") converted += 1 print(f"converted {converted} palettes -> {out}") if __name__ == "__main__": main()