Skip to content

feat(tmux): add floating agent-status popup (prefix + g)#52

Open
TSHOGX wants to merge 3 commits into
Ataraxy-Labs:mainfrom
TSHOGX:feat/agent-status-popup
Open

feat(tmux): add floating agent-status popup (prefix + g)#52
TSHOGX wants to merge 3 commits into
Ataraxy-Labs:mainfrom
TSHOGX:feat/agent-status-popup

Conversation

@TSHOGX

@TSHOGX TSHOGX commented Jun 27, 2026

Copy link
Copy Markdown

Summary

  • Add a centered floating agent-status panel via tmux display-popup, bound to prefix + g (also reachable through the prefix o → g table). It lists agents across all sessions; Enter jumps to the selected agent's pane.
  • It opens scoped to all sessions; press a to toggle between all sessions and just the focused one. q / Esc closes the popup.
  • Configurable via @opensessions-popup-key (key, default g) and @opensessions-popup-width / @opensessions-popup-height (default 80% / 70%).

Motivation

The docked sidebar is great while you're working in a session, but there was no quick, transient way to glance at agent status across all sessions and jump to one without permanently rearranging panes. A floating popup gives that at-a-glance overview and disappears as soon as you've picked where to go.

Implementation notes

The TUI runs in a dedicated popup mode (OPENSESSIONS_POPUP=1) so the transient client behaves differently from a docked sidebar:

  • Skips IdentifyPane, so the server never resizes the popup pane to sidebar width or believes a docked sidebar is visible.
  • Quitting (q / Esc) or selecting an agent closes the popup locally and does not send /quit, keeping the shared server alive so the popup can be toggled freely.
  • Agent-panel scope is kept local: the a toggle no longer rewrites the shared server scope.
  • Does not pull focus back to the launching pane on startup.
  • popup.sh cold-starts the server first (via server-common.sh) in case a prior popup or sidebar tore it down.

Verification

  • cargo test -p opensessions-sidebar-core (34 passed, includes new coverage for popup focus, local scope, local quit, and jump-to-pane-then-close)
  • cargo build -p opensessions-sidebar

Add a centered floating panel that lists agents across all sessions and
lets you jump to one with Enter, opened via `tmux display-popup` and bound
to `prefix + g` (also reachable through the `prefix o → g` table). The key
is configurable with `@opensessions-popup-key` and the size with
`@opensessions-popup-width` / `@opensessions-popup-height`.

Unlike the docked sidebar, the TUI runs in popup mode (set through the
OPENSESSIONS_POPUP env var) so it can be toggled freely:

- skips IdentifyPane, so the server never resizes the transient pane to
  sidebar width or believes a docked sidebar is visible
- quitting (q / Esc) or selecting an agent closes the popup locally and
  does not send /quit, keeping the shared server alive
- agent-panel scope is kept local: it opens scoped to all sessions and the
  `a` toggle no longer rewrites the shared server scope
- does not pull focus back to the launching pane on startup

popup.sh cold-starts the server first via server-common.sh in case a prior
popup or sidebar tore it down.

Covered by unit tests for popup focus, local scope, local quit, and
jump-to-pane-then-close behavior.

@inspect-review inspect-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inspect review

Triage: 24 entities analyzed | 0 critical, 0 high, 16 medium, 8 low
Verdict: standard_review

Findings (1)

  1. [low] Formatting error in queue_focus_agent_pane: closing brace is on the same line as the if statement, causing a syntax error

Reviewed by inspect | Entity-level triage found 0 high-risk changes

The function's closing brace shared a line with the popup-mode if block,
which rustfmt flagged. Put it on its own line.

@inspect-review inspect-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inspect review

Triage: 24 entities analyzed | 0 critical, 0 high, 16 medium, 8 low
Verdict: standard_review

Findings (2)

  1. [low] In main.rs, the popup mode check for startup_refocused is incorrect. When popup_mode is true, the code does nothing (empty if branch), then falls through to the else-if that checks identity. This means a popup will still execute do_startup_refocus if identity exists, defeating the purpose of the popup_mode guard.
  2. [low] In main.rs, the popup mode check for startup_refocused is missing an else-if chain, causing do_startup_refocus to never be called for non-popup mode. The code has 'if popup_mode { ... } else if let Some(identity) = ...' but the else-if will never execute because it's not properly chained.

Reviewed by inspect | Entity-level triage found 0 high-risk changes

Rewrite the empty popup_mode if-branch as an explicit `if !popup_mode`
guard around the identity refocus. Behavior is unchanged (popups never
refocus the launching pane; docked sidebars with an identity do), but the
intent reads clearly without an empty branch that looked like a fall-through.

@inspect-review inspect-review Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

inspect review

Triage: 24 entities analyzed | 0 critical, 0 high, 16 medium, 8 low
Verdict: standard_review

Findings (2)

  1. [low] In main.rs, the popup mode check for startup_refocused is inverted - it checks !popup_mode but the comment says 'A popup is its own transient client, so skip the startup refocus'. However, the logic is correct (skip when popup_mode is true). The real bug is that startup_refocused is set to true BEFORE the conditional check, so even in popup mode, it prevents any future refocus attempts. This means if the first message isn't a State message, subsequent State messages won't trigger refocus logic.
  2. [low] In main.rs, there are two separate blocks checking app.should_close and returning early (lines 265-268 and 307-309 in the new code). The first block at line 265 returns Ok(()) after draining commands, but the second block at line 307 returns Ok(()) WITHOUT draining launches that were just collected. This creates an asymmetry where launches queued between command draining and the second should_close check will be lost.

Reviewed by inspect | Entity-level triage found 0 high-risk changes

@TSHOGX

TSHOGX commented Jun 27, 2026

Copy link
Copy Markdown
Author

Thanks for the review. I looked into both of the latest findings and don't think either is a live bug — happy to be corrected:

  1. startup_refocused set before the check — this is intentional. It's a one-shot startup refocus: in popup mode we deliberately never refocus the launching pane, so setting the flag and skipping is the desired behavior. For docked sidebars it fires exactly once on the first State message (the block lives in the State arm), which is when refocus is meaningful. As noted, the conditional logic itself is correct.

  2. should_close returning before drain_launchesshould_close is only ever set in popup mode, and only by quit (q/Esc) or jump-to-agent (activate_focused_item). Pending launches are produced only by l/L and the DiffCount hit target, none of which set should_close. So the two never co-occur and no launch can be dropped; both early-return blocks behave identically. The FocusAgentPane command from the jump path is already drained before the should_close return.

Let me know if I'm missing a path where a launch and should_close can be queued in the same frame.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant