Add event bus for live UI updates
Introduces a system for broadcasting real-time events to the web UI. This enables features like automatic page reloads when case data changes in the background, improving the user experience by keeping the UI synchronized with the backend. Key components: - `events` module: Contains the `CaseEventKind` enum, `CaseEvent` struct, and `EventSender` type for managing the broadcast channel. - SSE endpoint (`/web/events`): Streams events to connected browsers. - Client-side JavaScript: Listens for events and triggers debounced page reloads. - Integration points: Workers and route handlers now emit events when relevant state changes occur.
This commit is contained in:
@@ -7,6 +7,7 @@ use tracing::{error, info, warn};
|
||||
|
||||
use super::{ANALYSIS_INPUT_FILE, AnalysisInput, AnalyzeReceiver, DOCUMENT_FILE, llm, prompt};
|
||||
use crate::config::Config;
|
||||
use crate::events::{self, CaseEventKind, EventSender};
|
||||
use crate::gazetteer::Gazetteer;
|
||||
use crate::{BusyGuard, WorkerBusy};
|
||||
|
||||
@@ -19,13 +20,14 @@ pub async fn run(
|
||||
client: reqwest::Client,
|
||||
worker_busy: WorkerBusy,
|
||||
vocab: Arc<Gazetteer>,
|
||||
events_tx: EventSender,
|
||||
) {
|
||||
info!(vocab_entries = vocab.len(), "Analyze worker started");
|
||||
let timeout = Duration::from_secs(config.llm_timeout_seconds);
|
||||
|
||||
while let Some(job) = rx.recv().await {
|
||||
let _guard = BusyGuard::new(worker_busy.clone());
|
||||
process(&job.case_dir, &config, &client, &vocab, timeout).await;
|
||||
process(&job.case_dir, &config, &client, &vocab, timeout, &events_tx).await;
|
||||
}
|
||||
|
||||
warn!("Analyze worker stopped (channel closed)");
|
||||
@@ -37,6 +39,7 @@ async fn process(
|
||||
client: &reqwest::Client,
|
||||
vocab: &Gazetteer,
|
||||
timeout: Duration,
|
||||
events_tx: &EventSender,
|
||||
) {
|
||||
let input_path = case_dir.join(ANALYSIS_INPUT_FILE);
|
||||
let document_path = case_dir.join(DOCUMENT_FILE);
|
||||
@@ -72,6 +75,12 @@ async fn process(
|
||||
warn!(path = %input_path.display(), error = %e, "removing analysis input failed");
|
||||
}
|
||||
info!(case = %case_dir.display(), "analysis done (stub, no recordings)");
|
||||
events::emit(
|
||||
events_tx,
|
||||
events::user_slug_of(case_dir),
|
||||
events::case_id_of(case_dir),
|
||||
CaseEventKind::DocumentReady,
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -134,6 +143,13 @@ async fn process(
|
||||
bytes = document.len(),
|
||||
"analysis done"
|
||||
);
|
||||
|
||||
events::emit(
|
||||
events_tx,
|
||||
events::user_slug_of(case_dir),
|
||||
events::case_id_of(case_dir),
|
||||
CaseEventKind::DocumentReady,
|
||||
);
|
||||
}
|
||||
|
||||
/// Write `bytes` to `path` atomically: write to `<path>.tmp`, fsync, rename.
|
||||
|
||||
Reference in New Issue
Block a user