feat: govern effective appearance defaults

This commit is contained in:
2026-08-20 07:03:28 +02:00
parent 6643c8fc1e
commit 8f642bd618
13 changed files with 429 additions and 61 deletions
+109
View File
@@ -0,0 +1,109 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any, Literal, Mapping
AppearancePalette = Literal["default", "civic_blue", "forest", "plum"]
AppearanceSource = Literal["user", "tenant", "system", "tenant_lock", "system_lock"]
APPEARANCE_PALETTES: tuple[AppearancePalette, ...] = ("default", "civic_blue", "forest", "plum")
APPEARANCE_SETTINGS_KEY = "appearance"
@dataclass(frozen=True, slots=True)
class EffectiveAppearance:
palette: AppearancePalette
source: AppearanceSource
locked: bool
system_default_palette: AppearancePalette
tenant_default_palette: AppearancePalette | None
inherited_palette: AppearancePalette
def as_dict(self) -> dict[str, object]:
return {
"palette": self.palette,
"source": self.source,
"locked": self.locked,
"system_default_palette": self.system_default_palette,
"tenant_default_palette": self.tenant_default_palette,
"inherited_palette": self.inherited_palette,
}
def normalize_appearance_palette(value: object, *, fallback: AppearancePalette | None = None) -> AppearancePalette | None:
normalized = str(value or "").strip().lower()
return normalized if normalized in APPEARANCE_PALETTES else fallback # type: ignore[return-value]
def appearance_settings(settings: Mapping[str, Any] | None) -> tuple[AppearancePalette | None, bool]:
raw = settings.get(APPEARANCE_SETTINGS_KEY) if isinstance(settings, Mapping) else None
if not isinstance(raw, Mapping):
return None, False
return normalize_appearance_palette(raw.get("default_palette")), raw.get("palette_locked") is True
def update_appearance_settings(
settings: Mapping[str, Any] | None,
*,
default_palette: AppearancePalette | None,
palette_locked: bool,
) -> dict[str, Any]:
updated = dict(settings or {})
appearance = dict(updated.get(APPEARANCE_SETTINGS_KEY) or {}) if isinstance(updated.get(APPEARANCE_SETTINGS_KEY), Mapping) else {}
if default_palette is None:
appearance.pop("default_palette", None)
else:
normalized = normalize_appearance_palette(default_palette)
if normalized is None:
raise ValueError("Unsupported appearance palette.")
appearance["default_palette"] = normalized
if palette_locked:
appearance["palette_locked"] = True
else:
appearance.pop("palette_locked", None)
if appearance:
updated[APPEARANCE_SETTINGS_KEY] = appearance
else:
updated.pop(APPEARANCE_SETTINGS_KEY, None)
return updated
def resolve_effective_appearance(
*,
system_settings: Mapping[str, Any] | None,
tenant_settings: Mapping[str, Any] | None,
user_settings: Mapping[str, Any] | None,
) -> EffectiveAppearance:
system_palette, system_locked = appearance_settings(system_settings)
system_palette = system_palette or "default"
tenant_palette, tenant_locked = appearance_settings(tenant_settings)
inherited_palette = tenant_palette or system_palette
raw_ui = user_settings.get("ui") if isinstance(user_settings, Mapping) else None
user_palette = normalize_appearance_palette(raw_ui.get("palette")) if isinstance(raw_ui, Mapping) else None
if system_locked:
return EffectiveAppearance(system_palette, "system_lock", True, system_palette, tenant_palette, system_palette)
if tenant_locked:
return EffectiveAppearance(inherited_palette, "tenant_lock", True, system_palette, tenant_palette, inherited_palette)
return EffectiveAppearance(
user_palette or inherited_palette,
"user" if user_palette else "tenant" if tenant_palette else "system",
False,
system_palette,
tenant_palette,
inherited_palette,
)
__all__ = [
"APPEARANCE_PALETTES",
"APPEARANCE_SETTINGS_KEY",
"AppearancePalette",
"AppearanceSource",
"EffectiveAppearance",
"appearance_settings",
"normalize_appearance_palette",
"resolve_effective_appearance",
"update_appearance_settings",
]