Add a curated theme system for "modern" style widgets, inspired by Tesserae
Build and push server image / test (push) Successful in 44s
Build and push server image / build-and-push (push) Successful in 3m33s
Build and push server image / deploy (push) Failing after 1m27s

Frame.theme (7 presets in app/theme_tokens.py) drives font family,
corner radius, drop shadow, and an accent hue for every modern-style
widget's header/accent region. Rich accent colors (not just the 6 flat
panel inks) are approximated via denser Bayer stippling confined to just
that region (html_render.ordered_dither_regions), so icon/text content
elsewhere stays exactly as crisp as it is today -- verified directly
against real Chromium renders, both in unit tests and via run-server.
"classic" is a byte-identical no-visual-change default: weather's header
keeps its original fixed blue gradient, tasks/calendar keep their flat
THEME_* ink.

Themes are purely stylistic -- battery's charge-level color, calendar/
tasks' per-owner event chips, and text's own per-widget font choice are
never touched.
This commit is contained in:
2026-07-31 10:34:28 +00:00
parent e331f5e5a1
commit 5f4f8f2ea7
32 changed files with 808 additions and 195 deletions
+10
View File
@@ -880,6 +880,15 @@ def _migration_38(conn) -> None:
conn.execute(text("ALTER TABLE calendar_widget_configs ADD COLUMN render_style TEXT NOT NULL DEFAULT 'classic'"))
def _migration_39(conn) -> None:
"""Frame-level curated theme for "modern" style widgets (models.
Frame.theme, see theme_tokens.THEMES) -- same guarded-per-column
shape as every prior migration."""
existing = {c["name"] for c in inspect(conn).get_columns("frames")}
if "theme" not in existing:
conn.execute(text("ALTER TABLE frames ADD COLUMN theme TEXT NOT NULL DEFAULT 'classic'"))
MIGRATIONS = [
(1, _migration_1),
(2, _migration_2),
@@ -919,6 +928,7 @@ MIGRATIONS = [
(36, _migration_36),
(37, _migration_37),
(38, _migration_38),
(39, _migration_39),
]