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,12 +1,7 @@
use tokio::sync::broadcast;
use domain::{
BroadcastPort, Layout, WidgetId, WidgetState,
};
use protocol::{
ServerMessage, WidgetDescriptor, WireDisplayHint, WireLayoutNode,
encode,
};
use crate::error::TcpServerError;
use domain::{BroadcastPort, Layout, WidgetId, WidgetState};
use protocol::{ServerMessage, WidgetDescriptor, WireDisplayHint, WireLayoutNode, encode};
use tokio::sync::broadcast;
pub struct TcpBroadcaster {
tx: broadcast::Sender<Vec<u8>>,
@@ -37,13 +32,14 @@ impl BroadcastPort for TcpBroadcaster {
widgets: &[(WidgetId, WidgetState)],
) -> Result<(), Self::Error> {
let wire_layout: WireLayoutNode = (&layout.root).into();
let wire_widgets: Vec<WidgetDescriptor> = widgets.iter().map(|(id, state)| {
WidgetDescriptor {
let wire_widgets: Vec<WidgetDescriptor> = widgets
.iter()
.map(|(id, state)| WidgetDescriptor {
id: *id,
display_hint: WireDisplayHint::IconValue,
state: state.into(),
}
}).collect();
})
.collect();
let msg = ServerMessage::ScreenUpdate {
layout: wire_layout,
@@ -58,13 +54,14 @@ impl BroadcastPort for TcpBroadcaster {
&self,
updates: &[(WidgetId, WidgetState)],
) -> Result<(), Self::Error> {
let wire_widgets: Vec<WidgetDescriptor> = updates.iter().map(|(id, state)| {
WidgetDescriptor {
let wire_widgets: Vec<WidgetDescriptor> = updates
.iter()
.map(|(id, state)| WidgetDescriptor {
id: *id,
display_hint: WireDisplayHint::IconValue,
state: state.into(),
}
}).collect();
})
.collect();
let msg = ServerMessage::DataUpdate {
widgets: wire_widgets,