diff options
| author | Igor Soarez <igor@soarez.org> | 2026-10-27 14:12:28 +0200 |
|---|---|---|
| committer | Igor Soarez <igor@soarez.org> | 2026-10-27 14:12:28 +0200 |
| commit | 50c433e82c368b9861a89deecb74e5f29fa50353 (patch) | |
| tree | fe6d87b79387f6c53ef9bf615b7b530daf9a8d3a /crates/dinggerd/src/hooks.rs | |
| parent | 9f3a567d4bb1b7b6e42a744936f993bcd437882d (diff) | |
mark as unread: make an idle agent done again, to come back to it
like "mark as unread" in mail: an agent you have seen (idle) becomes done
again, last among done agents of its importance, with no sound or
notification. it stays done until you come back to it: leave its tmux
window and return, or jump to it. marked while you look at it, it holds
until you look away (`held`); a new turn clears it. only idle agents can
be marked.
- daemon: `pane.mark_unread` on the hook socket; the watcher applies it
after the tick's observations (mark_unread, advance's hold rule).
- `dingger unread [--pane]`: refuses done, working, blocked, and panes
without an agent; returns once the agent shows as done.
- pi: /unread (refused while the agent works or is blocked).
- panel: right-click an idle agent in the full list, "Mark as unread".
- search: ⌘U or right-click marks the selected idle agent; the footer
offers ⌘U when it applies; search stays open.
tests: unit (hold, jump, new turn, idle only), e2e step 22 (queue
position, hold, look away and back, jump, refusals, no sound, menu, the
extension), pi-live in real pi 1.4 (a mark, not a turn: an unknown
command would also end done), swift selftest (menu only for idle, footer).
Diffstat (limited to 'crates/dinggerd/src/hooks.rs')
| -rw-r--r-- | crates/dinggerd/src/hooks.rs | 27 |
1 files changed, 26 insertions, 1 deletions
diff --git a/crates/dinggerd/src/hooks.rs b/crates/dinggerd/src/hooks.rs index a649fa0..b2f9e97 100644 --- a/crates/dinggerd/src/hooks.rs +++ b/crates/dinggerd/src/hooks.rs @@ -18,6 +18,9 @@ //! order, so a turn that starts and finishes between two polls is still a //! completion, and each report wakes the watcher immediately. //! +//! `{"method":"pane.mark_unread","params":{"pane_id":"%3"}}` marks an idle +//! agent unread (`dingger unread`, pi's /unread): it shows as done again. +//! //! Each line is answered with `{"ok":true}`. //! //! `{"method":"poke"}` only wakes the watcher; `dingger focus` and tmux's @@ -26,7 +29,7 @@ //! until the next report or until the pane stops running the agent; screen //! detection is the fallback for panes that never report. -use std::collections::HashMap; +use std::collections::{HashMap, HashSet}; use std::io::{BufRead, BufReader}; use std::os::unix::net::{UnixListener, UnixStream}; use std::path::PathBuf; @@ -68,6 +71,20 @@ fn reports() -> MutexGuard<'static, HashMap<String, PaneReports>> { .unwrap_or_else(|poisoned| poisoned.into_inner()) } +/// Panes marked unread, not yet applied by the watcher. +fn unread_marks() -> MutexGuard<'static, HashSet<String>> { + static MARKS: OnceLock<Mutex<HashSet<String>>> = OnceLock::new(); + MARKS + .get_or_init(|| Mutex::new(HashSet::new())) + .lock() + .unwrap_or_else(|poisoned| poisoned.into_inner()) +} + +/// Whether the pane was marked unread since the last call. +pub fn take_unread(pane_id: &str) -> bool { + unread_marks().remove(pane_id) +} + fn wake_signal() -> &'static (Mutex<bool>, Condvar) { static WAKE: OnceLock<(Mutex<bool>, Condvar)> = OnceLock::new(); WAKE.get_or_init(|| (Mutex::new(false), Condvar::new())) @@ -145,6 +162,14 @@ fn handle_stream(stream: UnixStream) { wake(); continue; } + if message.method == "pane.mark_unread" { + if let Some(params) = message.params { + info!(pane = %params.pane_id, "marked unread"); + unread_marks().insert(params.pane_id); + wake(); + } + continue; + } let (true, Some(params)) = (message.method == "pane.report_agent", message.params) else { continue; }; |
