Skip to main content

App

Struct App 

Source
pub struct App {
Show 43 fields pub stage: Stage, pub previous_stage: Stage, pub focused_pane: Pane, pub team: TeamSnapshot, pub selected_agent: Option<usize>, pub detail_buffer: Vec<String>, pub version: &'static str, pub capabilities: Capabilities, pub splash_started: Instant, pub last_refresh: Instant, pub last_pane_refresh: Instant, pub running: bool, pub tutorial_completed: bool, pub mailbox_tab: MailboxTab, pub mailbox: MailboxBuffers, pub mailbox_input_mode: Option<MailboxInputKind>, pub mailbox_input_snapshot: String, pub mailbox_detail_modal: Option<MessageRow>, pub mailbox_detail_scroll: u16, pub now_secs: f64, pub pending_approvals: Vec<Approval>, pub selected_approval: usize, pub approval_error: Option<String>, pub compose_target: Option<ComposeTarget>, pub compose_editor: Editor, pub compose_error: Option<String>, pub layout: MainLayout, pub wall_scroll: usize, pub selected_channel: Option<usize>, pub detail_splits: Vec<(String, SplitOrientation)>, pub selected_split: usize, pub pending_chord: Option<KeyCode>, pub tutorial_pending_for_team: bool, pub spinner_frame: usize, pub tutorial_step: usize, pub compose_picker_open: bool, pub compose_picker_index: usize, pub compose_attach_input_open: bool, pub compose_attach_buffer: String, pub last_synced_pane_sizes: HashMap<String, (u16, u16)>, pub detail_buffer_activity: Option<(String, u64)>, pub sysinfo: System, pub rate_limit_indicator_enabled: bool,
}

Fields§

§stage: Stage§previous_stage: Stage

Tracked so QuitConfirm can return to whichever stage opened it.

§focused_pane: Pane§team: TeamSnapshot§selected_agent: Option<usize>

Index into team.agents of the agent the detail pane is streaming. None when the team is empty or roster navigation hasn’t picked one yet.

§detail_buffer: Vec<String>

Lines from the most recent pane capture. Bounded to the last MAX_DETAIL_LINES so the buffer doesn’t grow unboundedly over a long-running session.

§version: &'static str§capabilities: Capabilities§splash_started: Instant§last_refresh: Instant

Last time the snapshot + pane capture were refreshed. Used by tick() to gate the next refresh.

§last_pane_refresh: Instant

Last time the focused agent’s pane was re-captured on the fast PANE_REFRESH_INTERVAL cadence (between full refreshes).

§running: bool§tutorial_completed: bool

First-launch detection — when the marker file exists, future stacked-PRs (PR-UI-7) skip the tutorial after splash. PR-UI-1 only reads the flag; nothing routes off it yet.

§mailbox_tab: MailboxTab

Active tab inside the mailbox pane (PR-UI-3). Walked with / when focused_pane == Mailbox (T-124 hard-swapped the prior [ / ] chord for arrow keys; T-074 bug 6 is the gating-on-focus invariant). Tab always cycles pane focus, never mailbox tabs — the previous “Tab cycles tabs when mailbox is focused” shape stranded operators inside the mailbox.

§mailbox: MailboxBuffers

Per-tab buffers + cursors for the focused agent’s mailbox view. Reset whenever the focused agent changes — switching agents starts the operator at the head of fresh traffic.

§mailbox_input_mode: Option<MailboxInputKind>

T-131 PR-2: which mailbox input the operator is currently editing, if any. Singleton — only one input open at a time across all tabs. When Some, Pane::Mailbox keystrokes route to the per-tab filter_text / search_text buffer on MailboxBuffers (the data lives there, per-tab; this is just the editing-UI flag).

§mailbox_input_snapshot: String

Pre-open snapshot of the active input buffer — restored on Esc (cancel-revert) so the operator can back out without losing the prior filter/search. Empty between sessions.

§mailbox_detail_modal: Option<MessageRow>

T-131 PR-3: mailbox detail modal — the row content the operator opened. Captured AT open-time and rendered from here independent of the underlying mailbox buffer: any subsequent extend() drain that would shift indices on the row cursor leaves this snapshot intact, so the operator sees the message they clicked, not whatever now happens to sit at the same index (variant (a) locked). None when no modal is open.

§mailbox_detail_scroll: u16

T-131 PR-3: vertical scroll offset (in wrapped body lines) within an open detail modal. Reset to 0 when the modal opens; bumped by j / Down / k / Up while the modal is the active stage. Ignored when no modal is open.

§now_secs: f64

T-131 PR-4: wall-clock seconds at the last render tick. The mailbox-row relative-time indicator (2m / 1h / 3d) reads from here so render is a pure function of App — snapshot tests can pin time deterministically by setting this field (otherwise wall-clock would diff snapshots every run). The run loop refreshes this before each terminal.draw; defaults to 0 in App::new so a freshly constructed test app + sent_at=0 fixture rows render now stably.

§pending_approvals: Vec<Approval>

Pending approvals snapshot (PR-UI-4). Drives the conditional stripe at the top of Triptych and the modal opened by a.

§selected_approval: usize

Index into pending_approvals of the row the modal is currently showing. Reset to 0 each time the modal opens; j / k (or / ) cycle.

§approval_error: Option<String>

Last error from a CLI-routed Approve/Deny call — surfaced inline in the modal so the operator sees why a decision didn’t take.

§compose_target: Option<ComposeTarget>

Open compose target — Some while Stage::ComposeModal is the active stage, None otherwise. Stored on App so the editor’s contents survive rerenders.

§compose_editor: Editor

Editor backing the compose modal. Reset to default() each time the modal opens so an old draft from a prior invocation can’t leak into a new send.

§compose_error: Option<String>

Last error from a CLI-routed send call — surfaced inline in the modal so the operator sees rate-limit / ACL-block errors without leaving the UI.

§layout: MainLayout

Active main-view layout (PR-UI-6). Triptych is the default; Ctrl+W toggles Wall, Ctrl+M toggles MailboxFirst.

§wall_scroll: usize

Top-of-window agent index for the Wall view’s vertical scroll. SPEC §3 caps visible tiles at 4; this offsets which 4-agent window is shown when the team has more.

§selected_channel: Option<usize>

Selected channel index (into team.channels) for the MailboxFirst layout’s channel list and for the broadcast picker. None until the operator picks one.

§detail_splits: Vec<(String, SplitOrientation)>

Splits within Triptych’s detail pane (PR-UI-6). When non-empty, the detail pane subdivides; each entry pairs an agent id with the per-split orientation (PR-UI-7 lift of the Q1 deferral). selected_split is the vim-window-motion focus.

§selected_split: usize§pending_chord: Option<KeyCode>

Chord-prefix machine for Ctrl+W follow-ups (PR-UI-7 lift of PR-UI-6’s Ctrl+Q alias). When Some(KeyCode::Char('w')), the next key is interpreted as a Ctrl+W follow: q = close split, o = close others. Cleared on any unrelated keypress so a typo doesn’t leave the editor stuck.

§tutorial_pending_for_team: bool

true when the operator’s first launch on this team has not yet completed the tutorial — drives the auto-open after splash. Reset to false on tutorial completion.

§spinner_frame: usize

Brand-spinner frame counter (PR-UI-7). Bumped each refresh tick so the statusline indicator shows the app is alive.

§tutorial_step: usize

Tutorial step cursor (PR-UI-7). Index into onboarding::STEPS; reset to 0 when the tutorial reopens.

§compose_picker_open: bool

Modal substage for the broadcast channel picker (PR-UI-6). When true the compose modal renders a picker over the editor; selecting a channel populates compose_target and drops back to the editor.

§compose_picker_index: usize

Picker selection cursor — index into team.channels.

§compose_attach_input_open: bool

T-32: when true, the compose modal renders a single-line path-input overlay instead of the editor; Enter appends a 📎 attachment: <path> line to the editor body and closes the overlay. Tab inside the editor opens it; Esc inside the overlay cancels back to the editor (matches the picker overlay’s modal-vs-modal symmetry from PR-UI-6).

§compose_attach_buffer: String

Single-line buffer for the path-input overlay. Reset on close so a cancelled draft can’t leak into the next attach attempt.

§last_synced_pane_sizes: HashMap<String, (u16, u16)>

T-199: per-session cache of the last Detail-pane size we pushed to tmux resize-pane. The run loop diffs the current Detail rect against this on every frame and only spawns the tmux command when the size actually changed — common case (no resize, no focus switch) is a HashMap lookup. Keyed by tmux_session (e.g. t-hello-manager). See crate::pane_resize.

§detail_buffer_activity: Option<(String, u64)>

#277: the (session, window_activity ts) of the content currently in detail_buffer, as last captured by the fast-cadence path. The fast path skips the heavy capture-pane only when the focused session AND its activity ts both still match — so a focus switch (different session) or new output (changed ts) always re-captures, and a stale frame can’t survive on screen. None forces a capture. The slow 1 Hz refreshes re-capture unconditionally (the safety net) and may leave this lagging by one tick: at most one redundant fast capture, never a wrong frame.

§sysinfo: System

T-209: live system handle for the bottom status bar’s CPU% + RAM% indicator. Refreshed in-place on the existing 1-second App tick (see refresh_with_default_sources and the run-loop tick at the top of run()); no background thread. default-features = false + only the system feature is enabled in the dep to keep the compile surface narrow. See crate::status_bar.

§rate_limit_indicator_enabled: bool

T-212 / #431 rate-limit indicator gate. true by default; false only when TEAMCTL_UI_RATE_LIMIT_INDICATOR=0 was set at App::new(). The bottom status bar’s center slot only renders when this is true. Tests can flip the field directly to exercise both branches without process-wide env-var racing.

Implementations§

Source§

impl App

Source

pub fn new() -> Self

Construct an empty App — no team snapshot loaded. Used by tests and as the splash-stage default. Production launch goes through App::launch() which immediately runs an initial refresh() so the splash screen already shows the real team name + agent count.

Source

pub fn enter_help_overlay(&mut self)

Per-tutorial-step cursor (used by Stage::Tutorial). Wraps at the end so t-then-keys walks the full tour.

Source

pub fn close_help_overlay(&mut self)

Source

pub fn enter_tutorial(&mut self)

Source

pub fn close_tutorial(&mut self)

Source

pub fn tutorial_advance(&mut self)

Source

pub fn tutorial_back(&mut self)

Source

pub fn toggle_wall_layout(&mut self)

Source

pub fn toggle_mailbox_first_layout(&mut self)

Source

pub fn wall_scroll_up(&mut self)

Source

pub fn wall_scroll_down(&mut self)

Source

pub fn select_next_channel(&mut self)

Source

pub fn select_prev_channel(&mut self)

Source

pub fn add_detail_split_vertical(&mut self)

Add a split for the focused agent (or current selection) to the detail pane. Cap at 4 splits per the SPEC §3 cap. Add a vertical split (PR-UI-7). Ctrl+| calls this.

Source

pub fn add_detail_split_horizontal(&mut self)

Add a horizontal split (PR-UI-7). Ctrl+- calls this.

Source

pub fn add_detail_split(&mut self)

Back-compat shim — earlier PRs called the unsuffixed name. Defaults to vertical (matching the most-common chord Ctrl+|). Kept so the test surface PR-UI-6 pinned doesn’t drift.

Source

pub fn close_focused_split(&mut self)

Source

pub fn cycle_split_next(&mut self)

Source

pub fn cycle_split_prev(&mut self)

Source

pub fn enter_compose_broadcast_with_picker(&mut self)

Open the broadcast compose flow — picker first when at least one channel is declared, else fall back to the project’s all channel (PR-UI-5 behaviour) on the assumption that all always exists in production composes.

Source

pub fn picker_next(&mut self)

Source

pub fn picker_prev(&mut self)

Source

pub fn picker_confirm(&mut self)

Source

pub fn open_compose_attach_input(&mut self)

T-32: open the path-input overlay. Resets the buffer so a previously-cancelled draft can’t carry over.

Source

pub fn confirm_compose_attach_input(&mut self)

T-32: append a 📎 attachment: <path> line to the compose editor and close the overlay. The line lands as a fresh row at the end of the body so the operator can edit it (or delete it) before sending. Whitespace-only buffers are ignored — Tab followed by Enter shouldn’t insert an empty marker.

Source

pub fn close_compose_attach_input(&mut self)

Source

pub fn cycle_mailbox_tab(&mut self)

Source

pub fn cycle_mailbox_tab_back(&mut self)

Source

pub fn mailbox_cursor_down(&mut self)

Source

pub fn mailbox_cursor_up(&mut self)

Source

pub fn mailbox_page_down(&mut self)

Source

pub fn mailbox_page_up(&mut self)

Source

pub fn mailbox_cursor_home(&mut self)

Source

pub fn mailbox_cursor_end(&mut self)

Source

pub fn open_mailbox_filter_input(&mut self)

Open the sender-substring filter input on the active tab. Snapshots the current value so Esc can revert.

Source

pub fn open_mailbox_search_input(&mut self)

Open the body-substring search input on the active tab. Snapshots the current value so Esc can revert.

Source

pub fn mailbox_input_push_char(&mut self, c: char)

Append c to the active input buffer. visible_indices recomputes live; the cursor re-clamps inside MailboxBuffers.

Source

pub fn mailbox_input_pop_char(&mut self)

Pop one character from the active input buffer.

Source

pub fn mailbox_input_confirm(&mut self)

Confirm and close the input — keep the operator’s typed text.

Source

pub fn mailbox_input_cancel(&mut self)

Cancel and close the input — revert the active buffer to the pre-open snapshot so the operator can back out without losing the prior filter / search.

Source

pub fn open_mailbox_detail_modal(&mut self)

Open the detail modal on the currently-selected mailbox row. No-op when visible_indices is empty (no row to select) so Enter on an empty / fully-filtered tab silently does nothing rather than opening a modal on garbage.

Source

pub fn close_mailbox_detail_modal(&mut self)

Close the detail modal and return to the Triptych. Clears the snapshot; the row cursor underneath is untouched.

Source

pub fn mailbox_detail_scroll_down(&mut self)

Scroll the detail modal body one wrapped line down. Caller supplies the maximum scroll value (lines beyond which there is no content); we clamp.

Source

pub fn mailbox_detail_scroll_up(&mut self)

Scroll the detail modal body one wrapped line up.

Source

pub fn cycle_focus_back(&mut self)

Source

pub fn has_pending_approvals(&self) -> bool

Source

pub fn enter_approvals_modal(&mut self)

Source

pub fn close_approvals_modal(&mut self)

Source

pub fn cycle_approval_next(&mut self)

Source

pub fn cycle_approval_prev(&mut self)

Source

pub fn focused_approval(&self) -> Option<&Approval>

Source

pub fn replace_approvals(&mut self, approvals: Vec<Approval>)

Replace the pending-approvals list. Closes the modal when the queue empties (no row to act on); preserves the modal otherwise but clamps selected_approval into range so an approval resolved out-of-band doesn’t leave us pointing at a stale index.

Source

pub fn apply_decision<D: ApprovalDecider>( &mut self, decider: &D, kind: Decision, note: &str, )

Apply a decision to the focused approval via the injected decider. The decider routes through teamctl approve|deny in production; tests inject a recorder. On success the row gets removed from the local pending_approvals snapshot optimistically — the next refresh_approvals will reconcile against the broker.

Source

pub fn enter_compose_dm_for_focused(&mut self)

Open the compose modal for the focused agent (if any). The @ chord. No-op when no agent is focused.

Source

pub fn enter_compose_broadcast(&mut self)

Open the compose modal targeting the project’s all channel — the broadcast wire. The ! chord. PR-UI-5 ships with channel scoping limited to all (the Wire tab is the only channel context the mailbox pane currently surfaces); PR-UI-6’s mailbox UI work will widen the scope to per-channel targets when individual channels become first-class in the pane.

Source

pub fn close_compose_modal(&mut self)

Source

pub fn apply_send<S: MessageSender, M: MailboxSource>( &mut self, sender: &S, mailbox_source: &M, )

Send the current compose body via the injected message sender. Routes through teamctl send|broadcast in production; tests inject a recorder. Closes the modal + triggers a mailbox refresh on success; surfaces error inline on failure.

Source

pub fn dismiss_splash(&mut self)

Source

pub fn cycle_focus(&mut self)

Source

pub fn select_prev(&mut self)

Move roster selection up by one — wraps at the top. No-op when the team is empty. Does not change focused_pane. Resets mailbox buffers when the resulting agent id differs from the prior selection — switching agents should start the operator at the head of fresh traffic.

Source

pub fn select_next(&mut self)

Move roster selection down by one — wraps at the bottom. No-op when the team is empty.

Source

pub fn selected_agent_id(&self) -> Option<String>

<project>:<agent> of the currently selected agent, if any.

Source

pub fn enter_quit_confirm(&mut self)

Source

pub fn cancel_quit(&mut self)

Source

pub fn confirm_quit(&mut self)

Source

pub fn replace_team(&mut self, team: TeamSnapshot)

Replace the team snapshot. Preserves the current selection when the agent at that index still exists; otherwise resets to the first agent (or None for an empty team). Resets the mailbox buffers when the resulting agent id differs from the prior selection — same agent-changed contract as select_next / select_prev.

Source

pub fn focused_session(&self) -> Option<&str>

Return the focused agent’s tmux session name, if any. Used by the run loop to know which session to capture.

Source

pub fn stream_target_session(&self) -> Option<String>

Tmux session that stream-keys mode should target. Cell 0 of the detail-pane split layout is always the focused agent; cells 1..N are the entries in detail_splits. When the operator has focused a non-zero split, route stream-keys to that split’s agent — that’s the cell visually showing as the focus ring, so it’s the one the operator expects to type into.

Source

pub fn enter_stream_keys(&mut self)

Enter stream-keys mode. No-op unless an agent is selected — without a target session there’s nothing to forward to. Caller is responsible for the focused-pane gate (entry chord only fires from focused_pane == Pane::Detail).

Source

pub fn exit_stream_keys(&mut self)

Exit stream-keys mode and return to whichever stage opened it. Esc is the only exit chord per the issue’s recommendation — every other key (including Ctrl+C) forwards to the agent.

Source

pub fn set_detail_buffer(&mut self, lines: Vec<String>)

Replace the detail buffer, clipped at the recent-line cap.

Trait Implementations§

Source§

impl Default for App

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

§

impl Freeze for App

§

impl RefUnwindSafe for App

§

impl Send for App

§

impl Sync for App

§

impl Unpin for App

§

impl UnsafeUnpin for App

§

impl UnwindSafe for App

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more