feat: custom themes polishing

This commit is contained in:
3252a8
2026-05-15 11:17:27 +03:00
parent b6ee5e8790
commit af7cee5014
28 changed files with 1474 additions and 306 deletions
+28
View File
@@ -92,6 +92,7 @@ class WebAppSettings(BaseModel):
title: str
primary_color: str
logo_url: Optional[str]
logo_use_emoji: bool
logo_emoji: str
logo_emoji_font: str
session_ttl_seconds: int
@@ -369,6 +370,7 @@ class Settings(BaseSettings):
),
)
WEBAPP_LOGO_URL: Optional[str] = Field(default=None)
WEBAPP_LOGO_USE_EMOJI: bool = Field(default=False)
WEBAPP_LOGO_EMOJI: str = Field(default="🫥")
WEBAPP_LOGO_EMOJI_FONT: str = Field(
default="system",
@@ -545,6 +547,7 @@ class Settings(BaseSettings):
title=self.WEBAPP_TITLE,
primary_color=self.WEBAPP_PRIMARY_COLOR,
logo_url=self.WEBAPP_LOGO_URL,
logo_use_emoji=self.WEBAPP_LOGO_USE_EMOJI,
logo_emoji=self.WEBAPP_LOGO_EMOJI,
logo_emoji_font=self.WEBAPP_LOGO_EMOJI_FONT,
session_ttl_seconds=self.WEBAPP_SESSION_TTL_SECONDS,
@@ -836,6 +839,31 @@ class Settings(BaseSettings):
theme_dir=self.WEBAPP_THEMES_DIR,
)
@field_validator("WEBAPP_PRIMARY_COLOR", mode="before")
@classmethod
def ignore_deprecated_webapp_primary_color_env(cls, _value):
return "#00fe7a"
@field_validator("WEBAPP_LOGO_URL", mode="before")
@classmethod
def ignore_deprecated_webapp_logo_url_env(cls, _value):
return None
@field_validator("WEBAPP_LOGO_USE_EMOJI", mode="before")
@classmethod
def ignore_deprecated_webapp_logo_use_emoji_env(cls, _value):
return False
@field_validator("WEBAPP_LOGO_EMOJI", mode="before")
@classmethod
def ignore_deprecated_webapp_logo_emoji_env(cls, _value):
return "🫥"
@field_validator("WEBAPP_LOGO_EMOJI_FONT", mode="before")
@classmethod
def ignore_deprecated_webapp_logo_emoji_font_env(cls, _value):
return "system"
@computed_field
@property
def referral_bonus_inviter(self) -> Dict[int, int]:
+24 -5
View File
@@ -8,7 +8,7 @@ import re
from pathlib import Path
from typing import Any, Dict, List, Literal, Optional
from pydantic import BaseModel, Field, model_validator
from pydantic import BaseModel, Field, field_validator, model_validator
logger = logging.getLogger(__name__)
@@ -48,6 +48,22 @@ class ThemeTokens(BaseModel):
admin_muted: Optional[str] = None
admin_dim: Optional[str] = None
@field_validator("accent")
@classmethod
def _normalize_accent_hex(cls, value: Optional[str]) -> Optional[str]:
if value is None:
return None
raw = str(value).strip()
if not raw:
return None
match = re.fullmatch(r"#?([0-9a-fA-F]{3}|[0-9a-fA-F]{6})", raw)
if not match:
raise ValueError("accent must be a hex color (#RGB or #RRGGBB)")
hex_value = match.group(1).lower()
if len(hex_value) == 3:
hex_value = "".join(char * 2 for char in hex_value)
return f"#{hex_value}"
class WebappTheme(BaseModel):
"""Single theme descriptor loaded from WEBAPP_THEMES_DIR/<key>/theme.json."""
@@ -256,10 +272,13 @@ def _theme_sort_key(theme: WebappTheme, index: int) -> tuple[int, int]:
def _sorted_themes(themes: List[WebappTheme]) -> List[WebappTheme]:
return [theme for _, theme in sorted(
((_theme_sort_key(t, i), t) for i, t in enumerate(themes)),
key=lambda pair: pair[0],
)]
return [
theme
for _, theme in sorted(
((_theme_sort_key(t, i), t) for i, t in enumerate(themes)),
key=lambda pair: pair[0],
)
]
def _themes_config_from_list(