add SPA config UI, wire media/rss adapters, event-driven layout push

- React SPA: dashboard, data sources CRUD, widgets CRUD, layout builder,
  presets. TanStack Router + Query, shadcn/ui, Vite proxy to :3000
- wire media + rss adapters into polling loop, remove xtb source type
- media adapter: read username/password from headers, proper subsonic auth
- event handler: subscribe to LayoutChanged, push screen update to clients
- fix clippy warnings across workspace (Default impls, collapsible ifs,
  redundant closures, is_none_or, unused imports)
This commit is contained in:
2026-06-19 00:12:42 +02:00
parent 21c08911df
commit 26ebfad3a2
175 changed files with 12338 additions and 801 deletions

View File

@@ -1,8 +1,6 @@
use std::collections::BTreeMap;
use domain::{
DisplayHint, KeyMapping, Value, WidgetConfig, WidgetId, WidgetState,
};
use application::DataProjection;
use domain::{DisplayHint, KeyMapping, Value, WidgetConfig, WidgetId, WidgetState};
use std::collections::BTreeMap;
fn weather_widget() -> WidgetConfig {
WidgetConfig::new(
@@ -11,8 +9,14 @@ fn weather_widget() -> WidgetConfig {
DisplayHint::IconValue,
10,
vec![
KeyMapping { source_path: "$.temp".into(), target_key: "temperature".into() },
KeyMapping { source_path: "$.icon".into(), target_key: "icon".into() },
KeyMapping {
source_path: "$.temp".into(),
target_key: "temperature".into(),
},
KeyMapping {
source_path: "$.icon".into(),
target_key: "icon".into(),
},
],
)
}
@@ -33,7 +37,10 @@ fn apply_poll_result_detects_new_widget_state() {
assert_eq!(changed.len(), 1);
assert_eq!(changed[0].0, 1);
assert_eq!(changed[0].1.data.get("temperature"), Some(&Value::Number(5.4)));
assert_eq!(
changed[0].1.data.get("temperature"),
Some(&Value::Number(5.4))
);
}
#[test]
@@ -56,7 +63,10 @@ fn apply_poll_result_detects_changed_value() {
let changed = projection.apply_poll_result(10, &weather_response(6.1), &widgets);
assert_eq!(changed.len(), 1);
assert_eq!(changed[0].1.data.get("temperature"), Some(&Value::Number(6.1)));
assert_eq!(
changed[0].1.data.get("temperature"),
Some(&Value::Number(6.1))
);
}
#[test]
@@ -69,9 +79,10 @@ fn apply_poll_result_only_updates_widgets_bound_to_source() {
"portfolio".into(),
DisplayHint::KeyValue,
20,
vec![
KeyMapping { source_path: "$.value".into(), target_key: "amount".into() },
],
vec![KeyMapping {
source_path: "$.value".into(),
target_key: "amount".into(),
}],
),
];