center

package
v0.0.20 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: MIT Imports: 35 Imported by: 0

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:

  1. 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.

  2. 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

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type DetachedTabInfo added in v0.0.14

type DetachedTabInfo struct {
	WorkspaceID string
	TabID       TabID
}

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 New

func New(cfg *config.Config) *Model

New creates a new center pane model.

func (*Model) ActiveTerminalStatusLine

func (m *Model) ActiveTerminalStatusLine() string

ActiveTerminalStatusLine returns the status line for the active terminal.

func (*Model) AddTab

func (m *Model) AddTab(tab *Tab)

AddTab appends a tab to the model. Used by harness/test code that builds tabs manually.

func (*Model) AddTabsFromWorkspace added in v0.0.9

func (m *Model) AddTabsFromWorkspace(ws *data.Workspace, tabs []data.TabInfo) tea.Cmd

AddTabsFromWorkspace adds new tabs without resetting existing UI state.

func (*Model) Blur

func (m *Model) Blur()

Blur removes focus.

func (*Model) CanConsumeWheel added in v0.0.18

func (m *Model) CanConsumeWheel() bool

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

func (m *Model) CleanupWorkspace(ws *data.Workspace)

CleanupWorkspace removes all tabs and state for a deleted workspace

func (*Model) Close

func (m *Model) Close()

Close cleans up all resources.

func (*Model) CloseActiveTab

func (m *Model) CloseActiveTab() tea.Cmd

CloseActiveTab closes the current tab (public wrapper)

func (*Model) ContentWidth

func (m *Model) ContentWidth() int

ContentWidth returns the content width inside the pane.

func (*Model) DetachActiveTab added in v0.0.9

func (m *Model) DetachActiveTab() tea.Cmd

DetachActiveTab closes the PTY client but keeps the tmux session alive.

func (*Model) DetachTabByID added in v0.0.9

func (m *Model) DetachTabByID(wsID string, tabID TabID) tea.Cmd

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) Focus

func (m *Model) Focus()

Focus sets the focus state.

func (*Model) Focused

func (m *Model) Focused() bool

Focused returns whether the center pane is focused.

func (*Model) FocusedAgentTitle added in v0.0.20

func (m *Model) FocusedAgentTitle() string

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

func (m *Model) GetActiveWorkspaceIDs() []string

GetActiveWorkspaceIDs returns all workspace IDs with active agents.

func (*Model) GetActiveWorkspaceRoots added in v0.0.5

func (m *Model) GetActiveWorkspaceRoots() []string

GetActiveWorkspaceRoots returns all workspace root paths with active agents.

func (*Model) GetRunningWorkspaceRoots added in v0.0.5

func (m *Model) GetRunningWorkspaceRoots() []string

GetRunningWorkspaceRoots returns all workspace root paths with running agents. This includes agents that are running but idle (waiting at prompt).

func (*Model) GetTabsInfo

func (m *Model) GetTabsInfo() ([]data.TabInfo, int)

GetTabsInfo returns information about current tabs for persistence

func (*Model) GetTabsInfoForWorkspace added in v0.0.9

func (m *Model) GetTabsInfoForWorkspace(wsID string) ([]data.TabInfo, int)

GetTabsInfoForWorkspace returns tab information for a specific workspace ID.

func (*Model) HasActiveAgents

func (m *Model) HasActiveAgents() bool

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

func (m *Model) HasActiveAgentsInWorkspace(wsID string) bool

HasActiveAgentsInWorkspace returns whether any tab in a workspace is actively outputting.

func (*Model) HasActiveTerminal added in v0.0.20

func (m *Model) HasActiveTerminal() bool

HasActiveTerminal reports whether the active tab has a terminal viewport.

func (*Model) HasDiffViewer added in v0.0.3

func (m *Model) HasDiffViewer() bool

HasDiffViewer returns true if the active tab has a diff viewer.

func (*Model) HasRunningAgents

func (m *Model) HasRunningAgents() bool

HasRunningAgents returns whether any tab has an active agent across workspaces.

func (*Model) HasTabs

func (m *Model) HasTabs() bool

HasTabs returns whether there are any tabs for the current workspace.

func (*Model) HasWorkspaceState added in v0.0.14

func (m *Model) HasWorkspaceState(wsID string) bool

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

func (m *Model) HelpBuildCount() uint64

HelpBuildCount reports how many times HelpLines has been invoked. Test instrumentation for the compose-time skip gate; not for production use.

func (*Model) HelpLines

func (m *Model) HelpLines(width int) []string

HelpLines returns the help lines for the given width, respecting visibility.

func (*Model) HelpVersion added in v0.0.20

func (m *Model) HelpVersion() uint64

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) Init

func (m *Model) Init() tea.Cmd

Init initializes the center pane.

func (*Model) IsTabActive added in v0.0.5

func (m *Model) IsTabActive(tab *Tab) bool

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) NextTab

func (m *Model) NextTab() tea.Cmd

NextTab switches to the next tab (public wrapper)

func (*Model) PrevTab

func (m *Model) PrevTab() tea.Cmd

PrevTab switches to the previous tab (public wrapper)

func (*Model) ReattachActiveTab added in v0.0.9

func (m *Model) ReattachActiveTab() tea.Cmd

ReattachActiveTab reattaches to a detached/stopped tmux session.

func (*Model) ReattachActiveTabIfDetached added in v0.0.14

func (m *Model) ReattachActiveTabIfDetached() tea.Cmd

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

func (m *Model) RebindWorkspaceID(previous, current *data.Workspace) tea.Cmd

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

func (m *Model) RestartActiveTab() tea.Cmd

RestartActiveTab restarts a stopped or detached agent tab by creating a fresh tmux client.

func (*Model) RestoreTabsFromWorkspace added in v0.0.9

func (m *Model) RestoreTabsFromWorkspace(ws *data.Workspace) tea.Cmd

RestoreTabsFromWorkspace recreates tabs from persisted workspace metadata. Only agent tabs with known assistants are restored.

func (*Model) RunTabActor added in v0.0.5

func (m *Model) RunTabActor(ctx context.Context) error

func (*Model) ScrollActiveTerminalPage added in v0.0.20

func (m *Model) ScrollActiveTerminalPage(direction int)

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) SelectTab

func (m *Model) SelectTab(index int) tea.Cmd

SelectTab switches to a specific tab by index (0-indexed)

func (*Model) SendToTerminal

func (m *Model) SendToTerminal(s string)

SendToTerminal sends a string directly to the active terminal

func (*Model) SetCanFocusRight

func (m *Model) SetCanFocusRight(can bool)

SetCanFocusRight controls whether focus-right hints should be shown.

func (*Model) SetInstanceID added in v0.0.9

func (m *Model) SetInstanceID(id string)

SetInstanceID sets the tmux instance tag for sessions created by this model.

func (*Model) SetMsgSink added in v0.0.4

func (m *Model) SetMsgSink(sink func(tea.Msg))

SetMsgSink sets a callback for PTY messages.

func (*Model) SetMsgSinkTry added in v0.0.17

func (m *Model) SetMsgSinkTry(sink func(tea.Msg) bool)

SetMsgSinkTry sets a callback for PTY messages that reports whether enqueue succeeded.

func (*Model) SetOffset

func (m *Model) SetOffset(x int)

SetOffset sets the X offset of the pane from screen left (for mouse coordinate conversion).

func (*Model) SetShowKeymapHints

func (m *Model) SetShowKeymapHints(show bool)

SetShowKeymapHints controls whether helper text is rendered.

func (*Model) SetSize

func (m *Model) SetSize(width, height int)

SetSize sets the center pane size.

func (*Model) SetStyles

func (m *Model) SetStyles(styles common.Styles)

SetStyles updates the component's styles (for theme changes).

func (*Model) SetTmuxOptions added in v0.0.20

func (m *Model) SetTmuxOptions(opts tmux.Options)

SetTmuxOptions stores the resolved tmux options and forwards them to the agent manager.

func (*Model) SetWorkspace added in v0.0.5

func (m *Model) SetWorkspace(ws *data.Workspace)

SetWorkspace sets the active workspace.

func (*Model) StartPTYReaders

func (m *Model) StartPTYReaders() tea.Cmd

StartPTYReaders starts reading from all PTYs across all workspaces

func (*Model) TabBarView

func (m *Model) TabBarView() string

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

func (m *Model) TerminalViewport() (x, y, width, height int)

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) Update

func (m *Model) Update(msg tea.Msg) (*Model, tea.Cmd)

Update handles messages

func (*Model) View

func (m *Model) View() string

View renders the center pane

func (*Model) ViewChromeOnly

func (m *Model) ViewChromeOnly() string

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

type PTYCursorRefresh struct {
	WorkspaceID string
	TabID       TabID
	Gen         uint64
}

PTYCursorRefresh re-renders chat cursor policy when time-based windows expire.

type PTYFlush

type PTYFlush struct {
	WorkspaceID string
	TabID       TabID
	CatchUp     bool
}

PTYFlush applies buffered PTY output for a tab.

type PTYOutput

type PTYOutput struct {
	WorkspaceID string
	TabID       TabID
	Data        []byte
}

PTYOutput is a message containing PTY output data

type PTYRestart added in v0.0.5

type PTYRestart struct {
	WorkspaceID string
	TabID       TabID
}

PTYRestart requests restarting a PTY reader for a tab.

type PTYStopped

type PTYStopped struct {
	WorkspaceID string
	TabID       TabID
	Err         error
}

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

func (t *Tab) WriteToTerminal(data []byte)

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 TabInputFailed struct {
	TabID       TabID
	WorkspaceID string
	Err         error
}

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL