diff --git a/claude_bottle/cli/dashboard.py b/claude_bottle/cli/dashboard.py index 296b51a..00011b4 100644 --- a/claude_bottle/cli/dashboard.py +++ b/claude_bottle/cli/dashboard.py @@ -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):