Private
Public Access
Add custom theme colors, full per-token control (#30)
Adds a 5th "Custom" option to the theme swatch grid alongside the existing 4 presets, opening a picker for all ~12 CSS custom properties (backgrounds, borders, text, three accent tiers, highlight, danger) plus a light/dark toggle for native control rendering. Persisted as a new users.custom_theme_colors JSONB column, validated server-side against exactly what a native <input type="color"> can ever produce. Colors survive switching to a preset and back, since there's no reason picking a preset for a moment should force redoing every color pick. Applied at runtime as inline custom properties on :root (presets stay static CSS) via a shared applyTheme() helper, which is also responsible for clearing those inline overrides when switching away -- otherwise they'd silently keep winning over whatever preset's own stylesheet values should apply next. Live preview on every color change; persists only on explicit "Save colors" (not per keystroke, since a color input fires continuously while dragging), and closing the modal without saving reverts the preview back to whatever's actually persisted. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
@@ -2,6 +2,7 @@ import uuid
|
||||
from datetime import datetime
|
||||
|
||||
from sqlalchemy import Boolean, DateTime, String, func
|
||||
from sqlalchemy.dialects.postgresql import JSONB
|
||||
from sqlalchemy.orm import Mapped, mapped_column
|
||||
|
||||
from app.models.base import Base
|
||||
@@ -19,6 +20,10 @@ class User(Base):
|
||||
is_active: Mapped[bool] = mapped_column(Boolean, default=True, nullable=False)
|
||||
display_name: Mapped[str | None] = mapped_column(String(50))
|
||||
theme: Mapped[str | None] = mapped_column(String(20))
|
||||
# Only meaningful when theme == "custom" -- kept even if the user
|
||||
# switches to a preset and back, so switching away from custom is never
|
||||
# destructive. Shape is CustomThemeColors (backend/app/schemas/user.py).
|
||||
custom_theme_colors: Mapped[dict | None] = mapped_column(JSONB)
|
||||
avatar_filename: Mapped[str | None] = mapped_column(String(64))
|
||||
avatar_content_type: Mapped[str | None] = mapped_column(String(50))
|
||||
# Manual override for the presence indicator -- when set, this user
|
||||
|
||||
Reference in New Issue
Block a user