diff options
| author | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-28 15:22:12 +0300 |
|---|---|---|
| committer | Ogulcan Celik <ogulcancelik@gmail.com> | 2026-05-28 15:23:04 +0300 |
| commit | 28222b20890a1f6cd36fd477de8e9bc6e5537ff5 (patch) | |
| tree | 75d54cf3a1d43f22b85adcfc6712b08cf31482a6 /src/server | |
| parent | a90c90aa4aa92142e4684a81dcb7c2cab9aaac6b (diff) | |
fix: use live workspace labels in notifications
refs #332
Diffstat (limited to 'src/server')
| -rw-r--r-- | src/server/headless.rs | 10 | ||||
| -rw-r--r-- | src/server/notifications.rs | 90 |
2 files changed, 99 insertions, 1 deletions
diff --git a/src/server/headless.rs b/src/server/headless.rs index 18717782..e6576df6 100644 --- a/src/server/headless.rs +++ b/src/server/headless.rs @@ -1218,6 +1218,7 @@ impl HeadlessServer { } else { toast_message_from_state_change( &self.app.state, + &self.app.terminal_runtimes, pane_id_val, suppress_active_tab_notifications, prev_state, @@ -1302,6 +1303,7 @@ impl HeadlessServer { } else { toast_message_from_state_change( &self.app.state, + &self.app.terminal_runtimes, pane_id_val, suppress_active_tab_notifications, prev_state, @@ -2115,12 +2117,17 @@ impl HeadlessServer { crate::app::state::ToastKind::Finished => "finished", crate::app::state::ToastKind::UpdateInstalled => "updated", }; + let workspace_label = self.app.state.workspaces[*ws_idx].display_name_from( + &self.app.state.terminals, + &self.app.terminal_runtimes, + ); let msg_text = format!( "{} {}: {}", agent_label, event_text, crate::app::actions::notification_context( &self.app.state.workspaces[*ws_idx], + &workspace_label, *ws_idx, *pane_id, ) @@ -2491,7 +2498,10 @@ impl HeadlessServer { .agent_metadata_deadline .filter(|deadline| now >= *deadline) { + let previous_toast = self.app.state.toast.clone(); for update in self.app.state.expire_agent_metadata_at(deadline, now) { + self.app + .refresh_new_herdr_toast_context_for_update(&update, &previous_toast); self.app.emit_pane_state_update(&update); } self.app.sync_agent_metadata_deadline(); diff --git a/src/server/notifications.rs b/src/server/notifications.rs index 1729c698..0ed681ac 100644 --- a/src/server/notifications.rs +++ b/src/server/notifications.rs @@ -4,6 +4,7 @@ use crate::config; use crate::detect::AgentState; use crate::layout::PaneId; use crate::protocol; +use crate::terminal::TerminalRuntimeRegistry; pub(crate) fn should_forward_toast_to_clients(delivery: config::ToastDelivery) -> bool { toast_notify_kind(delivery).is_some() @@ -19,6 +20,7 @@ pub(crate) fn toast_notify_kind(delivery: config::ToastDelivery) -> Option<proto pub(crate) fn toast_message_from_state_change( state: &AppState, + terminal_runtimes: &TerminalRuntimeRegistry, pane_id: PaneId, suppress_active_tab_notifications: bool, prev_state: AgentState, @@ -41,11 +43,12 @@ pub(crate) fn toast_message_from_state_change( .terminals .get(&pane.attached_terminal_id) .and_then(|terminal| terminal.effective_agent_label())?; + let workspace_label = ws.display_name_from(&state.terminals, terminal_runtimes); Some(format!( "{} {}: {}", agent_label, toast_event_text(kind), - app::actions::notification_context(ws, ws_idx, pane_id) + app::actions::notification_context(ws, &workspace_label, ws_idx, pane_id) )) }) }) @@ -58,3 +61,88 @@ fn toast_event_text(kind: app::state::ToastKind) -> &'static str { app::state::ToastKind::UpdateInstalled => "updated", } } + +#[cfg(test)] +mod tests { + use super::*; + use crate::detect::Agent; + use crate::terminal::TerminalState; + + fn init_repo(path: &std::path::Path) { + let status = std::process::Command::new("git") + .args(["init", "-q"]) + .current_dir(path) + .status() + .unwrap(); + assert!(status.success(), "git init failed for {}", path.display()); + } + + #[tokio::test] + async fn toast_message_uses_live_root_runtime_cwd_label() { + let mut state = AppState::test_new(); + state + .workspaces + .push(crate::workspace::Workspace::test_new("stale")); + state.ensure_test_terminals(); + let root = state.workspaces[0].tabs[0].root_pane; + let terminal_id = state.workspaces[0].terminal_id(root).cloned().unwrap(); + let temp_root = std::env::temp_dir().join(format!( + "herdr-forwarded-toast-context-{}-{}", + std::process::id(), + std::time::SystemTime::now() + .duration_since(std::time::UNIX_EPOCH) + .unwrap() + .as_nanos() + )); + let stale_cwd = temp_root.join("__herdr_original__"); + let live_cwd = temp_root.join("__herdr_projects__"); + std::fs::create_dir_all(&stale_cwd).unwrap(); + std::fs::create_dir_all(&live_cwd).unwrap(); + init_repo(&stale_cwd); + init_repo(&live_cwd); + state.workspaces[0].custom_name = None; + state.workspaces[0].identity_cwd = stale_cwd.clone(); + let mut terminal = TerminalState::new(terminal_id.clone(), stale_cwd); + terminal.set_detected_state(Some(Agent::Codex), AgentState::Idle); + state.terminals.insert(terminal_id.clone(), terminal); + let (events, _) = tokio::sync::mpsc::channel(4); + let runtime = crate::terminal::TerminalRuntime::spawn( + root, + 24, + 80, + live_cwd.clone(), + 0, + crate::terminal_theme::TerminalTheme::default(), + "/bin/sh", + events, + std::sync::Arc::new(tokio::sync::Notify::new()), + std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false)), + ) + .unwrap(); + let deadline = std::time::Instant::now() + std::time::Duration::from_secs(2); + while runtime.cwd() != Some(live_cwd.clone()) && std::time::Instant::now() < deadline { + tokio::time::sleep(std::time::Duration::from_millis(10)).await; + } + let mut terminal_runtimes = TerminalRuntimeRegistry::new(); + terminal_runtimes.insert(terminal_id, runtime); + + let message = toast_message_from_state_change( + &state, + &terminal_runtimes, + root, + false, + AgentState::Working, + AgentState::Idle, + ); + + assert_eq!( + message.as_deref(), + Some("codex finished: __herdr_projects__ ยท 1") + ); + + for (_, runtime) in terminal_runtimes.drain() { + runtime.shutdown(); + } + let _ = std::fs::remove_dir_all(temp_root); + } +} |
