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).
41 lines
1.7 KiB
Rust
41 lines
1.7 KiB
Rust
use crate::error::SqliteConfigError;
|
|
use domain::{ThemeColor, ThemeConfig};
|
|
|
|
pub fn theme_to_json(theme: &ThemeConfig) -> Result<String, SqliteConfigError> {
|
|
let v = serde_json::json!({
|
|
"primary": color_to_json(&theme.primary),
|
|
"secondary": color_to_json(&theme.secondary),
|
|
"accent": color_to_json(&theme.accent),
|
|
"text": color_to_json(&theme.text),
|
|
"background": color_to_json(&theme.background),
|
|
});
|
|
serde_json::to_string(&v).map_err(|e| SqliteConfigError::Serialization(e.to_string()))
|
|
}
|
|
|
|
pub fn theme_from_json(json: &str) -> Result<ThemeConfig, SqliteConfigError> {
|
|
let v: serde_json::Value =
|
|
serde_json::from_str(json).map_err(|e| SqliteConfigError::Serialization(e.to_string()))?;
|
|
let err = |msg: &str| SqliteConfigError::Serialization(msg.into());
|
|
|
|
Ok(ThemeConfig {
|
|
primary: color_from_json(&v["primary"]).map_err(|_| err("invalid primary"))?,
|
|
secondary: color_from_json(&v["secondary"]).map_err(|_| err("invalid secondary"))?,
|
|
accent: color_from_json(&v["accent"]).map_err(|_| err("invalid accent"))?,
|
|
text: color_from_json(&v["text"]).map_err(|_| err("invalid text"))?,
|
|
background: color_from_json(&v["background"]).map_err(|_| err("invalid background"))?,
|
|
})
|
|
}
|
|
|
|
fn color_to_json(c: &ThemeColor) -> serde_json::Value {
|
|
serde_json::json!({ "r": c.r, "g": c.g, "b": c.b })
|
|
}
|
|
|
|
fn color_from_json(v: &serde_json::Value) -> Result<ThemeColor, SqliteConfigError> {
|
|
let err = |msg: &str| SqliteConfigError::Serialization(msg.into());
|
|
Ok(ThemeColor {
|
|
r: v["r"].as_u64().ok_or_else(|| err("missing r"))? as u8,
|
|
g: v["g"].as_u64().ok_or_else(|| err("missing g"))? as u8,
|
|
b: v["b"].as_u64().ok_or_else(|| err("missing b"))? as u8,
|
|
})
|
|
}
|