notedeck

One damus client to rule them all
git clone git://jb55.com/notedeck
Log | Files | Refs | README | LICENSE

commit abe852a6d5092566f91c68e0ef4cd9875420c272
parent 6073de570cf90400c88d772251bcea13ee38fa10
Author: William Casarin <jb55@jb55.com>
Date:   Fri, 20 Feb 2026 13:30:20 -0800

dave: clear Done indicator based on session type

The Done focus cue was never cleared because auto-steal bypasses
the SwitchTo action path. Now Done indicators are cleared by a
dedicated clear_done_indicators() step: local agentic sessions
clear when git working tree is clean, chat and remote sessions
clear when the user is viewing them.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

Diffstat:
Mcrates/notedeck_dave/src/focus_queue.rs | 10++++++++++
Mcrates/notedeck_dave/src/lib.rs | 3+++
Mcrates/notedeck_dave/src/update.rs | 36++++++++++++++++++++++++++++++++++--
3 files changed, 47 insertions(+), 2 deletions(-)

diff --git a/crates/notedeck_dave/src/focus_queue.rs b/crates/notedeck_dave/src/focus_queue.rs @@ -129,6 +129,16 @@ impl FocusQueue { } } + /// Remove a session from the queue only if its priority is Done. + pub fn dequeue_done(&mut self, session_id: SessionId) { + if let Some(i) = self.find(session_id) { + if self.entries[i].priority == FocusPriority::Done { + self.entries.remove(i); + self.normalize_cursor_after_remove(i); + } + } + } + pub fn next(&mut self) -> Option<SessionId> { if self.entries.is_empty() { self.cursor = None; diff --git a/crates/notedeck_dave/src/lib.rs b/crates/notedeck_dave/src/lib.rs @@ -2416,6 +2416,9 @@ impl notedeck::App for Dave { notedeck::platform::try_vibrate(); } + // Clear Done indicators whose condition is met + update::clear_done_indicators(&self.session_manager, &mut self.focus_queue); + // Suppress auto-steal while the user is typing (non-empty input) let user_is_typing = self .session_manager diff --git a/crates/notedeck_dave/src/update.rs b/crates/notedeck_dave/src/update.rs @@ -520,6 +520,38 @@ pub fn toggle_auto_steal( new_state } +/// Clear Done indicators for sessions whose clearing condition is met. +/// +/// - **Local agentic sessions**: cleared when the git working tree is clean +/// (the user has committed or reverted changes). +/// - **Chat and remote sessions**: cleared when the session is the active one +/// (the user is viewing it). +pub fn clear_done_indicators(session_manager: &SessionManager, focus_queue: &mut FocusQueue) { + let active_id = session_manager.active_id(); + for session in session_manager.iter() { + if focus_queue.get_session_priority(session.id) != Some(FocusPriority::Done) { + continue; + } + let should_clear = if !session.is_remote() { + if let Some(agentic) = &session.agentic { + agentic + .git_status + .current() + .is_some_and(|r| r.as_ref().is_ok_and(|d| d.is_clean())) + } else { + // Chat session: clear when viewing + active_id == Some(session.id) + } + } else { + // Remote session: clear when viewing + active_id == Some(session.id) + }; + if should_clear { + focus_queue.dequeue_done(session.id); + } + } +} + /// Process auto-steal focus logic: switch to focus queue items as needed. /// Returns true if focus was stolen (switched to a NeedsInput or Done session), /// which can be used to raise the OS window. @@ -574,8 +606,8 @@ pub fn process_auto_steal_focus( tracing::debug!("Auto-steal: saved home session {:?}", home_session); } - // Jump to first Done item (keep in queue so blue dot renders; - // cleared when user manually focuses the session) + // Jump to first Done item (keep in queue; cleared externally + // when the session's clearing condition is met) if let Some(idx) = focus_queue.first_done_index() { focus_queue.set_cursor(idx); if let Some(entry) = focus_queue.current() {