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

@@ -3,8 +3,8 @@ mod support;
use application::ConfigService;
use domain::{
AlignItems, ConfigRepository, ContainerNode, DataSource, DataSourceConfig, DataSourceType,
Direction, DisplayHint, DisplayHintKind, DomainEvent, JustifyContent, KeyMapping, Layout, LayoutChild,
LayoutNode, LayoutPreset, Sizing, WidgetConfig,
Direction, DisplayHint, DisplayHintKind, DomainEvent, JustifyContent, KeyMapping, Layout,
LayoutChild, LayoutNode, LayoutPreset, Sizing, WidgetConfig,
};
use std::time::Duration;
use support::{InMemoryConfigRepository, InMemoryEventPublisher};

View File

@@ -1,6 +1,6 @@
use domain::{
ConfigRepository, DataSource, DataSourceId, DomainEvent, EventPublisher, Layout, LayoutPreset,
LayoutPresetId, User, WidgetConfig, WidgetId,
LayoutPresetId, ThemeConfig, User, WidgetConfig, WidgetId,
};
use std::collections::HashMap;
use std::sync::Mutex;
@@ -9,6 +9,7 @@ pub struct InMemoryConfigRepository {
widgets: Mutex<HashMap<WidgetId, WidgetConfig>>,
data_sources: Mutex<HashMap<DataSourceId, DataSource>>,
layout: Mutex<Option<Layout>>,
theme: Mutex<Option<ThemeConfig>>,
presets: Mutex<HashMap<LayoutPresetId, LayoutPreset>>,
}
@@ -18,6 +19,7 @@ impl InMemoryConfigRepository {
widgets: Mutex::new(HashMap::new()),
data_sources: Mutex::new(HashMap::new()),
layout: Mutex::new(None),
theme: Mutex::new(None),
presets: Mutex::new(HashMap::new()),
}
}
@@ -92,6 +94,15 @@ impl ConfigRepository for InMemoryConfigRepository {
Ok(())
}
async fn get_theme(&self) -> Result<Option<ThemeConfig>, Self::Error> {
Ok(self.theme.lock().unwrap().clone())
}
async fn save_theme(&self, theme: &ThemeConfig) -> Result<(), Self::Error> {
*self.theme.lock().unwrap() = Some(theme.clone());
Ok(())
}
async fn get_preset(&self, id: LayoutPresetId) -> Result<Option<LayoutPreset>, Self::Error> {
Ok(self.presets.lock().unwrap().get(&id).cloned())
}