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,10 +1,13 @@
use domain::{WidgetConfig, WidgetId};
use crate::SqliteConfigStore;
use crate::error::SqliteConfigError;
use crate::serialization::widget as ser;
use domain::{WidgetConfig, WidgetId};
impl SqliteConfigStore {
pub(crate) async fn get_widget_impl(&self, id: WidgetId) -> Result<Option<WidgetConfig>, SqliteConfigError> {
pub(crate) async fn get_widget_impl(
&self,
id: WidgetId,
) -> Result<Option<WidgetConfig>, SqliteConfigError> {
let row = sqlx::query("SELECT * FROM widgets WHERE id = ?")
.bind(id as i64)
.fetch_optional(&self.pool)
@@ -23,10 +26,13 @@ impl SqliteConfigStore {
.await
.map_err(SqliteConfigError::Sql)?;
rows.iter().map(|r| ser::widget_from_row(r)).collect()
rows.iter().map(ser::widget_from_row).collect()
}
pub(crate) async fn save_widget_impl(&self, config: &WidgetConfig) -> Result<(), SqliteConfigError> {
pub(crate) async fn save_widget_impl(
&self,
config: &WidgetConfig,
) -> Result<(), SqliteConfigError> {
let mappings_json = ser::mappings_to_json(&config.mappings)?;
let hint_str = ser::display_hint_to_str(&config.display_hint);