88 lines
2.6 KiB
Python
88 lines
2.6 KiB
Python
import re
|
|
|
|
import jsonfile
|
|
|
|
THEMES_FILE = "themes.json"
|
|
DESKTOP_FILE = "desktop.json"
|
|
THEME_MODES = ["dark", "light"]
|
|
COLOR_KEYS = [
|
|
"accent",
|
|
"selection",
|
|
"muted",
|
|
"background",
|
|
"dark_background",
|
|
"lighter_background",
|
|
"foreground",
|
|
"red",
|
|
"yellow",
|
|
"orange",
|
|
"green",
|
|
"cyan",
|
|
"blue",
|
|
"magenta",
|
|
]
|
|
HEX_COLOR_PATTERN = re.compile(r"^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$")
|
|
ID_PATTERN = re.compile(r"^[a-z0-9]+(?:-[a-z0-9]+)*$")
|
|
|
|
|
|
def load_themes() -> list[dict]:
|
|
return jsonfile.load(THEMES_FILE)
|
|
|
|
|
|
def save_themes(themes: list[dict]) -> None:
|
|
jsonfile.save(THEMES_FILE, themes)
|
|
|
|
|
|
def find_theme(themes: list[dict], theme_id: str) -> dict | None:
|
|
return next((theme for theme in themes if theme.get("id") == theme_id), None)
|
|
|
|
|
|
def default_theme_id() -> str | None:
|
|
try:
|
|
return jsonfile.load(DESKTOP_FILE).get("defaultTheme")
|
|
except (OSError, ValueError):
|
|
return None
|
|
|
|
|
|
def set_default_theme(theme_id: str) -> None:
|
|
desktop = jsonfile.load(DESKTOP_FILE)
|
|
desktop["defaultTheme"] = theme_id
|
|
jsonfile.save(DESKTOP_FILE, desktop)
|
|
|
|
|
|
def check_theme(theme: dict) -> dict:
|
|
if not ID_PATTERN.match(theme.get("id", "")):
|
|
raise ValueError("Theme id must be lowercase letters, numbers and dashes")
|
|
if theme.get("mode") not in THEME_MODES:
|
|
raise ValueError("Mode must be dark or light")
|
|
for key in COLOR_KEYS:
|
|
if not HEX_COLOR_PATTERN.match(theme.get("colors", {}).get(key, "")):
|
|
raise ValueError(f"{key} must be a hex color like #818cf8")
|
|
for color in theme.get("activeBorder") or []:
|
|
if not HEX_COLOR_PATTERN.match(color):
|
|
raise ValueError("Active border colors must be hex colors")
|
|
return theme
|
|
|
|
|
|
def save_theme(original_id: str | None, theme: dict) -> None:
|
|
check_theme(theme)
|
|
themes = load_themes()
|
|
duplicate = find_theme(themes, theme["id"])
|
|
if duplicate and theme["id"] != original_id:
|
|
raise ValueError(f"A theme with the id {theme['id']} already exists")
|
|
if original_id is None:
|
|
themes.append(theme)
|
|
else:
|
|
index = next(index for index, item in enumerate(themes) if item.get("id") == original_id)
|
|
themes[index] = theme
|
|
if original_id != theme["id"] and default_theme_id() == original_id:
|
|
set_default_theme(theme["id"])
|
|
save_themes(themes)
|
|
|
|
|
|
def remove_theme(theme_id: str) -> None:
|
|
if default_theme_id() == theme_id:
|
|
raise ValueError("That is the default theme. Pick another default first")
|
|
themes = [theme for theme in load_themes() if theme.get("id") != theme_id]
|
|
save_themes(themes)
|