diff --git a/admin.py b/admin.py index ecfb45f..6278852 100644 --- a/admin.py +++ b/admin.py @@ -337,6 +337,58 @@ def api_layout_pack(): return jsonify({"ok": True, "items": cfg["layout"]["items"]}) +DISPLAY_THEMES = { + "default": { + "label": "Classic White", + "bg": "#ffffff", "fg": "#000000", + "accent": "#ff8800", + }, + "dark": { + "label": "Dark Mode", + "bg": "#111111", "fg": "#f0f0f0", + "accent": "#6d8eff", + }, + "sepia": { + "label": "Sepia", + "bg": "#f4ecd8", "fg": "#5b4636", + "accent": "#c0392b", + }, + "nord": { + "label": "Nord", + "bg": "#eceff4", "fg": "#2e3440", + "accent": "#88c0d0", + }, + "terminal": { + "label": "Terminal (Green)", + "bg": "#0d0d00", "fg": "#e8c840", + "accent": "#ffcc00", + }, +} + + +@app.route("/api/display_theme", methods=["GET", "POST"]) +def api_display_theme(): + """Theme für das Display. GET = aktuelles Theme, POST = setzen.""" + a = require_auth() + if a: return a + cfg = dashboard_mod.load_config() + if request.method == "GET": + theme_key = cfg.get("display_theme", "default") + theme = DISPLAY_THEMES.get(theme_key, DISPLAY_THEMES["default"]) + return jsonify({ + "current": theme_key, + "themes": {k: v["label"] for k, v in DISPLAY_THEMES.items()}, + "theme": theme, + }) + data = request.get_json(silent=True) or {} + theme_key = data.get("theme", "default") + if theme_key not in DISPLAY_THEMES: + return jsonify({"ok": False, "error": "unknown theme"}), 400 + cfg["display_theme"] = theme_key + dashboard_mod.save_config(cfg) + return jsonify({"ok": True, "current": theme_key, "theme": DISPLAY_THEMES[theme_key]}) + + @app.route("/api/plugin_config/", methods=["GET", "POST"]) def api_plugin_config(plugin_name): """Plugin-spezifische Config (separat vom Layout).""" diff --git a/dashboard.py b/dashboard.py index a901fe1..cf5bb3b 100644 --- a/dashboard.py +++ b/dashboard.py @@ -260,6 +260,46 @@ class Dashboard: self.items = items self.widgets = widgets self.config_mtime = CONFIG_PATH.stat().st_mtime if CONFIG_PATH.exists() else 0 + self.display_theme = cfg.get("display_theme", "default") + + def apply_display_theme(self, img): + """Überschreibe BG/FG des Bildes je nach display_theme.""" + theme = self.display_theme if hasattr(self, 'display_theme') else "default" + if theme == "default": + return + # Map theme → palette overrides + # For now only BG/FG swap makes sense for ACeP + # dark/terminal invert, sepia/nord just shift + themed_palette = { + "dark": {"bg": (17, 17, 17), "fg": (240, 240, 240)}, + "sepia": {"bg": (244, 236, 216), "fg": (91, 70, 54)}, + "nord": {"bg": (236, 239, 244), "fg": (46, 52, 64)}, + "terminal": {"bg": (13, 13, 0), "fg": (232, 200, 64)}, + } + if theme not in themed_palette: + return + overrides = themed_palette[theme] + # Flood-fill BG areas with theme BG (simple but effective for flat designs) + # We do an Image.paste to recolor the BG — for now just swap BG pixel + bg_r, bg_g, bg_b = overrides["bg"] + fg_color = overrides["fg"] + # Replace WHITE pixels with theme BG, replace BLACK pixels with theme FG + data = img.convert("RGB") + w, h = img.size + # Direct pixel access — only for flat BG designs + for y in range(h): + for x in range(w): + r, g, b = data.getpixel((x, y)) + if r > 240 and g > 240 and b > 240: + # WHITE → theme bg + data.putpixel((x, y), (bg_r, bg_g, bg_b)) + elif r < 15 and g < 15 and b < 15: + # BLACK → theme fg + data.putpixel((x, y), fg_color) + # Convert back to palette-compatible mode + pal_img = img.copy() + pal_img.paste(data) + return pal_img def maybe_reload_config(self): if not CONFIG_PATH.exists(): @@ -288,6 +328,10 @@ class Dashboard: items = list(self.items) widgets = list(self.widgets) img = render_full(items, widgets, self.fonts) + # Apply display theme (BG/FG color remap) — admin preview also uses this + themed = self.apply_display_theme(img) + if themed is not None: + return themed return img def display(self, img: Image.Image): diff --git a/templates/index.html b/templates/index.html index 3321feb..989fdbd 100644 --- a/templates/index.html +++ b/templates/index.html @@ -6,24 +6,36 @@ epaper-dashboard @@ -494,6 +529,11 @@
+
+ + + +