feat(dashboard): highlight proposals pane + bell on new proposal #50

Merged
didericis merged 2 commits from proposal-arrival-highlight into main 2026-05-26 16:07:15 -04:00
Showing only changes of commit 3a7b7d054b - Show all commits
+22 -4
View File
@@ -1356,6 +1356,11 @@ def _main_loop(stdscr: "curses._CursesWindow") -> None:
# pre-existing queue entries on its first poll; those # pre-existing queue entries on its first poll; those
# shouldn't ring the bell as if they just arrived. # shouldn't ring the bell as if they just arrived.
saw_first_tick = False saw_first_tick = False
# The dashboard's own tmux pane id (tmux sets `$TMUX_PANE`
# per-pane). Captured at startup so a new-proposal arrival
# can `tmux select-pane` back to the dashboard from
# whatever pane the operator is currently in.
dashboard_pane_id = os.environ.get("TMUX_PANE", "")
while True: while True:
pending = discover_pending() pending = discover_pending()
if selected >= len(pending): if selected >= len(pending):
@@ -1367,16 +1372,29 @@ def _main_loop(stdscr: "curses._CursesWindow") -> None:
now = time.monotonic() now = time.monotonic()
live_ids = {qp.proposal.id for qp in pending} live_ids = {qp.proposal.id for qp in pending}
# Detect proposals we've never seen before — used to ring # Detect proposals we've never seen before. Triggers:
# the terminal bell (so tmux's monitor-bell or the # - terminal bell (`curses.beep` → tmux's monitor-bell)
# terminal's own bell-on-activity surface a notice when # - tmux focus jump to the dashboard pane (so the
# the operator isn't looking at the dashboard pane). # operator notices even if they were typing at claude)
# - dashboard's internal focus flip to the proposals
# pane (so j/k navigates the queued items immediately)
newly_arrived = live_ids - first_seen.keys() newly_arrived = live_ids - first_seen.keys()
if saw_first_tick and newly_arrived: if saw_first_tick and newly_arrived:
try: try:
curses.beep() curses.beep()
except curses.error: except curses.error:
pass pass
if dashboard_pane_id and _in_tmux():
_tmux_select_pane(dashboard_pane_id)
focus = PANE_PROPOSALS
# Land the cursor on the first new proposal so the
# operator can act immediately. Proposals are sorted
# by arrival_timestamp ascending; find the lowest
# index whose id is in `newly_arrived`.
for i, qp in enumerate(pending):
if qp.proposal.id in newly_arrived:
selected = i
break
for proposal_id in live_ids: for proposal_id in live_ids:
first_seen.setdefault(proposal_id, now) first_seen.setdefault(proposal_id, now)
for stale_id in list(first_seen.keys() - live_ids): for stale_id in list(first_seen.keys() - live_ids):