vterm

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: 8 Imported by: 0

Documentation

Overview

Package vterm is a terminal emulator: it parses a stream of ANSI/VT escape sequences into a cell grid plus scrollback and renders that state back to ANSI. It is the source of truth for what a hosted agent's terminal looks like, feeding the compositor and the center/sidebar UI models.

Index

Constants

View Source
const MaxScrollback = 10000
View Source
const SyncStallTimeout = 2 * time.Second

SyncStallTimeout bounds how long a DEC 2026 sync-begin may freeze rendering without its matching sync-end. A well-behaved writer closes a sync region within one frame; if the end marker never arrives (writer died mid-frame, output trimmed under overflow), the frozen snapshot must not persist forever. Write, RenderBuffers, and Version check the deadline, so the terminal recovers on the next output, rendered frame, or snapshot-cache key check, whichever comes first.

Variables

This section is empty.

Functions

func ContainsSGRParam added in v0.0.20

func ContainsSGRParam(s string, target int) bool

ContainsSGRParam reports whether s contains an SGR escape sequence (CSI ... m) carrying the given numeric parameter. vterm owns ANSI/SGR scanning, so tests in any package that renders via vterm can share this instead of re-deriving it.

func NormalizeSelectionRange added in v0.0.20

func NormalizeSelectionRange(startX, startY, endX, endY int) (int, int, int, int)

NormalizeSelectionRange orders the two endpoints so that (startX, startY) comes before (endX, endY) in reading order (top-to-bottom, left-to-right on the same row) and returns them. Coordinates may be in any consistent space (screen-relative or absolute lines) as long as both endpoints use the same.

func RenderableRune added in v0.0.20

func RenderableRune(r rune) rune

RenderableRune substitutes a NUL rune (an unwritten cell) with a space so it renders as blank rather than a control character. Other runes pass through unchanged. Shared by the live renderer and the compositor render paths.

func SelectionContains added in v0.0.20

func SelectionContains(startX, startY, endX, endY, x, y int) bool

SelectionContains reports whether cell (x, y) falls inside the linear selection bounded by (startX, startY)..(endX, endY). The bounds MUST already be normalized (see NormalizeSelectionRange) so start precedes end. The first row of the range starts at startX, the last row ends at endX, and any rows in between are fully selected. This is the rectangular-selection-free contains test shared by the live renderer and the compositor snapshot paths.

func VTermHasScrollback added in v0.0.20

func VTermHasScrollback(v *VTerm) bool

VTermHasScrollback reports whether v is non-nil and currently has scrollback the viewport can move into. It is the shared leaf used by the per-pane CanConsumeWheel checks to avoid hover-wheel focus steals from panes with no scrollable history.

Types

type Cell

type Cell struct {
	Rune  rune
	Style Style
	Width int // 1 normal, 2 wide, 0 continuation
	// GraphemeCluster, when non-empty, is the full grapheme (base rune plus
	// combining marks) for this cell. Empty means "use Rune". Readers that emit
	// text should prefer it; width/layout logic still uses Rune + Width.
	GraphemeCluster string
}

Cell represents a single character cell

func CopyLine

func CopyLine(src []Cell) []Cell

CopyLine deep copies a line

func DefaultCell

func DefaultCell() Cell

DefaultCell returns a blank cell

func MakeBlankLine

func MakeBlankLine(width int) []Cell

MakeBlankLine creates a blank line

type Color

type Color struct {
	Type  ColorType
	Value uint32 // Indexed: 0-255, RGB: 0xRRGGBB
}

Color represents a terminal color

type ColorType

type ColorType uint8
const (
	ColorDefault ColorType = iota
	ColorIndexed
	ColorRGB
)

type CursorRenderState added in v0.0.20

type CursorRenderState struct {
	X, Y       int
	ShowCursor bool
	Hidden     bool
}

CursorRenderState is the cached cursor state from the previous render frame, used to detect cursor-only changes and mark the affected lines dirty.

type PaneModeState added in v0.0.19

type PaneModeState struct {
	HasState bool
	// PreserveExistingState keeps the current terminal mode state when a pane
	// snapshot did not include authoritative tmux mode fields.
	PreserveExistingState bool
	AltScreen             bool
	OriginMode            bool
	CursorHidden          bool
	ScrollTop             int
	ScrollBottom          int
	HasAltSavedCursor     bool
	AltSavedCursorX       int
	AltSavedCursorY       int
}

PaneModeState describes the VT mode state that should accompany an authoritative tmux pane snapshot.

type Parser

type Parser struct {
	// contains filtered or unexported fields
}

Parser handles ANSI escape sequence parsing

func NewParser

func NewParser(vt *VTerm) *Parser

NewParser creates a new parser for the given VTerm

func (*Parser) CarryState added in v0.0.17

func (p *Parser) CarryState() ParserCarryState

func (*Parser) Parse

func (p *Parser) Parse(data []byte)

Parse processes bytes from PTY output

func (*Parser) Reset added in v0.0.17

func (p *Parser) Reset()

type ParserCarryMode added in v0.0.17

type ParserCarryMode uint8
const (
	ParserCarryText ParserCarryMode = iota
	ParserCarryEscape
	ParserCarryCSI
	ParserCarryCSIParam
	ParserCarryOSC
	ParserCarryDCS
	ParserCarryCharset
	ParserCarryOSCEscape
	ParserCarryDCSEscape
)

type ParserCarryState added in v0.0.17

type ParserCarryState struct {
	Mode          ParserCarryMode
	UTF8Remaining int
}

func AdvanceParserCarryState added in v0.0.17

func AdvanceParserCarryState(seed ParserCarryState, data []byte) ParserCarryState

AdvanceParserCarryState models parser continuity across chunk boundaries. It is the single shared state machine for this: PTY overflow trimming (internal/ui/ptyio) and speculative actor carry previews both consume it directly rather than maintaining copies.

type ResponseWriter

type ResponseWriter func([]byte)

ResponseWriter is called when the terminal needs to send a response back to the PTY

type Style

type Style struct {
	Fg        Color
	Bg        Color
	Bold      bool
	Dim       bool
	Italic    bool
	Underline bool
	Blink     bool
	Reverse   bool
	Hidden    bool
	Strike    bool
}

Style holds text styling attributes

func SuppressBlankUnderline added in v0.0.20

func SuppressBlankUnderline(r rune, style Style) Style

SuppressBlankUnderline drops the underline attribute when rune r is blank (NUL or space). Some TUIs leave underline enabled while clearing rows; underline on spaces renders as scanlines, so it is removed for blank cells at render time. The rule is shared by the live renderer and both compositor render paths so they stay pixel-identical.

func (Style) ANSI added in v0.0.20

func (s Style) ANSI() string

ANSI converts a Style to a full ANSI escape sequence, leading with a reset. Optimized to avoid allocations using strings.Builder.

func (Style) DeltaANSI added in v0.0.20

func (s Style) DeltaANSI(next Style) string

DeltaANSI returns the minimal SGR escape sequence to transition from the receiver (the previous style) to next. It avoids the overhead of always emitting a full reset, and returns "" when nothing changed. Optimized to avoid allocations using strings.Builder.

type TerminalSnapshot added in v0.0.20

type TerminalSnapshot struct {
	// Data is the raw ANSI capture (e.g. tmux capture-pane output).
	Data []byte
	// Cols/Rows are the dimensions the capture was taken at; zero values fall
	// back to the terminal's current size.
	Cols, Rows int
	// CursorX/CursorY position the cursor after a full load when HasCursor is
	// set.
	CursorX, CursorY int
	HasCursor        bool
	// ModeState carries alt-screen/origin/cursor-visibility/scroll-region
	// state, with the explicit missing-state policy described above.
	ModeState PaneModeState
}

TerminalSnapshot is a captured pane image plus the metadata needed to replay it into a VTerm: the raw capture bytes, the geometry the capture was taken at, the cursor, and the pane mode state.

It is the unified entry point for the capture-load paths. The historical per-shape variants (PrependScrollbackWithSize, LoadPaneCaptureWithCursorAndModes, AppendScrollbackDeltaWithSize) remain as the implementation, but new callers should construct a TerminalSnapshot and use LoadSnapshot / AppendDelta / PrependHistory.

Missing mode state is an explicit decision, never a silent default: when ModeState.HasState is false, ModeState.PreserveExistingState selects between keeping the terminal's current modes (partial captures) and resetting them to defaults (authoritative captures). Builders of a TerminalSnapshot must set it deliberately.

type VTerm

type VTerm struct {
	// Screen buffer (visible area)
	Screen [][]Cell

	// Scrollback buffer (oldest at index 0)
	Scrollback [][]Cell

	// Cursor position (0-indexed)
	CursorX, CursorY int

	// Dimensions
	Width, Height int

	// Scroll viewing position (0 = live, >0 = lines scrolled up)
	ViewOffset int

	// Alt screen mode (full-screen TUI applications).
	AltScreen bool
	// AllowAltScreenScrollback keeps scrollback active even in alt screen.
	// Useful for tmux-backed sessions where scrollback should remain available.
	AllowAltScreenScrollback bool

	// Scrolling region (for DECSTBM)
	ScrollTop    int
	ScrollBottom int
	// Origin mode (DECOM) - cursor positions are relative to scroll region.
	OriginMode bool

	// Current style for new characters
	CurrentStyle Style

	// Saved cursor state (for DECSC/DECRC)
	SavedCursorX int
	SavedCursorY int
	SavedStyle   Style

	// Cursor visibility (controlled externally when pane is focused)
	ShowCursor bool

	// CursorHidden tracks if terminal app hid cursor via DECTCEM (mode 25)
	CursorHidden bool

	// IgnoreCursorVisibilityControls ignores DECTCEM mode 25 hide/show toggles.
	// Used by chat-style tabs that render a steady cursor independent of app output.
	IgnoreCursorVisibilityControls bool
	// TreatLFAsCRLF makes bare LF advance to the next line and return to column 0.
	// This is useful for some chat agents that emit LF-only streams.
	TreatLFAsCRLF bool
	// CaptureNormalScreenOnClear preserves chat-style full-screen redraw frames
	// that use normal-screen CSI 2J/3J instead of the alternate screen.
	CaptureNormalScreenOnClear bool
	// contains filtered or unexported fields
}

VTerm is a virtual terminal emulator with scrollback support.

Synchronization contract: VTerm has no internal mutex. All callers must provide external synchronization. In practice, every call site (WriteToTerminal, SidebarPTYFlush, and TerminalLayer snapshot creation) holds TerminalState.mu for the duration of the operation. Snapshot-based rendering (TerminalLayer) copies data under the lock and then renders the immutable snapshot without locks.

func New

func New(width, height int) *VTerm

New creates a new VTerm with the given dimensions

func (*VTerm) AbsoluteLineToScreenY added in v0.0.4

func (v *VTerm) AbsoluteLineToScreenY(absLine int) int

AbsoluteLineToScreenY converts an absolute line number to a screen Y coordinate. Returns -1 if the line is not currently visible.

func (*VTerm) AppendDelta added in v0.0.20

func (v *VTerm) AppendDelta(snap TerminalSnapshot, visibleHistoryRows int)

AppendDelta appends the snapshot rows that are missing from the current buffers (post-attach growth). visibleHistoryRows is the number of history rows that a preceding resize folded back into the visible screen, so they are not appended twice.

func (*VTerm) AppendScrollbackDelta added in v0.0.19

func (v *VTerm) AppendScrollbackDelta(data []byte)

AppendScrollbackDelta appends only the missing suffix from a newer tmux history capture. This is used after a pre-attach full-pane snapshot so rows that scrolled into history during the snapshot->attach gap are preserved without replacing the restored visible frame.

func (*VTerm) AppendScrollbackDeltaWithSize added in v0.0.19

func (v *VTerm) AppendScrollbackDeltaWithSize(data []byte, width, height, visibleHistoryRows int)

AppendScrollbackDeltaWithSize appends the missing suffix from a newer tmux history capture parsed at the tmux geometry that produced it. When the local viewport has changed since capture time, visibleHistoryRows indicates how many of the newest captured rows are already visible after the restore/resize and should not be appended back into scrollback.

func (*VTerm) ClearDirty

func (v *VTerm) ClearDirty()

ClearDirty resets dirty tracking state after a render.

func (*VTerm) ClearDirtyWithCursor

func (v *VTerm) ClearDirtyWithCursor(showCursor bool)

ClearDirtyWithCursor resets dirty tracking state and updates cursor tracking. This should be called after snapshotting to track cursor position changes.

func (*VTerm) ClearSelection

func (v *VTerm) ClearSelection()

ClearSelection clears the current selection

func (*VTerm) CursorHiddenForRender added in v0.0.14

func (v *VTerm) CursorHiddenForRender() bool

CursorHiddenForRender returns the effective cursor-hidden state for rendering.

func (*VTerm) DirtyLines

func (v *VTerm) DirtyLines() ([]bool, bool)

DirtyLines returns the dirty line flags and whether all lines are dirty. This is used by VTermLayer for optimized rendering. The returned slice is reused between calls; callers must not retain it across mutations.

func (*VTerm) GetScrollInfo

func (v *VTerm) GetScrollInfo() (int, int)

GetScrollInfo returns (current offset, max offset)

func (*VTerm) GetSelectedText

func (v *VTerm) GetSelectedText(startX, startLine, endX, endLine int) string

GetSelectedText extracts text from the current selection. Returns empty string if no selection is active.

func (*VTerm) GetTextRange

func (v *VTerm) GetTextRange(startX, startLine, endX, endLine int) string

GetTextRange extracts text from a range in the combined scrollback+screen buffer. Coordinates are absolute line indices (0-based) and columns.

func (*VTerm) HasSelection added in v0.0.4

func (v *VTerm) HasSelection() bool

HasSelection returns true if there is an active selection.

func (*VTerm) IsInSelection

func (v *VTerm) IsInSelection(x, screenY int) bool

IsInSelection checks if coordinate (x, screenY) is within the selection. screenY is a screen-relative coordinate (0 to Height-1).

func (*VTerm) IsScrolled

func (v *VTerm) IsScrolled() bool

IsScrolled returns true if viewing scrollback

func (*VTerm) LastCursorState added in v0.0.20

func (v *VTerm) LastCursorState() CursorRenderState

LastCursorState returns the cursor position and visibility from the previous render frame.

func (*VTerm) LineCells

func (v *VTerm) LineCells(line int) []Cell

LineCells returns the cell slice for an absolute line index in scrollback+screen.

func (*VTerm) LoadPaneCapture added in v0.0.19

func (v *VTerm) LoadPaneCapture(data []byte)

LoadPaneCapture replaces the terminal screen + scrollback with a full tmux pane capture (scrollback plus the current visible screen). This seeds a reattached client with the latest frame before live PTY output resumes.

func (*VTerm) LoadPaneCaptureWithCursorAndModes added in v0.0.19

func (v *VTerm) LoadPaneCaptureWithCursorAndModes(
	data []byte,
	cursorX, cursorY int,
	hasCursor bool,
	modeState PaneModeState,
)

LoadPaneCaptureWithCursorAndModes replaces the terminal screen + scrollback with a full tmux pane capture and applies the accompanying tmux VT mode state when one is available.

func (*VTerm) LoadSnapshot added in v0.0.20

func (v *VTerm) LoadSnapshot(snap TerminalSnapshot)

LoadSnapshot replaces the terminal's screen and scrollback with the snapshot content, applying cursor and mode state.

func (*VTerm) MaxViewOffset

func (v *VTerm) MaxViewOffset() int

MaxViewOffset returns the maximum scrollback offset for the current buffers. Used by the sidebar/center wheel handlers to decide whether scrollback exists.

func (*VTerm) MouseReportingEnabled added in v0.0.20

func (v *VTerm) MouseReportingEnabled() bool

MouseReportingEnabled reports whether the hosted terminal application has requested mouse event reporting.

func (*VTerm) MouseSGRMode added in v0.0.20

func (v *VTerm) MouseSGRMode() bool

MouseSGRMode reports whether SGR extended mouse coordinates are enabled.

func (*VTerm) NoteSyncViewportInteraction added in v0.0.20

func (v *VTerm) NoteSyncViewportInteraction()

NoteSyncViewportInteraction records a viewport interaction during synchronized output. Scrolled into history, the frozen viewport stays anchored when the sync ends; back at the live view, the anchor is released and recorded hidden growth is discarded. This is an explicit API called by the UI scroll paths (mousewheel/keys) rather than a hidden side effect of the viewport math: programmatic ViewOffset changes do not touch the anchor unless their call site opts in.

func (*VTerm) ParserCarryState added in v0.0.17

func (v *VTerm) ParserCarryState() ParserCarryState

ParserCarryState reports any in-flight parser state from previously flushed PTY bytes. Callers must provide external synchronization.

func (*VTerm) PrependHistory added in v0.0.20

func (v *VTerm) PrependHistory(snap TerminalSnapshot)

PrependHistory inserts the snapshot rows above the existing scrollback (older captured history fetched after the live view was already populated).

func (*VTerm) PrependScrollback added in v0.0.9

func (v *VTerm) PrependScrollback(data []byte)

PrependScrollback parses captured scrollback content (with ANSI escapes) and prepends the resulting lines to the scrollback buffer. This is used to populate scrollback history when attaching to an existing tmux session. It is a no-op if data is empty.

func (*VTerm) PrependScrollbackWithSize added in v0.0.19

func (v *VTerm) PrependScrollbackWithSize(data []byte, width, height int)

PrependScrollbackWithSize parses captured scrollback content using the cell geometry that tmux had when it produced the capture, then prepends the resulting lines to the scrollback buffer.

func (*VTerm) Render

func (v *VTerm) Render() string

Render returns the terminal content as a string with ANSI codes

func (*VTerm) RenderAndClear added in v0.0.20

func (v *VTerm) RenderAndClear() string

RenderAndClear renders the terminal and marks the rendered state clean in one step, so callers cannot forget the clear that keeps the cache coherent.

func (*VTerm) RenderBuffers

func (v *VTerm) RenderBuffers() ([][]Cell, int)

RenderBuffers returns the current screen buffer and scrollback length. During synchronized output, it returns the frozen snapshot. As a failsafe it force-releases a sync region whose end marker is overdue (SyncStallTimeout) so a writer that died mid-frame cannot freeze the terminal forever.

func (*VTerm) ResetParserState added in v0.0.17

func (v *VTerm) ResetParserState()

ResetParserState clears any carried parser state after buffered PTY bytes are forcibly discarded. Callers must provide external synchronization.

func (*VTerm) Resize

func (v *VTerm) Resize(width, height int)

Resize handles terminal resize.

func (*VTerm) ResizeWithoutHistoryReveal added in v0.0.19

func (v *VTerm) ResizeWithoutHistoryReveal(width, height int)

ResizeWithoutHistoryReveal resizes the terminal without pulling rows out of scrollback to fill newly revealed viewport space.

func (*VTerm) ScreenYToAbsoluteLine added in v0.0.4

func (v *VTerm) ScreenYToAbsoluteLine(screenY int) int

ScreenYToAbsoluteLine converts a screen Y coordinate (0 to Height-1) to an absolute line number. Absolute line 0 is the first line in scrollback.

func (*VTerm) ScrollView

func (v *VTerm) ScrollView(delta int)

ScrollView scrolls the view by delta lines (positive = up into history)

func (*VTerm) ScrollViewAndNote added in v0.0.20

func (v *VTerm) ScrollViewAndNote(delta int)

ScrollViewAndNote scrolls the view by delta lines and then records the resulting viewport interaction, exactly as if the caller had written ScrollView followed by NoteSyncViewportInteraction. UI scroll paths that move the viewport in response to user input (mousewheel, drag-select edge scroll, PgUp/PgDown) pair these two calls; this helper keeps that pairing in one place so the anchor bookkeeping cannot drift between call sites.

func (*VTerm) ScrollViewTo

func (v *VTerm) ScrollViewTo(offset int)

ScrollViewTo sets absolute scroll position

func (*VTerm) ScrollViewToBottom

func (v *VTerm) ScrollViewToBottom()

ScrollViewToBottom returns to live view

func (*VTerm) ScrollViewToTop

func (v *VTerm) ScrollViewToTop()

ScrollViewToTop scrolls to oldest content

func (*VTerm) SelActive

func (v *VTerm) SelActive() bool

SelActive reports whether a selection is active.

func (*VTerm) SelEndLine added in v0.0.4

func (v *VTerm) SelEndLine() int

SelEndLine returns the selection end line (absolute line number).

func (*VTerm) SelEndX

func (v *VTerm) SelEndX() int

SelEndX returns the selection end X.

func (*VTerm) SelEndY

func (v *VTerm) SelEndY() int

SelEndY returns the selection end Y in screen coordinates. Returns -1 if the end line is not visible.

func (*VTerm) SelStartLine added in v0.0.4

func (v *VTerm) SelStartLine() int

SelStartLine returns the selection start line (absolute line number).

func (*VTerm) SelStartX

func (v *VTerm) SelStartX() int

SelStartX returns the selection start X.

func (*VTerm) SelStartY

func (v *VTerm) SelStartY() int

SelStartY returns the selection start Y in screen coordinates. Returns -1 if the start line is not visible.

func (*VTerm) SelectedText added in v0.0.18

func (v *VTerm) SelectedText() string

SelectedText returns text from the stored selection coordinates. It does not check selActive so callers that hold their own Active flag (e.g. common.SelectionState) can still copy after a concurrent ClearSelection.

func (*VTerm) SetResponseWriter

func (v *VTerm) SetResponseWriter(w ResponseWriter)

SetResponseWriter sets the callback for terminal query responses

func (*VTerm) SetSelection

func (v *VTerm) SetSelection(startX, startLine, endX, endLine int, active, rect bool)

SetSelection stores selection coordinates for rendering with highlight. startLine and endLine are absolute line numbers (0 = first scrollback line).

func (*VTerm) SyncActive

func (v *VTerm) SyncActive() bool

SyncActive reports whether synchronized output is currently active.

func (*VTerm) TakePendingClipboard added in v0.0.20

func (v *VTerm) TakePendingClipboard() []byte

TakePendingClipboard returns and clears any clipboard payload captured from an OSC 52 write. Returns nil when none is pending.

func (*VTerm) Title added in v0.0.20

func (v *VTerm) Title() string

Title returns the most recent window/tab title reported via OSC 0/1/2.

func (*VTerm) Version

func (v *VTerm) Version() uint64

Version returns the current version counter. This increments whenever visible content changes.

func (*VTerm) VisibleScreen

func (v *VTerm) VisibleScreen() [][]Cell

VisibleScreen returns the currently visible screen buffer as a copy.

func (*VTerm) VisibleScreenInto added in v0.0.12

func (v *VTerm) VisibleScreenInto(dst [][]Cell) [][]Cell

VisibleScreenInto returns the currently visible screen buffer, reusing dst when possible. dst is resized as needed and its lines are reused to reduce allocations.

func (*VTerm) VisibleScreenWithSelection

func (v *VTerm) VisibleScreenWithSelection() [][]Cell

VisibleScreenWithSelection returns the visible screen with selection highlighting applied.

func (*VTerm) WorkingDir added in v0.0.20

func (v *VTerm) WorkingDir() string

WorkingDir returns the most recent working directory reported via OSC 7 (raw payload, e.g. "file://host/path").

func (*VTerm) Write

func (v *VTerm) Write(data []byte)

Write processes input bytes from PTY

Jump to

Keyboard shortcuts

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