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,5 +1,5 @@
use crate::entities::{DataSourceId, LayoutPresetId, WidgetId};
use crate::value_objects::Layout;
use crate::value_objects::{Layout, ThemeConfig};
#[derive(Debug, Clone)]
pub enum DomainEvent {
@@ -10,6 +10,7 @@ pub enum DomainEvent {
DataSourceUpdated { id: DataSourceId },
DataSourceRemoved { id: DataSourceId },
LayoutChanged { layout: Layout },
ThemeChanged { theme: ThemeConfig },
LayoutPresetSaved { id: LayoutPresetId },
LayoutPresetLoaded { id: LayoutPresetId },
LayoutPresetDeleted { id: LayoutPresetId },

View File

@@ -16,6 +16,6 @@ pub use ports::{
};
pub use value_objects::{
AlignItems, ContainerNode, Direction, DisplayHint, DisplayHintKind, HAlign, JustifyContent,
KeyMapping, Layout, LayoutChild, LayoutNode, LayoutValidationError, Sizing, VAlign, Value,
WidgetError, WidgetState,
KeyMapping, Layout, LayoutChild, LayoutNode, LayoutValidationError, Sizing, ThemeColor,
ThemeConfig, VAlign, Value, WidgetError, WidgetState,
};

View File

@@ -1,5 +1,5 @@
use crate::entities::WidgetId;
use crate::value_objects::{DisplayHint, Layout, WidgetState};
use crate::value_objects::{DisplayHint, Layout, ThemeConfig, WidgetState};
use std::future::Future;
pub trait BroadcastPort {
@@ -15,4 +15,9 @@ pub trait BroadcastPort {
&self,
updates: &[(WidgetId, DisplayHint, WidgetState)],
) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn push_theme_update(
&self,
theme: &ThemeConfig,
) -> impl Future<Output = Result<(), Self::Error>> + Send;
}

View File

@@ -1,7 +1,7 @@
use crate::entities::{
DataSource, DataSourceId, LayoutPreset, LayoutPresetId, User, WidgetConfig, WidgetId,
};
use crate::value_objects::Layout;
use crate::value_objects::{Layout, ThemeConfig};
use std::future::Future;
pub trait ConfigRepository {
@@ -51,6 +51,12 @@ pub trait ConfigRepository {
id: LayoutPresetId,
) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn get_theme(&self) -> impl Future<Output = Result<Option<ThemeConfig>, Self::Error>> + Send;
fn save_theme(
&self,
theme: &ThemeConfig,
) -> impl Future<Output = Result<(), Self::Error>> + Send;
fn get_user_by_username(
&self,
username: &str,

View File

@@ -1,5 +1,6 @@
mod key_mapping;
mod layout;
mod theme;
mod value;
mod widget_state;
@@ -8,5 +9,6 @@ pub use layout::{
AlignItems, ContainerNode, Direction, JustifyContent, Layout, LayoutChild, LayoutNode,
LayoutValidationError, Sizing,
};
pub use theme::{ThemeColor, ThemeConfig};
pub use value::Value;
pub use widget_state::{DisplayHint, DisplayHintKind, HAlign, VAlign, WidgetError, WidgetState};

View File

@@ -0,0 +1,47 @@
#[derive(Debug, Clone, PartialEq)]
pub struct ThemeColor {
pub r: u8,
pub g: u8,
pub b: u8,
}
#[derive(Debug, Clone, PartialEq)]
pub struct ThemeConfig {
pub primary: ThemeColor,
pub secondary: ThemeColor,
pub accent: ThemeColor,
pub text: ThemeColor,
pub background: ThemeColor,
}
impl Default for ThemeConfig {
fn default() -> Self {
Self {
primary: ThemeColor {
r: 0x00,
g: 0x7A,
b: 0xCC,
},
secondary: ThemeColor {
r: 0x88,
g: 0x88,
b: 0x88,
},
accent: ThemeColor {
r: 0xE9,
g: 0x45,
b: 0x60,
},
text: ThemeColor {
r: 0xFF,
g: 0xFF,
b: 0xFF,
},
background: ThemeColor {
r: 0x00,
g: 0x00,
b: 0x00,
},
}
}
}