feat(dashboard): auto-focus dashboard pane + proposals on new arrival
test / unit (pull_request) Successful in 18s
test / integration (pull_request) Successful in 1m10s

When a fresh proposal arrives, the dashboard now also:

  - Runs `tmux select-pane -t \$TMUX_PANE` (the dashboard's own
    pane id, captured at startup) so tmux focus jumps to the
    dashboard from wherever the operator was (typically claude
    in the right pane).
  - Flips internal focus to PANE_PROPOSALS so j/k navigates the
    queued items immediately.
  - Lands the selected cursor on the first new proposal —
    proposals are sorted by arrival ascending, so the earliest
    new arrival in the batch gets the cursor.

Stacks with the bell + label highlight from the previous
commit. The operator gets:
  1. Audible bell (or tmux activity marker)
  2. Tmux focus on the dashboard pane
  3. Dashboard's internal focus on the proposals list
  4. Cursor on the actual new proposal
  5. Pane label flashing `(new!)` in bold green

— all without leaving the keyboard.
This commit is contained in:
2026-05-26 16:04:23 -04:00
parent 9ac05c1a63
commit 3a7b7d054b
+22 -4
View File
@@ -1356,6 +1356,11 @@ def _main_loop(stdscr: "curses._CursesWindow") -> None:
# pre-existing queue entries on its first poll; those
# shouldn't ring the bell as if they just arrived.
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:
pending = discover_pending()
if selected >= len(pending):
@@ -1367,16 +1372,29 @@ def _main_loop(stdscr: "curses._CursesWindow") -> None:
now = time.monotonic()
live_ids = {qp.proposal.id for qp in pending}
# Detect proposals we've never seen before — used to ring
# the terminal bell (so tmux's monitor-bell or the
# terminal's own bell-on-activity surface a notice when
# the operator isn't looking at the dashboard pane).
# Detect proposals we've never seen before. Triggers:
# - terminal bell (`curses.beep` → tmux's monitor-bell)
# - tmux focus jump to the dashboard pane (so the
# 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()
if saw_first_tick and newly_arrived:
try:
curses.beep()
except curses.error:
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:
first_seen.setdefault(proposal_id, now)
for stale_id in list(first_seen.keys() - live_ids):