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,7 @@
use protocol::{
ServerMessage, ClientMessage, WidgetDescriptor,
WireDisplayHint, WireLayoutNode, WireContainerNode, WireLayoutChild,
WireDirection, WireSizing, WireWidgetState, WireKeyValue, WireValue,
encode, decode_server_message, encode_client, decode_client_message,
ClientMessage, ServerMessage, WidgetDescriptor, WireContainerNode, WireDirection,
WireDisplayHint, WireKeyValue, WireLayoutChild, WireLayoutNode, WireSizing, WireValue,
WireWidgetState, decode_client_message, decode_server_message, encode, encode_client,
};
#[test]
@@ -23,19 +22,23 @@ fn screen_update_round_trips() {
},
],
}),
widgets: vec![
WidgetDescriptor {
id: 1,
display_hint: WireDisplayHint::IconValue,
state: WireWidgetState {
data: vec![
WireKeyValue { key: "temperature".into(), value: WireValue::String("5.4°C".into()) },
WireKeyValue { key: "icon".into(), value: WireValue::String("cloud_rain".into()) },
],
error: None,
},
widgets: vec![WidgetDescriptor {
id: 1,
display_hint: WireDisplayHint::IconValue,
state: WireWidgetState {
data: vec![
WireKeyValue {
key: "temperature".into(),
value: WireValue::String("5.4°C".into()),
},
WireKeyValue {
key: "icon".into(),
value: WireValue::String("cloud_rain".into()),
},
],
error: None,
},
],
}],
};
let frame = encode(&msg).unwrap();
@@ -47,18 +50,17 @@ fn screen_update_round_trips() {
#[test]
fn data_update_round_trips() {
let msg = ServerMessage::DataUpdate {
widgets: vec![
WidgetDescriptor {
id: 3,
display_hint: WireDisplayHint::TextBlock,
state: WireWidgetState {
data: vec![
WireKeyValue { key: "body".into(), value: WireValue::String("Breaking news...".into()) },
],
error: None,
},
widgets: vec![WidgetDescriptor {
id: 3,
display_hint: WireDisplayHint::TextBlock,
state: WireWidgetState {
data: vec![WireKeyValue {
key: "body".into(),
value: WireValue::String("Breaking news...".into()),
}],
error: None,
},
],
}],
};
let frame = encode(&msg).unwrap();