feat: Nomarchy ground-up rewrite on NixOS 26.05
Full replacement of the previous iteration, rebuilt around three ideas:
- Pure evaluation: theme-state.json lives inside the flake and is read
via the nomarchy.stateFile option — no --impure, ever.
- All-Home-Manager theming: `nomarchy-theme-sync apply` writes the JSON
and runs `home-manager switch`; every theme change is one atomic,
rollbackable generation. Wallpaper (swww) is the sole runtime piece.
- Flat, downstream-first layout: modules/{nixos,home} with one
options.nix each, exported as nixosModules/homeModules + overlay +
flake template; system (nixos-rebuild) and desktop (home-manager
switch) rebuild paths are fully split.
Ships 21 themes imported from the previous iteration (palettes,
wallpapers, btop themes, six whole-swap Waybar identities), Stylix for
the GTK/Qt/cursor long tail, a live ISO target with offline theme
switching, and docs/TESTING.md with the QEMU verification workflow.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
96
tools/import-palettes.py
Normal file
96
tools/import-palettes.py
Normal file
@@ -0,0 +1,96 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Convert old-distro palettes (colors.toml) into Nomarchy theme JSON presets.
|
||||
|
||||
Usage: tools/import-palettes.py <old-palettes-dir> <output-themes-dir>
|
||||
|
||||
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:
|
||||
<slug>.json the palette (Nomarchy theme schema)
|
||||
<slug>/backgrounds/ wallpapers, copied verbatim
|
||||
<slug>/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()
|
||||
49
tools/test-live-iso.sh
Executable file
49
tools/test-live-iso.sh
Executable file
@@ -0,0 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
# Build the Nomarchy live ISO and boot it in QEMU for a try-before-install
|
||||
# run. Ported from the previous Nomarchy iteration. See docs/TESTING.md
|
||||
# for what to verify once it's up.
|
||||
|
||||
set -e
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
echo "Building Nomarchy live ISO..."
|
||||
nix build .#nixosConfigurations.nomarchy-live.config.system.build.isoImage
|
||||
|
||||
ISO=$(ls -1 result/iso/*.iso 2>/dev/null | head -n 1)
|
||||
if [ -z "$ISO" ]; then
|
||||
echo "Error: ISO build succeeded but no .iso file found in result/iso/" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Prefer UEFI with OVMF so the ISO boots the same way a modern install
|
||||
# does; fall back to legacy BIOS if OVMF isn't available on this host.
|
||||
OVMF_CANDIDATES=(
|
||||
"/run/current-system/sw/share/OVMF/OVMF_CODE.fd"
|
||||
"/run/current-system/sw/share/qemu/edk2-x86_64-code.fd"
|
||||
"/nix/var/nix/profiles/system/sw/share/OVMF/OVMF_CODE.fd"
|
||||
"/usr/share/OVMF/OVMF_CODE.fd"
|
||||
)
|
||||
BIOS_ARG=()
|
||||
for c in "${OVMF_CANDIDATES[@]}"; do
|
||||
if [ -f "$c" ]; then
|
||||
BIOS_ARG=(-drive "if=pflash,format=raw,readonly=on,file=$c")
|
||||
VARS="${c%_CODE.fd}_VARS.fd"
|
||||
[ -f "$VARS" ] && BIOS_ARG+=(-drive "if=pflash,format=raw,readonly=on,file=$VARS")
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
# KVM if the host supports it; software emulation otherwise (slow).
|
||||
ACCEL_ARG=()
|
||||
[ -r /dev/kvm ] && ACCEL_ARG=(-enable-kvm -cpu host)
|
||||
|
||||
echo "Launching ISO: $ISO"
|
||||
# virtio-vga-gl + gl=on gives the guest real OpenGL — Hyprland and
|
||||
# Ghostty need it; without GL the session may fail to start.
|
||||
exec qemu-system-x86_64 \
|
||||
"${ACCEL_ARG[@]}" \
|
||||
-m 4096 -smp 2 \
|
||||
"${BIOS_ARG[@]}" \
|
||||
-device virtio-vga-gl -display gtk,gl=on \
|
||||
-device virtio-net-pci,netdev=n0 -netdev user,id=n0 \
|
||||
-cdrom "$ISO" -boot d
|
||||
Reference in New Issue
Block a user