Files
oai-web/server/web/themes.py
2026-04-08 12:43:24 +02:00

152 lines
4.9 KiB
Python

"""
web/themes.py — CSS variable themes for oAI-Web.
Each theme is a set of CSS variable overrides injected into base.html as a
<style> block, overriding the :root defaults in style.css.
Non-colour tokens (--radius, --font, --mono) are not overridden so they
inherit from style.css.
"""
from __future__ import annotations
DEFAULT_THEME = "dark"
# Each entry: id → {label, preview (bg, accent), vars}
THEMES: dict[str, dict] = {
"dark": {
"label": "Dark",
"preview": {"bg": "#1a1d27", "accent": "#6c8ef5"},
"vars": {
"--bg": "#0f1117",
"--bg2": "#1a1d27",
"--bg3": "#22263a",
"--border": "#2e3249",
"--text": "#e2e4ef",
"--text-dim": "#7b82a8",
"--accent": "#6c8ef5",
"--accent-dim": "#3d5099",
"--green": "#4caf7d",
"--red": "#e05252",
"--yellow": "#e0a632",
},
},
"darker": {
"label": "Darker",
"preview": {"bg": "#111111", "accent": "#6c8ef5"},
"vars": {
"--bg": "#000000",
"--bg2": "#0d0d0d",
"--bg3": "#1a1a1a",
"--border": "#2a2a2a",
"--text": "#e2e4ef",
"--text-dim": "#6b7280",
"--accent": "#6c8ef5",
"--accent-dim": "#3d5099",
"--green": "#4caf7d",
"--red": "#e05252",
"--yellow": "#e0a632",
},
},
"light": {
"label": "Light",
"preview": {"bg": "#ffffff", "accent": "#4b6ef5"},
"vars": {
"--bg": "#f5f6fa",
"--bg2": "#ffffff",
"--bg3": "#eef0f7",
"--border": "#d1d5e8",
"--text": "#1a1d2e",
"--text-dim": "#6b7280",
"--accent": "#4b6ef5",
"--accent-dim": "#a3b4fa",
"--green": "#2e8a57",
"--red": "#c0392b",
"--yellow": "#b7791f",
},
},
"nord": {
"label": "Nord",
"preview": {"bg": "#3b4252", "accent": "#88c0d0"},
"vars": {
"--bg": "#2e3440",
"--bg2": "#3b4252",
"--bg3": "#434c5e",
"--border": "#4c566a",
"--text": "#eceff4",
"--text-dim": "#8a97b0",
"--accent": "#88c0d0",
"--accent-dim": "#4a7a8a",
"--green": "#a3be8c",
"--red": "#bf616a",
"--yellow": "#ebcb8b",
},
},
"solarized": {
"label": "Solarized Dark",
"preview": {"bg": "#073642", "accent": "#268bd2"},
"vars": {
"--bg": "#002b36",
"--bg2": "#073642",
"--bg3": "#0d4454",
"--border": "#1a5566",
"--text": "#839496",
"--text-dim": "#586e75",
"--accent": "#268bd2",
"--accent-dim": "#1a5c8c",
"--green": "#859900",
"--red": "#dc322f",
"--yellow": "#b58900",
},
},
"gruvbox": {
"label": "Gruvbox",
"preview": {"bg": "#3c3836", "accent": "#d79921"},
"vars": {
"--bg": "#282828",
"--bg2": "#3c3836",
"--bg3": "#504945",
"--border": "#665c54",
"--text": "#ebdbb2",
"--text-dim": "#928374",
"--accent": "#458588",
"--accent-dim": "#2d5b5e",
"--green": "#98971a",
"--red": "#cc241d",
"--yellow": "#d79921",
},
},
"catppuccin": {
"label": "Catppuccin",
"preview": {"bg": "#1e1e2e", "accent": "#cba6f7"},
"vars": {
"--bg": "#1e1e2e",
"--bg2": "#181825",
"--bg3": "#313244",
"--border": "#45475a",
"--text": "#cdd6f4",
"--text-dim": "#6c7086",
"--accent": "#cba6f7",
"--accent-dim": "#6e4d9e",
"--green": "#a6e3a1",
"--red": "#f38ba8",
"--yellow": "#f9e2af",
},
},
}
def get_theme_css(theme_id: str) -> str:
"""Return a CSS :root override block for the given theme, or empty string for the default."""
if theme_id == DEFAULT_THEME or theme_id not in THEMES:
return ""
vars = THEMES[theme_id]["vars"]
lines = "\n".join(f" {k}: {v};" for k, v in vars.items())
return f":root {{\n{lines}\n}}"
def theme_list() -> list[dict]:
"""Return theme metadata for the UI picker (no CSS vars)."""
return [
{"id": tid, "label": t["label"], "preview": t["preview"]}
for tid, t in THEMES.items()
]