Documentation
¶
Overview ¶
Package center implements the center-pane Bubble Tea model: the agent tab strip, per-tab PTY I/O and flushing, the diff viewer, and mouse/keyboard text selection. It is the largest UI subsystem; tab work is serialized through a per-model actor (see tab_actor.go) and output is parsed by internal/vterm. For the streaming/scrolling model (flush pipeline, DEC 2026 frame atomicity, viewport anchoring, chat history view, drag auto-scroll) see SCROLLING.md in this package.
Tab-actor concurrency invariants. The tab actor (RunTabActor) serializes all mutations to a tab's Terminal so that PTY-reader goroutines, the Bubble Tea update loop, and selection/scroll handlers never touch the same terminal concurrently. When editing this package, preserve these rules:
Single write path. Every tab terminal write goes through RunTabActor via a tabEvent (sendTabEvent -> handleTabEvent), or — only when the actor channel cannot accept the event — through the synchronous fallback in recoverFailedActorSend. Never call tab.WriteToTerminal or other terminal mutators directly from Update; that bypasses the actor and races the reader.
Backpressure contract. shouldDropTabEvent sheds load only for the selection/scroll class of events (selection-update, selection-scroll-tick, scroll-by, scroll-page) once tabEvents is >=75% full; those are coalescible UI gestures. tabEventWriteOutput is NEVER dropped — losing output corrupts the terminal — and the closed-tab guard in sendTabEvent returns true (treat as delivered) for write events so callers do not re-buffer them.
See internal/app/MESSAGE_FLOW.md and internal/app/ARCHITECTURE.md (Invariants) for the broader External-message rules these constraints fit inside.
Index ¶
- type DetachedTabInfo
- type Model
- func (m *Model) ActiveTerminalStatusLine() string
- func (m *Model) AddTab(tab *Tab)
- func (m *Model) AddTabsFromWorkspace(ws *data.Workspace, tabs []data.TabInfo) tea.Cmd
- func (m *Model) Blur()
- func (m *Model) CanConsumeWheel() bool
- func (m *Model) CleanupWorkspace(ws *data.Workspace)
- func (m *Model) Close()
- func (m *Model) CloseActiveTab() tea.Cmd
- func (m *Model) ContentWidth() int
- func (m *Model) DetachActiveTab() tea.Cmd
- func (m *Model) DetachTabByID(wsID string, tabID TabID) tea.Cmd
- func (m *Model) EnforceAttachedAgentTabLimit(maxAttached int) ([]DetachedTabInfo, []tea.Cmd)
- func (m *Model) Focus()
- func (m *Model) Focused() bool
- func (m *Model) FocusedAgentTitle() string
- func (m *Model) GetActiveWorkspaceIDs() []string
- func (m *Model) GetActiveWorkspaceRoots() []string
- func (m *Model) GetRunningWorkspaceRoots() []string
- func (m *Model) GetTabsInfo() ([]data.TabInfo, int)
- func (m *Model) GetTabsInfoForWorkspace(wsID string) ([]data.TabInfo, int)
- func (m *Model) HasActiveAgents() bool
- func (m *Model) HasActiveAgentsInWorkspace(wsID string) bool
- func (m *Model) HasActiveTerminal() bool
- func (m *Model) HasDiffViewer() bool
- func (m *Model) HasRunningAgents() bool
- func (m *Model) HasTabs() bool
- func (m *Model) HasWorkspaceState(wsID string) bool
- func (m *Model) HelpBuildCount() uint64
- func (m *Model) HelpLines(width int) []string
- func (m *Model) HelpVersion() uint64
- func (m *Model) Init() tea.Cmd
- func (m *Model) IsTabActive(tab *Tab) bool
- func (m *Model) NextTab() tea.Cmd
- func (m *Model) PrevTab() tea.Cmd
- func (m *Model) ReattachActiveTab() tea.Cmd
- func (m *Model) ReattachActiveTabIfDetached() tea.Cmd
- func (m *Model) RebindWorkspaceID(previous, current *data.Workspace) tea.Cmd
- func (m *Model) RestartActiveTab() tea.Cmd
- func (m *Model) RestoreTabsFromWorkspace(ws *data.Workspace) tea.Cmd
- func (m *Model) RunTabActor(ctx context.Context) error
- func (m *Model) ScrollActiveTerminalPage(direction int)
- func (m *Model) SelectTab(index int) tea.Cmd
- func (m *Model) SendToTerminal(s string)
- func (m *Model) SetCanFocusRight(can bool)
- func (m *Model) SetInstanceID(id string)
- func (m *Model) SetMsgSink(sink func(tea.Msg))
- func (m *Model) SetMsgSinkTry(sink func(tea.Msg) bool)
- func (m *Model) SetOffset(x int)
- func (m *Model) SetShowKeymapHints(show bool)
- func (m *Model) SetSize(width, height int)
- func (m *Model) SetStyles(styles common.Styles)
- func (m *Model) SetTmuxOptions(opts tmux.Options)
- func (m *Model) SetWorkspace(ws *data.Workspace)
- func (m *Model) StartPTYReaders() tea.Cmd
- func (m *Model) TabBarView() string
- func (m *Model) TerminalLayer() *compositor.VTermLayer
- func (m *Model) TerminalLayerWithCursorOwner(cursorOwner bool) *compositor.VTermLayer
- func (m *Model) TerminalViewport() (x, y, width, height int)
- func (m *Model) TickSpinner()
- func (m *Model) Update(msg tea.Msg) (*Model, tea.Cmd)
- func (m *Model) View() string
- func (m *Model) ViewChromeOnly() string
- type PTYCursorRefresh
- type PTYFlush
- type PTYOutput
- type PTYRestart
- type PTYStopped
- type Tab
- type TabID
- type TabInputFailed
- type TerminalMetrics
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type DetachedTabInfo ¶ added in v0.0.14
DetachedTabInfo identifies a tab detached by automatic limit enforcement.
type Model ¶
type Model struct {
// contains filtered or unexported fields
}
Model is the Bubbletea model for the center pane
func (*Model) ActiveTerminalStatusLine ¶
ActiveTerminalStatusLine returns the status line for the active terminal.
func (*Model) AddTab ¶
AddTab appends a tab to the model. Used by harness/test code that builds tabs manually.
func (*Model) AddTabsFromWorkspace ¶ added in v0.0.9
AddTabsFromWorkspace adds new tabs without resetting existing UI state.
func (*Model) CanConsumeWheel ¶ added in v0.0.18
CanConsumeWheel reports whether the active center tab can meaningfully handle mouse-wheel input. Detached chat tabs count so wheel-driven focus can trigger reattach; otherwise a tab must have scrollable diff or terminal content.
func (*Model) CleanupWorkspace ¶ added in v0.0.5
CleanupWorkspace removes all tabs and state for a deleted workspace
func (*Model) CloseActiveTab ¶
CloseActiveTab closes the current tab (public wrapper)
func (*Model) ContentWidth ¶
ContentWidth returns the content width inside the pane.
func (*Model) DetachActiveTab ¶ added in v0.0.9
DetachActiveTab closes the PTY client but keeps the tmux session alive.
func (*Model) DetachTabByID ¶ added in v0.0.9
DetachTabByID closes the PTY client for a specific tab and keeps the tmux session alive.
func (*Model) EnforceAttachedAgentTabLimit ¶ added in v0.0.14
func (m *Model) EnforceAttachedAgentTabLimit(maxAttached int) ([]DetachedTabInfo, []tea.Cmd)
EnforceAttachedAgentTabLimit detaches least-recently-focused chat tabs when the number of attached/running chat tabs exceeds maxAttached.
The currently focused tab in the active workspace is never auto-detached.
func (*Model) FocusedAgentTitle ¶ added in v0.0.20
FocusedAgentTitle returns the OSC-reported window title of the currently displayed agent tab, or "" when there is none. Reads the tab's VTerm under tab.mu (the VTerm has no internal lock).
func (*Model) GetActiveWorkspaceIDs ¶ added in v0.0.9
GetActiveWorkspaceIDs returns all workspace IDs with active agents.
func (*Model) GetActiveWorkspaceRoots ¶ added in v0.0.5
GetActiveWorkspaceRoots returns all workspace root paths with active agents.
func (*Model) GetRunningWorkspaceRoots ¶ added in v0.0.5
GetRunningWorkspaceRoots returns all workspace root paths with running agents. This includes agents that are running but idle (waiting at prompt).
func (*Model) GetTabsInfo ¶
GetTabsInfo returns information about current tabs for persistence
func (*Model) GetTabsInfoForWorkspace ¶ added in v0.0.9
GetTabsInfoForWorkspace returns tab information for a specific workspace ID.
func (*Model) HasActiveAgents ¶
HasActiveAgents returns whether any tab has emitted output recently. This is used to drive UI activity indicators without relying on process liveness alone.
func (*Model) HasActiveAgentsInWorkspace ¶ added in v0.0.5
HasActiveAgentsInWorkspace returns whether any tab in a workspace is actively outputting.
func (*Model) HasActiveTerminal ¶ added in v0.0.20
HasActiveTerminal reports whether the active tab has a terminal viewport.
func (*Model) HasDiffViewer ¶ added in v0.0.3
HasDiffViewer returns true if the active tab has a diff viewer.
func (*Model) HasRunningAgents ¶
HasRunningAgents returns whether any tab has an active agent across workspaces.
func (*Model) HasWorkspaceState ¶ added in v0.0.14
HasWorkspaceState reports whether the model has tab state for a workspace. True means tabs were explicitly managed (even if currently empty).
func (*Model) HelpBuildCount ¶ added in v0.0.20
HelpBuildCount reports how many times HelpLines has been invoked. Test instrumentation for the compose-time skip gate; not for production use.
func (*Model) HelpLines ¶
HelpLines returns the help lines for the given width, respecting visibility.
func (*Model) HelpVersion ¶ added in v0.0.20
HelpVersion returns the monotonic version of the inputs to HelpLines. The compose layer in internal/app skips rebuilding the help string while this version and the compose geometry are unchanged.
func (*Model) IsTabActive ¶ added in v0.0.5
IsTabActive returns whether a specific tab has emitted output recently. This is used for the tab bar spinner animation (shows activity, not just running state).
func (*Model) ReattachActiveTab ¶ added in v0.0.9
ReattachActiveTab reattaches to a detached/stopped tmux session.
func (*Model) ReattachActiveTabIfDetached ¶ added in v0.0.14
ReattachActiveTabIfDetached attempts reattach only when the active tab is a detached assistant/chat tab. It is safe to call from automatic UI flows.
func (*Model) RebindWorkspaceID ¶ added in v0.0.14
RebindWorkspaceID migrates tab state from a previous workspace ID to a new one. This keeps tabs/session state visible when workspace identity changes during reloads.
func (*Model) RestartActiveTab ¶ added in v0.0.9
RestartActiveTab restarts a stopped or detached agent tab by creating a fresh tmux client.
func (*Model) RestoreTabsFromWorkspace ¶ added in v0.0.9
RestoreTabsFromWorkspace recreates tabs from persisted workspace metadata. Only agent tabs with known assistants are restored.
func (*Model) ScrollActiveTerminalPage ¶ added in v0.0.20
ScrollActiveTerminalPage scrolls the active terminal by one page-sized step. A positive direction scrolls up into history; a negative direction scrolls down toward live output.
func (*Model) SendToTerminal ¶
SendToTerminal sends a string directly to the active terminal
func (*Model) SetCanFocusRight ¶
SetCanFocusRight controls whether focus-right hints should be shown.
func (*Model) SetInstanceID ¶ added in v0.0.9
SetInstanceID sets the tmux instance tag for sessions created by this model.
func (*Model) SetMsgSink ¶ added in v0.0.4
SetMsgSink sets a callback for PTY messages.
func (*Model) SetMsgSinkTry ¶ added in v0.0.17
SetMsgSinkTry sets a callback for PTY messages that reports whether enqueue succeeded.
func (*Model) SetOffset ¶
SetOffset sets the X offset of the pane from screen left (for mouse coordinate conversion).
func (*Model) SetShowKeymapHints ¶
SetShowKeymapHints controls whether helper text is rendered.
func (*Model) SetTmuxOptions ¶ added in v0.0.20
SetTmuxOptions stores the resolved tmux options and forwards them to the agent manager.
func (*Model) SetWorkspace ¶ added in v0.0.5
SetWorkspace sets the active workspace.
func (*Model) StartPTYReaders ¶
StartPTYReaders starts reading from all PTYs across all workspaces
func (*Model) TabBarView ¶
TabBarView returns the rendered tab bar string.
func (*Model) TerminalLayer ¶
func (m *Model) TerminalLayer() *compositor.VTermLayer
TerminalLayer returns a VTermLayer for the active terminal, if any. It snapshots terminal state under lock and reuses cached snapshots when valid.
func (*Model) TerminalLayerWithCursorOwner ¶ added in v0.0.16
func (m *Model) TerminalLayerWithCursorOwner(cursorOwner bool) *compositor.VTermLayer
TerminalLayerWithCursorOwner returns a VTermLayer for the active terminal while enforcing whether this pane currently owns cursor rendering.
func (*Model) TerminalViewport ¶
TerminalViewport returns the terminal content area coordinates relative to the pane. Returns (x, y, width, height) where the terminal content should be rendered. This is for layer-based rendering positioning within the bordered pane. Uses terminalMetrics() as the single source of truth for geometry.
func (*Model) TickSpinner ¶ added in v0.0.5
func (m *Model) TickSpinner()
TickSpinner advances the spinner animation frame.
func (*Model) ViewChromeOnly ¶
ViewChromeOnly renders only the pane chrome (border, tab bar, help lines) without the terminal content. This is used with VTermLayer for layer-based rendering. IMPORTANT: The output structure must match View() exactly so buildBorderedPane produces the same layout.
type PTYCursorRefresh ¶ added in v0.0.16
PTYCursorRefresh re-renders chat cursor policy when time-based windows expire.
type PTYRestart ¶ added in v0.0.5
PTYRestart requests restarting a PTY reader for a tab.
type PTYStopped ¶
PTYStopped signals that the PTY read loop has stopped (terminal closed or error)
type Tab ¶
type Tab struct {
ID TabID // Unique identifier that survives slice reordering
Name string
Assistant string
Workspace *data.Workspace
Agent *appPty.Agent
SessionName string
Detached bool
Terminal *vterm.VTerm // Virtual terminal emulator with scrollback
DiffViewer *diff.Model // Native diff viewer (replaces PTY-based viewer)
Running bool // Whether the agent is actively running
// ptyio.State holds the shared PTY buffering/reader/restart/snapshot
// bookkeeping (locking owned by mu, as documented on the type).
ptyio.State
// Mouse selection state
Selection common.SelectionState
// contains filtered or unexported fields
}
Tab represents a single tab in the center pane
func (*Tab) WriteToTerminal ¶
WriteToTerminal writes bytes to the tab terminal while holding the tab lock.
type TabID ¶
type TabID string
TabID is a unique identifier for a tab that survives slice reordering
type TabInputFailed ¶ added in v0.0.6
type TerminalMetrics ¶
type TerminalMetrics struct {
// For mouse hit-testing (screen coordinates to terminal coordinates)
ContentStartX int // X offset from pane left edge (border + padding)
ContentStartY int // Y offset from pane top edge (border + tab bar)
// Terminal dimensions
Width int // Terminal width in columns
Height int // Terminal height in rows
}
TerminalMetrics holds the computed geometry for the terminal content area. This is the single source of truth for terminal positioning and sizing.
Source Files
¶
- harness.go
- model.go
- model_activity_visibility.go
- model_chat_tab.go
- model_cursor_policy.go
- model_cursor_refresh.go
- model_input.go
- model_input_echo.go
- model_input_keys.go
- model_input_lifecycle.go
- model_input_lifecycle_pty.go
- model_input_lifecycle_pty_activity.go
- model_input_lifecycle_pty_restart.go
- model_input_mouse.go
- model_lifecycle.go
- model_pty_config.go
- model_pty_lifecycle.go
- model_pty_reader.go
- model_pty_status.go
- model_pty_status_chat_cursor.go
- model_pty_status_cursor_geometry.go
- model_pty_status_cursor_glyph.go
- model_pty_status_recent_input.go
- model_pty_status_sanitize.go
- model_pty_trace.go
- model_render.go
- model_render_tabbar.go
- model_scrolled_history.go
- model_tab.go
- model_tab_phase.go
- model_tabs.go
- model_tabs_actions.go
- model_tabs_limit.go
- model_tabs_restore.go
- model_tabs_session.go
- model_tabs_session_reattach.go
- model_tabs_viewer.go
- model_wheel.go
- model_workspace_rebind.go
- tab_actor.go
- tab_actor_selection.go
- tab_actor_write.go