theme config, layout preview, container alignment

Server: ThemeConfig entity + CRUD (GET/PUT /theme), SQLite persistence,
ThemeUpdate broadcast to ESP32 on save and initial connect.
Client: render engine uses theme colors, full-screen redraw on theme change.
SPA: theme page with color pickers + presets, layout preview with TS port
of layout engine, justify/align controls on containers.
DisplayHint refactored to struct (kind + h_align + v_align).
This commit is contained in:
2026-06-19 03:26:18 +02:00
parent 81a4167382
commit fe59b68c37
46 changed files with 1276 additions and 118 deletions

View File

@@ -1,6 +1,6 @@
use domain::{
ConfigRepository, DataSource, DataSourceId, Layout, LayoutPreset, LayoutPresetId, User,
WidgetConfig, WidgetId,
ConfigRepository, DataSource, DataSourceId, Layout, LayoutPreset, LayoutPresetId, ThemeConfig,
User, WidgetConfig, WidgetId,
};
use std::collections::HashMap;
use std::sync::RwLock;
@@ -15,6 +15,7 @@ pub struct MemoryConfigStore {
widgets: RwLock<HashMap<WidgetId, WidgetConfig>>,
data_sources: RwLock<HashMap<DataSourceId, DataSource>>,
layout: RwLock<Option<Layout>>,
theme: RwLock<Option<ThemeConfig>>,
presets: RwLock<HashMap<LayoutPresetId, LayoutPreset>>,
users: RwLock<Vec<User>>,
}
@@ -25,6 +26,7 @@ impl Default for MemoryConfigStore {
widgets: RwLock::new(HashMap::new()),
data_sources: RwLock::new(HashMap::new()),
layout: RwLock::new(None),
theme: RwLock::new(None),
presets: RwLock::new(HashMap::new()),
users: RwLock::new(Vec::new()),
}
@@ -125,6 +127,23 @@ impl ConfigRepository for MemoryConfigStore {
Ok(())
}
async fn get_theme(&self) -> Result<Option<ThemeConfig>, Self::Error> {
let guard = self
.theme
.read()
.map_err(|_| MemoryConfigError::LockPoisoned)?;
Ok(guard.clone())
}
async fn save_theme(&self, theme: &ThemeConfig) -> Result<(), Self::Error> {
let mut guard = self
.theme
.write()
.map_err(|_| MemoryConfigError::LockPoisoned)?;
*guard = Some(theme.clone());
Ok(())
}
async fn get_preset(&self, id: LayoutPresetId) -> Result<Option<LayoutPreset>, Self::Error> {
let guard = self
.presets