tui

package
v0.25.0 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: MIT Imports: 36 Imported by: 0

Documentation

Overview

Package tui is a small terminal UI: raw-mode input, differential line renderer, multi-line editor, and chat view. No external TUI framework; just ANSI escape codes.

Index

Constants

View Source
const (
	SeqQueryColorScheme = "\x1b[?996n"
	SeqQueryMode2031    = "\x1b[?2031$p"
	SeqEnableMode2031   = "\x1b[?2031h"
	SeqDisableMode2031  = "\x1b[?2031l"
)

Appearance query and DEC mode 2031 control sequences.

View Source
const (
	InputStylePlain = "plain"
	InputStyleLines = "lines"
	InputStyleBlock = "block"

	StatusPositionAboveInput = "above_input"
	StatusPositionBelowInput = "below_input"

	WorkingPositionAboveInput = "above_input"
	WorkingPositionBelowInput = "below_input"

	SubagentPositionAboveInput = "above_input"
	SubagentPositionBelowInput = "below_input"
)
View Source
const (
	// RightBarSeparatorWidth is the one-cell divider between the main pane
	// and the host-owned right bar.
	RightBarSeparatorWidth = 1
	// RightBarMinWidth keeps widget titles and checklist markers readable.
	RightBarMinWidth = 24
	// RightBarMaxWidth prevents a long extension line from taking most of
	// the transcript on wide terminals.
	RightBarMaxWidth = 36
	// RightBarMinMainWidth preserves enough room for the editor and chat.
	RightBarMinMainWidth = 48
)
View Source
const (
	SeqHideCursor  = "\x1b[?25l"
	SeqShowCursor  = "\x1b[?25h"
	SeqClearScreen = "\x1b[2J\x1b[H"
	// SeqClearScreenNoHome is SeqClearScreen without the trailing
	// cursor-home (\x1b[H). Use it whenever a MoveTo(...) follows
	// immediately. VS Code's integrated terminal interprets a bare
	// CUP-no-args during a clear as "snap the viewport scrollbar to
	// row 0", which makes the user's scroll position jump on every
	// repaint. The explicit MoveTo we emit afterwards positions the
	// cursor identically without triggering that snap.
	SeqClearScreenNoHome = "\x1b[2J"
	SeqClearScrollback   = "\x1b[3J"
	// SeqCursorHome moves to the top-left of the visible viewport.
	// SeqClearToEnd erases from the cursor to the end of the screen
	// without scrolling content into scrollback (unlike \x1b[2J on
	// xterm.js). Together they clear the visible frame in place, which
	// the VS Code terminal needs for duplicate-free full repaints.
	SeqCursorHome        = "\x1b[H"
	SeqClearToEnd        = "\x1b[0J"
	SeqClearLine         = "\x1b[2K"
	SeqResetScrollRegion = "\x1b[r"
	SeqDeleteKittyImages = "\x1b_Ga=d\x1b\\"
	SeqBracketedPasteOn  = "\x1b[?2004h"
	SeqBracketedPasteOff = "\x1b[?2004l"
	// Request enhanced keyboard reporting where supported. Kitty-style
	// keyboard protocol covers Ghostty, Kitty, VS Code's integrated
	// terminal, and recent xterm.js builds. Xterm modifyOtherKeys is a
	// useful fallback for terminals/tmux configurations that expose
	// modified Enter as CSI 27;<mod>;<code>~.
	SeqEnhancedKeyboardOn  = "\x1b[>1u\x1b[>4;2m"
	SeqEnhancedKeyboardOff = "\x1b[<u\x1b[>4m"
	// Basic mouse tracking + SGR extended coordinates. Used only
	// when explicitly enabled by the interactive mode (currently VS
	// Code terminal) so terminals with good native scrolling, like
	// Ghostty, are left alone.
	SeqMouseOn         = "\x1b[?1000h\x1b[?1006h"
	SeqMouseOff        = "\x1b[?1000l\x1b[?1006l"
	SeqAltScreenOn     = "\x1b[?1049h"
	SeqAltScreenOff    = "\x1b[?1049l"
	SeqSynchronizedOn  = "\x1b[?2026h"
	SeqSynchronizedOff = "\x1b[?2026l"
	// SeqSaveCursor / SeqRestoreCursor use DECSC/DECRC. All terminals
	// we target adjust the saved row when natural scrolling occurs, so
	// these survive scroll-on-write inside the bottom band.
	SeqSaveCursor    = "\x1b7"
	SeqRestoreCursor = "\x1b8"
	// SeqEraseToEnd erases from the cursor to the end of the screen.
	SeqEraseToEnd = "\x1b[J"
)

HideCursor, ShowCursor, ClearScreen, BracketedPasteOn/Off, etc.

View Source
const (
	ToolCollapsePreview = 10
	ToolCollapseLines   = 12
)

ToolCollapsePreview is the number of lines shown before a long tool result is replaced with a "... ctrl+o to expand" footer. Tool results shorter than ToolCollapseLines always render in full.

View Source
const FlushLeftSentinel = '\x1c'

FlushLeftSentinel was used previously to opt fenced code blocks out of the prose indent. The current rendering keeps fences aligned with surrounding prose, so the sentinel is no longer emitted; the constant is kept (and exported) so any older caller that still strips it remains a harmless no-op.

Variables

View Source
var Dark = Theme{
	FG:                Color256(253),
	Muted:             Color256(244),
	Accent:            Color256(111),
	User:              Color256(180),
	UserBubbleBG:      ColorRGB(66, 69, 75),
	UserBubbleFG:      Color256(248),
	Assistant:         Color256(117),
	Tool:              Color256(114),
	ToolOut:           Color256(245),
	Error:             Color256(203),
	Warning:           Color256(214),
	Spinner:           Color256(183),
	ThinkingMax:       Color256(207),
	SelectionBG:       Color256(24),
	SelectionFG:       Color256(231),
	SpinnerFrames:     defaultSpinnerFrames,
	SpinnerIntervalMS: 80,
	SyntaxBaseStyle:   "monokai",
	Syntax:            nordSyntax,
}
View Source
var Light = Theme{
	FG:                Color256(236),
	Muted:             Color256(244),
	Accent:            Color256(33),
	User:              Color256(94),
	UserBubbleBG:      Color256(254),
	UserBubbleFG:      Color256(240),
	Assistant:         Color256(31),
	Tool:              Color256(28),
	ToolOut:           Color256(240),
	Error:             Color256(160),
	Warning:           Color256(166),
	Spinner:           Color256(91),
	ThinkingMax:       Color256(127),
	SelectionBG:       Color256(153),
	SelectionFG:       Color256(232),
	SpinnerFrames:     defaultSpinnerFrames,
	SpinnerIntervalMS: 80,
	SyntaxBaseStyle:   "monokai",
	Syntax:            nordSyntax,
}

Functions

func AppearanceQuery added in v0.24.0

func AppearanceQuery() string

AppearanceQuery returns the complete profile query used at startup and on a replacement generation. The ANSI palette is requested in one OSC 4 batch.

func Bold

func Bold(s string) string

Bold wraps s in bold SGR.

func CellAspectRatio

func CellAspectRatio() float64

CellAspectRatio returns the pixel-height / pixel-width ratio for one terminal cell. ZUT_CELL_ASPECT lets users tune inline-image row reservation for terminals/fonts where the default causes overlap or excessive blank space. Values outside a sane range are ignored.

func ContextUsageText added in v0.19.0

func ContextUsageText(used, max int) string

ContextUsageText returns the latest request's context utilization without styling so callers can safely measure, truncate, and color the whole line.

func CursorColor added in v0.2.0

func CursorColor(color TerminalColor) string

CursorColor returns OSC 12 to set the terminal cursor color. Unlike SGR styling, the cursor is terminal-owned, so modal backdrops must set this separately from their dimmed text rows.

func CursorColor256

func CursorColor256(index int) string

func CursorShapeBlock

func CursorShapeBlock() string

func DetectTrueColor

func DetectTrueColor(termEnv, colorTerm string) bool

DetectTrueColor reports whether TERM or COLORTERM advertises direct color support. This follows the same conservative signals used by ../vev.

func Dim

func Dim(s string) string

Dim wraps s in dim SGR. Re-apply dim after sequences that clear faint so independently styled segments remain dimmed too. Like Bold and Italic, it restores only its own attribute so callers can safely compose styles.

func DimLines added in v0.2.0

func DimLines(lines []string) []string

DimLines returns a dimmed copy of lines. It is suitable for content behind a modal layer: the foreground layer can retain its normal styling while every styled segment in the background stays dimmed.

func ExtractLastNewText

func ExtractLastNewText(raw string) (value string, ok, done bool, editIdx int)

ExtractLastNewText finds the most recent `"newText"` field inside an array of edit objects, scanning from the end of raw backwards so we get the one currently being streamed rather than an earlier completed edit. Returns the partial string value the same way ExtractPartialStringField does, plus the 1-indexed edit number in the array (so the UI can show "edit 2 of N" or similar).

This is aimed at the `edit` tool's streaming shape:

{"path":"...","edits":[{"oldText":"x","newText":"y"},
                         {"oldText":"a","newText":"b<streaming>

We want to show `b<streaming>` while it grows.

func ExtractPartialStringField

func ExtractPartialStringField(raw, field string) (value string, ok, done bool)

ExtractPartialStringField scans raw (a partial JSON object's bytes) for the given top-level string field and returns the unescaped value seen so far. If the value is still being written, it returns what's available with ok=true but done=false. If the closing unescaped quote has been reached, done=true.

This is deliberately small and best-effort: zut uses it to show the live body of a `write` tool call while the model is still typing it, before the full JSON object has been received. It assumes the field is a top-level key (no nested lookup), matches the first occurrence, and tolerates unfinished `\uXXXX` escapes by dropping a trailing incomplete escape sequence.

A production-grade JSON parser would be overkill for this use case; we only care about extracting one field incrementally.

func HighlightCode

func HighlightCode(src, lang string) []string

HighlightCode syntax-colors src and returns the result split into lines, ready for a line-based diff renderer. If no language is given or chroma has no lexer for it, src is returned as-is (one entry per line). Safe to call from multiple goroutines.

Results are memoised by (lang, src) so repeated calls from the view builder (which runs on every redraw) don't re-tokenise. Cache is bounded and evicts oldest entries past its cap.

func ImageDimensions

func ImageDimensions(data []byte) (int, int)

ImageDimensions returns width and height in pixels, or zeros on error. Used for the text fallback so the user sees something useful.

func InlineImageFootprint

func InlineImageFootprint(data []byte, cellsWide, maxRows int) (int, int)

InlineImageFootprint returns the rendered (rows, cells) footprint of an image at the requested cellsWide / maxRows budget, preserving aspect ratio. When the image's natural height exceeds maxRows, the rendered width shrinks below cellsWide so the aspect ratio is preserved within the row clamp; callers wrapping the image in a frame need that actual width to place a closing border at the right column.

func InputBlock

func InputBlock(th Theme, lines []string, width int) []string

func InputLines

func InputLines(th Theme, lines []string, width int) []string

func Italic

func Italic(s string) string

Italic wraps s in italic SGR.

func JoinRightBar

func JoinRightBar(th Theme, main, rightBar string, mainWidth, rightBarWidth int) string

JoinRightBar combines one main-pane row and one right-bar row without allowing either side to soft-wrap. The returned row occupies exactly mainWidth + RightBarSeparatorWidth + rightBarWidth display cells.

func LanguageFromPath

func LanguageFromPath(p string) string

LanguageFromPath maps file extensions to chroma lexer names.

func MoveTo

func MoveTo(row, col int) string

MoveTo moves the cursor to 1-indexed (row, col).

func NormalizeInputStyle

func NormalizeInputStyle(v string) string

func NormalizeStatusPosition

func NormalizeStatusPosition(v string) string

func NormalizeSubagentPosition added in v0.4.0

func NormalizeSubagentPosition(v string) string

NormalizeSubagentPosition returns the placement for live subagent activity. A missing value keeps activity immediately below the input by default.

func NormalizeWorkingPosition

func NormalizeWorkingPosition(v string) string

func ParseAppearanceCSI added in v0.24.0

func ParseAppearanceCSI(raw []byte) (scheme *SchemeEvent, mode *ModeReportEvent)

ParseAppearanceCSI parses the two runtime CSI response families used by terminal-owned themes: current scheme (997) and mode ownership (DECRQM).

func ReadClipboardImagePNG

func ReadClipboardImagePNG() (string, []byte, bool, error)

ReadClipboardImagePNG preserves the legacy Linux behavior. Linux image reads retain their clipboard MIME type through ReadClipboardImage instead.

func ReadClipboardText

func ReadClipboardText() (string, bool, error)

ReadClipboardText reads plain text from a Wayland or X11 clipboard. The small command-line clients are used to preserve zut's CGO-free build.

func RenderInlineImage

func RenderInlineImage(proto ImageProtocol, data []byte, mime string, maxCellsWide int) string

RenderInlineImage returns a terminal escape sequence that draws data inline. If the protocol is None, returns "" so the caller can fall back to a text placeholder.

maxCellsWide caps the rendered width in terminal cells (columns) for protocols that honor it. 0 means "let the terminal decide".

func RenderInlineImageScaled

func RenderInlineImageScaled(proto ImageProtocol, data []byte, mime string, maxCellsWide, maxCellsHigh int) string

RenderInlineImageScaled renders an image with both width and height clamps (in terminal cells). Values <= 0 mean "let the terminal decide".

func RenderMarkdown

func RenderMarkdown(src string, th Theme, width int) string

RenderMarkdown renders a small subset of Markdown to styled terminal text using theme colors. Supported: headings, bold, italic, inline code, fenced code blocks, bullet lists, numbered lists, blockquotes, simple GitHub-style tables. Not supported: links with complex formatting, HTML.

width is used to draw horizontal rules (e.g. around code fences). Pass 0 to use a reasonable fallback.

func RenderRightBar

func RenderRightBar(th Theme, widgets []RightBarWidget, width, height int) []string

RenderRightBar renders a bounded, full-height side rail without adding a second frame around the host-owned separator. Widgets are sorted by extension name and ID so output stays deterministic even when extension frames arrive concurrently. Long lines are clipped with a three-dot ellipsis while reserving one cell of right padding.

func ReportCWD

func ReportCWD(dir string) string

ReportCWD returns the OSC 7 sequence that tells the terminal the current working directory, formatted as a file URL. Terminals such as kitty, WezTerm, iTerm2, and GNOME Terminal use this to open new tabs / splits in the same directory. Returns "" for an empty path so callers can write it unconditionally. The path is percent-encoded per RFC 3986 (only unreserved characters and the path separator are left bare) and prefixed with the local hostname.

func ResetCursorColor

func ResetCursorColor() string

func ResetCursorShape

func ResetCursorShape() string

func RightBarColumns

func RightBarColumns(cols int) (mainWidth, rightBarWidth int, ok bool)

RightBarColumns returns the main-pane and side-rail widths for a terminal. It returns ok=false when keeping both panes usable would leave too little room for the rail; callers should then render right-bar widgets using their normal fallback placement.

func RowsForInlineImage

func RowsForInlineImage(data []byte, cellsWide, maxRows int) int

RowsForInlineImage returns the number of terminal rows an image rendered at cellsWide columns will occupy, preserving aspect ratio. Clamped to maxRows. Returns 0 if the image cannot be decoded.

func SetScrollRegion

func SetScrollRegion(top, bottom int) string

SetScrollRegion returns the DECSTBM sequence to set the terminal's scroll region (1-indexed, inclusive). Lines that scroll out of this region still flow into the terminal's scrollback, but content above or below the region is not affected by scroll-induced movement.

func SetTitle

func SetTitle(title string) string

SetTitle returns an OSC 0 sequence that sets the terminal's tab/window title. Control characters are removed from the payload so user- or model-provided text cannot inject another terminal control sequence.

func ShortArgs

func ShortArgs(tool string, raw json.RawMessage) string

ShortArgs renders a tool call's arguments into a one-line suffix for the "tool name <args>" header. tool is the tool name so we can add shape-specific decorations: for read we append the requested line range (e.g. "path:1-200") pulled from the offset/limit args, which is useful context at a glance without expanding the result body. Other tools keep the legacy "path or command, truncated" shape.

The truncation width defaults to 60 cells but can be tuned via the ZUT_TOOL_ARG_WIDTH environment variable (see toolArgWidth).

Exported because the interactive mode pre-populates the ToolCallView.Args field with this value as soon as the tool call is announced, so the live overlay's header matches what the finalised transcript will later render.

func StatusBar

func StatusBar(p StatusBarParams) []string

StatusBar builds the status shown above the editor. Always returns two lines when a cwd is provided: the stats on the first line, the cwd on its own line below, indented to match the stats column. This keeps the status bar stable across terminal resizes (the cwd never jumps from right-aligned-on-line-1 to flush-left-on-line-2) and makes a long cwd safe at any width.

Layout:

<busyPrefix>  (provider) model  stats   <- line 1
  cwd                                   <- line 2 (2-space indent)

The old "ctrl+c exit - /help" / "esc cancel" hint is gone entirely. The slash-command popup and the queued/sliding-in chips already cover the discoverability of those keybindings.

func ThemeExists

func ThemeExists(zutHome, preferred string) bool

func UsageStatsParts added in v0.19.0

func UsageStatsParts(p UsageStatsParams) []string

UsageStatsParts returns compact, provider-neutral token and cost labels.

func WrapANSILine

func WrapANSILine(s string, limit int) []string

WrapANSILine is the exported form of wrapANSILine so other modes / dialogs can reuse the same visible-width-aware wrap behavior.

func WriteBell

func WriteBell(w io.Writer) error

WriteBell emits one standalone terminal alert character. Callers should route it through the same output writer used for the rest of the terminal so redraws and alerts share the host's output boundary.

Types

type AppearanceParser added in v0.24.0

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

AppearanceParser controls which terminal replies may be consumed. Keeping the gate here prevents OSC-looking editor input from disappearing unless zut actively issued a matching query, while 997 notifications are accepted only after notification support is known.

func (*AppearanceParser) SetNotifications added in v0.24.0

func (p *AppearanceParser) SetNotifications(enabled bool)

func (*AppearanceParser) SetPendingColors added in v0.24.0

func (p *AppearanceParser) SetPendingColors(pending bool)

func (*AppearanceParser) SetPendingScheme added in v0.24.0

func (p *AppearanceParser) SetPendingScheme(pending bool)

SetPendingScheme permits one solicited 997 response and marks it so the event loop does not mistake it for a new terminal notification.

type AppearanceSource added in v0.24.0

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

AppearanceSource filters accepted terminal appearance replies before they reach Reader. It deliberately leaves bracketed-paste payload opaque.

func NewAppearanceSource added in v0.24.0

func NewAppearanceSource(read func() (byte, error), peek func(time.Duration) (byte, bool, error), parser *AppearanceParser, emit func(InputEvent)) *AppearanceSource

func (*AppearanceSource) ReadByte added in v0.24.0

func (s *AppearanceSource) ReadByte() (byte, error)

type ClipboardImage

type ClipboardImage struct {
	MimeType string
	Data     []byte
}

ClipboardImage is an image read from the system clipboard.

func ReadClipboardImage

func ReadClipboardImage(ctx context.Context) (ClipboardImage, bool, error)

ReadClipboardImage reads an image from the Wayland or X11 system clipboard. Clipboard helper failures are intentionally treated as an empty clipboard so that optional desktop integrations do not affect paste handling.

type ColorDepth added in v0.24.0

type ColorDepth uint8

ColorDepth is the terminal output capability used when resolving a color.

const (
	// Indexed256 is the zero value to preserve the legacy behavior for
	// profiles assembled by callers that predate ColorDepth.
	ColorDepthIndexed256 ColorDepth = iota
	ColorDepthANSI16
	ColorDepthTrueColor
)

func DetectColorDepth added in v0.24.0

func DetectColorDepth(termEnv, colorTerm string) ColorDepth

DetectColorDepth conservatively chooses the highest color mode advertised by the environment. Terminals without a 256-color signal stay ANSI16.

type ColorEvent added in v0.24.0

type ColorEvent struct {
	Kind  int // 10 foreground, 11 background, 4 palette
	Slot  int
	Color TerminalColor
}

ColorEvent is one default foreground/background or ANSI-palette reply.

func ParseAppearanceOSC added in v0.24.0

func ParseAppearanceOSC(raw []byte) []ColorEvent

ParseAppearanceOSC parses one complete BEL/ST-terminated OSC sequence. Combined OSC 4 responses produce one ColorEvent for every valid pair.

type Editor

type Editor struct {
	// Lines is the current buffer, one entry per line.
	Lines    []string
	CursorR  int // row index into Lines
	CursorC  int // rune index into Lines[CursorR]
	Prompt   string
	MaxWidth int

	// Mask replaces rendered input runes with asterisks while preserving the
	// real buffer for submission. It is intended for secrets such as API keys.
	Mask bool
	// contains filtered or unexported fields
}

Editor is a simple multi-line text editor for the input area.

Users can type, paste, move the cursor, and submit. The editor exposes its rendered height and the current cursor row/col for the outer renderer. It does NOT draw itself directly; instead, Render() returns the visible lines.

func NewEditor

func NewEditor(prompt string) *Editor

NewEditor returns an empty editor with the given prompt.

func (*Editor) Clear

func (e *Editor) Clear()

Clear resets the buffer.

func (*Editor) HandleKey

func (e *Editor) HandleKey(k Key) (submit bool)

HandleKey applies k to the editor. It returns submit=true when the user pressed enter and there is content to send; the caller should read SubmitValue() and then Clear().

func (*Editor) Insert

func (e *Editor) Insert(s string)

Insert places s into the editor at the cursor, splitting on newlines so multi-line pastes preserve their structure.

func (*Editor) IsEmpty

func (e *Editor) IsEmpty() bool

IsEmpty reports whether the buffer has no visible content.

func (*Editor) MoveVertical

func (e *Editor) MoveVertical(dir int) bool

moveCursorVisual moves the cursor one visual row in direction dir (-1 = up, +1 = down) through the wrapped layout the user sees on screen. Handles both multi-line logical inputs and the case where a single long line wraps across several visual rows.

Algorithm: rebuild the same wrapped layout Render produces, tagging each visual row with (logicalRow, runeOffsetStart, runeOffsetEnd, leadingWidth). Find the row the cursor sits on, then pick (row+dir) and map the cursor's current visual column (minus the target row's leading indent) to a rune index inside that row's slice of its logical line. No-op at the top/bottom edges of the whole buffer. MoveVertical moves the cursor one visual row up/down through the rendered editor layout. It returns true when the cursor moved, false at the top/bottom edge. Callers can use false to fall back to outer UI scrolling.

func (*Editor) Render

func (e *Editor) Render(width int) (lines []string, visualRow, visualCol int)

Render returns the editor's visible lines (wrapped to width). visualRow/visualCol describe where the cursor lands within the returned lines.

func (*Editor) SetValue

func (e *Editor) SetValue(s string)

SetValue replaces the buffer and places the cursor at the end. Also drops any stored pastes because the placeholders they back are now gone from the visible text.

func (*Editor) SubmitValue

func (e *Editor) SubmitValue() string

SubmitValue returns the buffer with every paste placeholder expanded to its stored body. Call once at submit time; the expansion is lossless (placeholders are only injected in HandleKey for KeyPaste with multi-line content).

Expansion is non-destructive: the internal paste map isn't touched. Clear() is what resets both the placeholder text and the map, and the caller already calls Clear() right after reading SubmitValue() as part of the submit flow.

func (*Editor) Value

func (e *Editor) Value() string

Value returns the buffer as a single string, WITHOUT expanding paste placeholders. Used for anything that should reflect what's visible on screen (history, slash-command detection, editor state). For the string that actually goes to the agent, use SubmitValue(), which expands each [paste #N +L lines] token back into the full pasted body.

type FloatingPane added in v0.18.0

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

FloatingPane is a stateful viewport for an independent TUI view. It keeps its scroll position while the same view is active, follows a focused row into view, and composes that view over a live background frame.

A FloatingPane is deliberately frontend-neutral: callers own their view's data and key handling, while the pane owns geometry, clipping, and visual composition.

func (*FloatingPane) Compose added in v0.18.0

func (p *FloatingPane) Compose(theme Theme, id, title string, background, content []string, cols, rows, contentCursorRow, contentCursorCol int) (pane FloatingPaneFrame, cursorRow, cursorCol int)

Compose returns a complete floating-pane frame. title is centered in the pane border. contentCursorRow is relative to content and is used both to retain the pane's own viewport and to return an absolute terminal cursor row. Pass -1 when the foreground has no text cursor.

func (*FloatingPane) Reset added in v0.18.0

func (p *FloatingPane) Reset()

Reset discards the viewport position. Callers normally do not need this: Compose resets it automatically when the view identity changes.

type FloatingPaneFrame added in v0.18.0

type FloatingPaneFrame struct {
	Background []string
	Lines      []string
	Rect       FloatingPaneRect
}

FloatingPaneFrame contains the dimmed background and foreground rows to paint at absolute terminal positions. Renderer.DrawFloating consumes it.

type FloatingPaneRect added in v0.18.0

type FloatingPaneRect struct {
	X, Y          int
	Width, Height int
	Drawer        bool
	Borderless    bool
}

FloatingPaneRect describes a pane's outer border in zero-based terminal coordinates. Content occupies the rectangle inside that border.

func FloatingPaneMaxRect added in v0.18.0

func FloatingPaneMaxRect(cols, rows int) FloatingPaneRect

FloatingPaneMaxRect returns the largest pane for the current terminal. The final pane is shortened to its content by Compose. Below 80 columns it becomes a full-width bottom drawer so dialogs remain readable and usable.

func (FloatingPaneRect) ContentHeight added in v0.18.0

func (r FloatingPaneRect) ContentHeight() int

func (FloatingPaneRect) ContentWidth added in v0.18.0

func (r FloatingPaneRect) ContentWidth() int

type ImageProtocol

type ImageProtocol int

ImageProtocol describes which inline-image escape the current terminal understands.

const (
	ImageProtocolNone   ImageProtocol = iota // no inline images, use text fallback
	ImageProtocolITerm2                      // iTerm2 proprietary OSC 1337 File= (also: WezTerm)
	ImageProtocolKitty                       // Kitty graphics protocol
)

func DetectImageProtocol

func DetectImageProtocol() ImageProtocol

DetectImageProtocol returns the best inline-image protocol supported by the current terminal, or ImageProtocolNone.

The default is to auto-detect: if the terminal advertises iTerm2 or Kitty-graphics support, we use it. The ZUT_INLINE_IMAGES env var overrides the default:

ZUT_INLINE_IMAGES=off         -> force text fallback
ZUT_INLINE_IMAGES=placeholder -> force text fallback (alias for off)
ZUT_INLINE_IMAGES=iterm       -> force iTerm2 protocol
ZUT_INLINE_IMAGES=kitty       -> force Kitty protocol
ZUT_INLINE_IMAGES=auto        -> explicit auto-detect (same as default)

type InputEvent added in v0.24.0

type InputEvent struct {
	Key    *Key
	Scheme *SchemeEvent
	Color  *ColorEvent
	Mode   *ModeReportEvent
}

InputEvent is emitted by the appearance-aware input path. Exactly one field is set. Key bytes that do not form an accepted protocol reply remain keys.

type Key

type Key struct {
	Kind  KeyKind
	Rune  rune   // for KeyRune
	Paste string // for KeyPaste
	Ctrl  bool
	Alt   bool
	Shift bool
	Super bool
}

Key is a parsed keypress.

type KeyKind

type KeyKind int
const (
	KeyRune KeyKind = iota
	KeyEnter
	KeyBackspace
	KeyTab
	KeyShiftTab
	KeyEsc
	KeyUp
	KeyDown
	KeyLeft
	KeyRight
	KeyHome
	KeyEnd
	KeyPageUp
	KeyPageDown
	KeyDelete
	KeyCtrlC
	KeyCtrlD
	KeyCtrlL
	KeyCtrlU
	KeyCtrlK
	KeyCtrlA
	KeyCtrlB
	KeyCtrlE
	KeyCtrlW
	KeyCtrlO
	KeyPaste
	KeyPasteClipboard
	KeyMouseWheelUp
	KeyMouseWheelDown
	KeyUnknown
)

type MessageAnchor

type MessageAnchor struct {
	MessageIdx int // index into v.Messages
	Row        int // first row of that message in the Build() output
}

MessageAnchor records where a rendered message starts in the chat line slice. Used by /jump so the dialog can scroll the viewport to the row where a turn's user prompt begins.

type ModeReportEvent added in v0.24.0

type ModeReportEvent struct {
	Mode   int
	Status int
}

ModeReportEvent is a DECRQM response. Status follows the terminal protocol: 1/3 set, 2 reset, 0/4 unsupported or permanently reset.

type ProcTerm

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

ProcTerm is a Terminal bound to the current process's tty.

func NewProcTerm

func NewProcTerm() *ProcTerm

NewProcTerm returns a Terminal bound to stdin/stdout.

func (*ProcTerm) EnterRaw

func (p *ProcTerm) EnterRaw() (func() error, error)

func (*ProcTerm) OnResize

func (p *ProcTerm) OnResize(fn func())

func (*ProcTerm) PeekByteTimeout

func (p *ProcTerm) PeekByteTimeout(d time.Duration) (byte, bool, error)

PeekByteTimeout uses a platform-specific non-blocking read to decide whether another byte is available within d. If not, returns (0, false, nil).

func (*ProcTerm) ReadByte

func (p *ProcTerm) ReadByte() (byte, error)

func (*ProcTerm) SetNonblock

func (p *ProcTerm) SetNonblock(enable bool) error

func (*ProcTerm) Size

func (p *ProcTerm) Size() (int, int)

func (*ProcTerm) Write

func (p *ProcTerm) Write(b []byte) (int, error)

type Reader

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

Reader parses a byte stream into Key events. It understands basic xterm escape sequences and bracketed paste.

func NewReader

func NewReader(read func() (byte, error)) *Reader

NewReader returns a Reader that pulls bytes from read.

func NewReaderWithPeek

func NewReaderWithPeek(read func() (byte, error), peek func(time.Duration) (byte, bool, error)) *Reader

NewReaderWithPeek returns a Reader that pulls bytes from read and uses peek to disambiguate bare Esc from the start of an escape sequence.

func (*Reader) Read

func (r *Reader) Read() (Key, error)

Read returns the next parsed Key.

type Renderer

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

Renderer maintains a previous frame and writes only the lines that changed on each Draw(). Callers pass a full target frame (slice of styled lines, already wrapped to width).

func NewRenderer

func NewRenderer(out io.Writer) *Renderer

NewRenderer returns a renderer that writes to out.

Detects VS Code's integrated terminal via $TERM_PROGRAM and, when detected, disables emission of \x1b[3J for the reasons documented on Renderer.keepScrollback. The env var is set by VS Code itself (and by Cursor, which forks VS Code's terminal — same xterm.js, same bug), so no user configuration is required.

func (*Renderer) Clear

func (r *Renderer) Clear()

Clear forces a full repaint on the next Draw and clears the screen plus scrollback. In main-screen flow mode this is required whenever already-emitted transcript layout changes (for example ctrl+o expand/collapse), because terminal scrollback cannot be edited reliably once printed.

func (*Renderer) Draw

func (r *Renderer) Draw(lines []string, cursorRow, cursorCol int)

func (*Renderer) DrawFloating added in v0.18.0

func (r *Renderer) DrawFloating(pane FloatingPaneFrame, cursorRow, cursorCol int)

DrawFloating paints a complete, dimmed live background and then an opaque floating pane at absolute coordinates. The background is deliberately invalidated on every call: the pane is not represented in Draw's frame cache, so repainting it first prevents stale foreground cells when either layer changes or the terminal is resized.

func (*Renderer) DrawLog

func (r *Renderer) DrawLog(chat, bottom []string, cursorBottomRow, cursorCol int)

DrawLog renders zut in the terminal's main screen as normal terminal flow rather than a fixed full-screen frame. Chat lines are emitted once into the host terminal scrollback; the current bottom block (dialogs, slash popup, status, editor) is erased and redrawn in place at the end.

cursorBottomRow/cursorCol are offsets into bottom, not the full frame.

func (*Renderer) DrawRightBar

func (r *Renderer) DrawRightBar(chat, bottom, rightBar []string, cursorBottomRow, cursorCol int)

DrawRightBar renders the complete transcript through DrawLog, then composites the persistent rail over the visible viewport. The flow cache remains authoritative so appended chat still enters native scrollback.

func (*Renderer) DrawRightBarDimmed added in v0.2.0

func (r *Renderer) DrawRightBarDimmed(chat, bottom, rightBar []string, cursorBottomRow, cursorCol int)

DrawRightBarDimmed renders a right-bar frame whose separator is part of a dimmed backdrop. Callers must pass already-dimmed main and right-bar rows.

func (*Renderer) Invalidate

func (r *Renderer) Invalidate()

Invalidate forces a full repaint on the next Draw without clearing the whole terminal first. Useful when the cached diff is unreliable but a visible full-screen flash would be too distracting.

func (*Renderer) KeepsScrollback

func (r *Renderer) KeepsScrollback() bool

KeepsScrollback reports whether this renderer suppresses the scrollback-clear escape (true under VS Code's terminal). Callers use it to pick a viewport-safe full repaint (Invalidate) over a scrollback-clearing one (Clear) when redrawing overlays.

func (*Renderer) ResetScrollRegion

func (r *Renderer) ResetScrollRegion()

Resize tells the renderer the current terminal size.

On a real size change we also issue a clear-screen so the next Draw starts from a blank slate. Without the clear, characters from the old (wider) layout linger past the new right edge and rows from before the new bottom hang around as garbage.

func (*Renderer) Resize

func (r *Renderer) Resize(cols, rows int)

func (*Renderer) SetTheme

func (r *Renderer) SetTheme(th Theme)

SetTheme updates renderer-level terminal styling. Changing the background affects every row, so cached frame state is invalidated.

type RightBarWidget

type RightBarWidget struct {
	Extension string
	ID        string
	Title     string
	Lines     []string
}

RightBarWidget is declarative content for one persistent extension widget. The host owns ordering and layout; widgets are display-only in the first version and should use OpenPanel for interaction.

type SchemeEvent added in v0.24.0

type SchemeEvent struct {
	Light     bool
	Solicited bool
}

SchemeEvent is a reported terminal color-scheme preference. Solicited is true only for the reply to a query initiated by zut.

type StatusBarParams

type StatusBarParams struct {
	Theme      Theme
	Provider   string
	Model      string
	Reasoning  string // "" means thinking off
	FastMode   bool   // show the provider's opt-in fast tier when enabled
	Busy       bool
	BusyPrefix string // spinner + funny line when busy
	CWD        string
	Locked     bool   // sandbox on?
	NoYolo     bool   // confirmation mode enabled?
	GoalStatus string // autonomous goal lifecycle, empty when none

	// Cumulative session usage and cost.
	Usage provider.Usage
	// Subscription is true when the credential is an OAuth token (claude
	// pro/max, chatgpt plus/pro) rather than a paid api key. We still
	// compute a cost for visibility and append "(sub)" so the user
	// knows no real money moved.
	Subscription bool

	// Last turn's input+cache tokens (approximates current live context).
	ContextUsed int
	ContextMax  int // model's context window; 0 disables the percentage

	// AutoCompacting is true when the agent is currently running a
	// model-triggered condense pass. Surfaces as "(auto)" after the
	// context percentage so it's clear where the spinner is coming from.
	AutoCompacting bool

	// Telegram true when the telegram bridge is connected. Adds a
	// small "- tg -" tag to the cwd line so the user can tell at a
	// glance that dms are being mirrored into this session.
	Telegram bool

	Cols int // terminal width; drives right-alignment of cwd
}

StatusBarParams groups the many bits of state the status bar needs. Grew from a flat argument list once we settled on the layout.

type StringSpinner added in v0.16.0

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

StringSpinner selects one text frame for each configured interval. It owns no ticker or goroutine; callers render it from their existing UI clock.

func NewStringSpinner added in v0.16.0

func NewStringSpinner(frames []string, interval time.Duration) StringSpinner

NewStringSpinner creates a reusable text animation. Empty frames render an empty string, and a non-positive interval keeps the first frame stable.

func (StringSpinner) FrameAt added in v0.16.0

func (s StringSpinner) FrameAt(startedAt, now time.Time) string

FrameAt returns the frame for now relative to startedAt. Times before the start clamp to the first frame.

type SyntaxTheme

type SyntaxTheme struct {
	Keyword             string
	KeywordConstant     string
	KeywordDeclaration  string
	KeywordNamespace    string
	KeywordReserved     string
	KeywordType         string
	NameBuiltin         string
	NameFunction        string
	NameClass           string
	NameDecorator       string
	LiteralString       string
	LiteralStringEscape string
	LiteralNumber       string
	Comment             string
	CommentPreproc      string
	Operator            string
	Punctuation         string
	Text                string
}

SyntaxTheme contains the chroma token colors used by code fences, file previews, and diffs. Values are chroma style entries, so they may include attributes after the color (for example "#81a1c1 bold").

type SyntaxThemeOverrides

type SyntaxThemeOverrides struct {
	Keyword             *string `json:"keyword,omitempty"`
	KeywordConstant     *string `json:"keyword_constant,omitempty"`
	KeywordDeclaration  *string `json:"keyword_declaration,omitempty"`
	KeywordNamespace    *string `json:"keyword_namespace,omitempty"`
	KeywordReserved     *string `json:"keyword_reserved,omitempty"`
	KeywordType         *string `json:"keyword_type,omitempty"`
	NameBuiltin         *string `json:"name_builtin,omitempty"`
	NameFunction        *string `json:"name_function,omitempty"`
	NameClass           *string `json:"name_class,omitempty"`
	NameDecorator       *string `json:"name_decorator,omitempty"`
	LiteralString       *string `json:"literal_string,omitempty"`
	LiteralStringEscape *string `json:"literal_string_escape,omitempty"`
	LiteralNumber       *string `json:"literal_number,omitempty"`
	Comment             *string `json:"comment,omitempty"`
	CommentPreproc      *string `json:"comment_preproc,omitempty"`
	Operator            *string `json:"operator,omitempty"`
	Punctuation         *string `json:"punctuation,omitempty"`
	Text                *string `json:"text,omitempty"`
}

type Terminal

type Terminal interface {
	io.Writer
	// Size returns (cols, rows).
	Size() (int, int)
	// OnResize registers a callback invoked on SIGWINCH (best effort).
	OnResize(func())
	// EnterRaw puts the tty into raw mode. Returns a restore func.
	EnterRaw() (restore func() error, err error)
	// ReadByte reads one byte of input. Blocks.
	ReadByte() (byte, error)
	// PeekByteTimeout reads one byte of input but returns (0, false, nil)
	// if no byte arrives within the timeout. Used to disambiguate bare
	// Esc from the start of an escape sequence.
	PeekByteTimeout(time.Duration) (byte, bool, error)
	// SetNonblock sets stdin to non-blocking mode (used by paste handling).
	// May be a no-op on some platforms.
	SetNonblock(bool) error
}

Terminal abstracts the real terminal for tests.

type TerminalColor

type TerminalColor struct {
	Mode  terminalColorMode
	Index int
	R     int
	G     int
	B     int
}

TerminalColor describes both explicit colors and terminal-owned colors. TerminalDefault and TerminalPaletteSlot intentionally differ from literal xterm-256 values: an adaptive theme follows the terminal palette while a custom numeric value retains its xterm meaning.

func Color256

func Color256(index int) TerminalColor

func ColorANSI

func ColorANSI(sgr int) TerminalColor

func ColorRGB

func ColorRGB(r, g, b int) TerminalColor

func TerminalDefault added in v0.24.0

func TerminalDefault() TerminalColor

func TerminalPaletteSlot added in v0.24.0

func TerminalPaletteSlot(index int) TerminalColor

type TerminalColorValue

type TerminalColorValue struct {
	TerminalColor
}

TerminalColorValue accepts any of these JSON forms:

24                         // xterm-256 color index
"#42454b"                  // RGB hex
{"mode":"ansi","index":100}
{"mode":"rgb","r":66,"g":69,"b":75}
{"mode":"256","index":254}

func (*TerminalColorValue) UnmarshalJSON

func (c *TerminalColorValue) UnmarshalJSON(data []byte) error

type TerminalProfile

type TerminalProfile struct {
	Foreground TerminalColor
	Background TerminalColor

	HasForeground bool
	HasBackground bool

	Palette      [16]TerminalColor
	PaletteKnown uint16
	Depth        ColorDepth
	// TrueColor is retained for callers compiled against the older profile.
	// Depth is authoritative when it is ColorDepthTrueColor.
	TrueColor   bool
	Light       bool
	SchemeKnown bool
}

TerminalProfile is the best-effort color snapshot reported by the controlling terminal. Palette entries are optional; an unknown slot is emitted as its direct ANSI SGR rather than substituted with a fixed zut RGB.

func (TerminalProfile) ColorDepth added in v0.24.0

func (p TerminalProfile) ColorDepth() ColorDepth

func (TerminalProfile) PaletteColor

func (p TerminalProfile) PaletteColor(index int) (TerminalColor, bool)

PaletteColor returns a reported ANSI palette entry when available.

type Theme

type Theme struct {
	FG           TerminalColor
	Muted        TerminalColor
	Accent       TerminalColor
	Background   *TerminalColor // optional full-row TUI background; nil keeps terminal default
	User         TerminalColor  // label color for the user role
	UserBubbleBG TerminalColor  // background tint behind user message rows
	UserBubbleFG TerminalColor  // foreground colour for user message rows
	Assistant    TerminalColor  // label color for the zut role
	Tool         TerminalColor
	ToolOut      TerminalColor
	Error        TerminalColor
	Warning      TerminalColor
	Spinner      TerminalColor // spinner frame and activity label
	ThinkingMax  TerminalColor // status color for the opt-in max reasoning level
	SelectionBG  TerminalColor // background for highlighted rows
	SelectionFG  TerminalColor // foreground for highlighted rows

	// UseTerminalPalette resolves TerminalPaletteSlot roles against the
	// reported palette on truecolor terminals. It has no effect on explicit
	// Color256 values from a custom theme.
	UseTerminalPalette bool
	Terminal           TerminalProfile

	SpinnerFrames     []string
	SpinnerIntervalMS int

	SyntaxBaseStyle string
	Syntax          SyntaxTheme
}

Semantic palette used by zut. Each role is a TerminalColor so themes can use indexed, ANSI, or RGB values without changing render code.

func DetectThemeFromBackground

func DetectThemeFromBackground(timeout time.Duration) Theme

DetectThemeFromBackground queries the controlling tty for its current foreground, background, and ANSI palette using OSC 10/11/4. Auto is always terminal-owned; the snapshot is retained for subsequent runtime resolution.

The query / parse runs synchronously before the TUI is initialised so the returned snapshot can drive the entire session. We briefly put stdin into raw mode and disable echo so OSC replies do not leak onto the user's screen.

func DetectThemeWithCustom

func DetectThemeWithCustom(zutHome, preferred string, timeout time.Duration) (Theme, string, error)

DetectThemeWithCustom performs the initial bounded profile query then uses the same loader and pure resolver as runtime selection.

func LoadThemeFromHome

func LoadThemeFromHome(zutHome, preferred string, detected Theme) (Theme, string, error)

LoadThemeFromHome is retained as the startup convenience wrapper. New runtime code should keep the ThemeSource and call ResolveTheme directly.

func TerminalTheme added in v0.24.0

func TerminalTheme(profile TerminalProfile) Theme

TerminalTheme constructs zut's adaptive default from terminal-owned defaults and ANSI slots. It never chooses a fixed built-in palette merely because a terminal reports a light background.

func (Theme) AccentBar

func (t Theme) AccentBar(c TerminalColor) string

AccentBar returns a 2-cell-wide leader: a coloured half-block glyph followed by a plain space gutter. Used as the speaker-label prefix in the chat ("▌ you", "▌ zut") and as the editor prompt so the bar reads consistently across the UI.

func (Theme) BG

func (t Theme) BG(c TerminalColor, s string) string

BG wraps s in a terminal background color. RGB values are quantized when the active terminal does not advertise truecolor support.

func (Theme) BG256 deprecated

func (t Theme) BG256(c TerminalColor, s string) string

BG256 retains the historical helper name while accepting the full TerminalColor model.

Deprecated: use BG.

func (Theme) BackgroundStyle

func (t Theme) BackgroundStyle() string

BackgroundStyle returns the SGR prefix for the optional full-row TUI background. Empty means zut should leave the terminal's configured background untouched.

func (Theme) DimColor

func (t Theme) DimColor(color TerminalColor, percent int) TerminalColor

DimColor fades a theme color toward the terminal background. It is used while resolving adaptive terminal surfaces and by overlay callers.

func (Theme) FG256 deprecated

func (t Theme) FG256(c TerminalColor, s string) string

FG256 retains the historical helper name while accepting the full TerminalColor model.

Deprecated: use FGColor.

func (Theme) FGColor

func (t Theme) FGColor(c TerminalColor, s string) string

FGColor wraps s in a foreground color. The name is intentionally explicit because the color may be xterm-256, ANSI, or RGB.

func (Theme) Highlight

func (t Theme) Highlight(s string) string

Highlight paints s with the theme's selection colors (foreground + background). The caller is responsible for padding s to the desired width; styling alone does not extend the background past content.

func (Theme) HighlightCode

func (th Theme) HighlightCode(src, lang string) []string

HighlightCode syntax-colors src using this theme's syntax palette.

func (Theme) PadHighlight

func (t Theme) PadHighlight(s string, width int) string

PadHighlight styles s and extends the selection background to the full terminal width so the highlight is a full row, not just a rectangle around the text.

func (Theme) SelectionStyle

func (t Theme) SelectionStyle() string

SelectionStyle returns the SGR prefix for the theme's selected row.

func (Theme) SelectionStyleFG

func (t Theme) SelectionStyleFG(fg TerminalColor) string

SelectionStyleFG returns the SGR prefix for selected-row text with a custom foreground. Useful for preserving semantic marks inside a highlighted row without hardcoding escape sequences outside theme.go.

func (Theme) UserBubble

func (t Theme) UserBubble(s string, width int) string

UserBubble paints a single user message row with the bubble background colour, padding to width so the tint extends to the full terminal width. Foreground stays in UserBubbleFG so text remains legible against the tint.

func (Theme) UserBubbleRow

func (t Theme) UserBubbleRow(content string, width int) string

UserBubbleRow renders one user-bubble row prefixed with a coloured half-block accent bar ("▌ ") so every line of the bubble has the zut-blue gutter at the very left. The bar lives outside the bubble tint (chat bg) so the bubble itself sits inside it. Width is the outer width including the bar; the bubble content is padded to width-2 (the bar + its trailing space).

type ThemeFile

type ThemeFile struct {
	Name        string              `json:"name"`
	Description string              `json:"description"`
	Colors      ThemeFileColorModes `json:"colors"`
	Overrides   ThemeOverrides      `json:"-"`
}

ThemeFile is the user-editable JSON shape loaded from $ZUT_HOME/themes/*.json. It carries metadata plus separate overrides for dark and light terminals.

func (*ThemeFile) UnmarshalJSON

func (tf *ThemeFile) UnmarshalJSON(data []byte) error

type ThemeFileColorModes

type ThemeFileColorModes struct {
	Base     ThemeOverrides `json:"-"`
	Dark     ThemeOverrides `json:"dark"`
	Light    ThemeOverrides `json:"light"`
	HasDark  bool           `json:"-"`
	HasLight bool           `json:"-"`
}

func (*ThemeFileColorModes) UnmarshalJSON

func (m *ThemeFileColorModes) UnmarshalJSON(data []byte) error

type ThemeOption

type ThemeOption struct {
	Value       string
	Label       string
	Description string
	Path        string
	Builtin     bool
}

ThemeOption is one selectable theme discovered under $ZUT_HOME/themes. Value is stored in config.json.

func AvailableThemes

func AvailableThemes(zutHome string) []ThemeOption

AvailableThemes returns built-in and user-installed themes suitable for a settings picker. Invalid JSON files are skipped.

func ThemeOptionFromFile

func ThemeOptionFromFile(path, value, source string) (ThemeOption, bool)

ThemeOptionFromFile parses one theme JSON file for picker display. value is what will be stored in config; pass an absolute path for extension-owned themes so they can be loaded without copying into $ZUT_HOME/themes.

type ThemeOverrides

type ThemeOverrides struct {
	FG                *TerminalColorValue  `json:"fg,omitempty"`
	Muted             *TerminalColorValue  `json:"muted,omitempty"`
	Accent            *TerminalColorValue  `json:"accent,omitempty"`
	Background        *TerminalColorValue  `json:"background,omitempty"`
	User              *TerminalColorValue  `json:"user,omitempty"`
	UserBubbleBG      *TerminalColorValue  `json:"user_bubble_bg,omitempty"`
	UserBubbleFG      *TerminalColorValue  `json:"user_bubble_fg,omitempty"`
	Assistant         *TerminalColorValue  `json:"assistant,omitempty"`
	Tool              *TerminalColorValue  `json:"tool,omitempty"`
	ToolOut           *TerminalColorValue  `json:"tool_out,omitempty"`
	Error             *TerminalColorValue  `json:"error,omitempty"`
	Warning           *TerminalColorValue  `json:"warning,omitempty"`
	Spinner           *TerminalColorValue  `json:"spinner,omitempty"`
	ThinkingMax       *TerminalColorValue  `json:"thinking_max,omitempty"`
	ThinkingMaxCamel  *TerminalColorValue  `json:"thinkingMax,omitempty"`
	SelectionBG       *TerminalColorValue  `json:"selection_bg,omitempty"`
	SelectionFG       *TerminalColorValue  `json:"selection_fg,omitempty"`
	SpinnerFrames     []string             `json:"spinner_frames,omitempty"`
	SpinnerIntervalMS *int                 `json:"spinner_interval_ms,omitempty"`
	SyntaxBaseStyle   *string              `json:"syntax_base_style,omitempty"`
	Syntax            SyntaxThemeOverrides `json:"syntax,omitempty"`
}

ThemeOverrides is intentionally pointer-based so a theme file can override only the colors it cares about and inherit the built-in dark/light defaults for everything else.

type ThemePreference added in v0.24.0

type ThemePreference struct {
	Persisted string
	Effective string
	Forced    bool
}

ThemePreference separates the persisted setting from the process-only ZUT_THEME override. Only dark/light force a running session.

func ResolveThemePreference added in v0.24.0

func ResolveThemePreference(persisted, env string) ThemePreference

type ThemeResolution added in v0.24.0

type ThemeResolution struct {
	Theme Theme
	Name  string
}

ThemeResolution is the pure result of resolving a preference against one terminal snapshot and, for custom preferences, one accepted source file.

func ResolveTheme added in v0.24.0

func ResolveTheme(preference string, source *ThemeSource, profile TerminalProfile) ThemeResolution

ResolveTheme performs no I/O. Custom themes always overlay TerminalTheme, so omitted roles continue to follow the controlling terminal.

type ThemeSource added in v0.24.0

type ThemeSource struct {
	Name   string
	Path   string
	Digest [sha256.Size]byte
	File   ThemeFile
}

ThemeSource is an immutable, fully validated custom theme revision. Runtime profile changes resolve this value without touching the filesystem again.

func LoadThemeSource added in v0.24.0

func LoadThemeSource(zutHome, preference string) (*ThemeSource, error)

LoadThemeSource resolves and validates a custom source. Built-in selections have no source and return nil, nil. The file limit prevents a polling reload from allocating an unbounded partial write.

type ToolCallView

type ToolCallView struct {
	ID   string
	Name string
	// Revision is advanced by the owning interactive state whenever a
	// visible field changes. A non-zero revision lets the live render cache
	// avoid rescanning a large result string on every redraw.
	Revision uint64
	Args     string // rendered argument summary
	Preview  string // side-effect-free result shown before confirmation
	Result   string // rendered result preview (truncated)
	Error    bool
	Done     bool

	// Streaming is true while the model is still typing the tool
	// call's JSON arguments. The TUI renders a live preview of any
	// interesting string fields (for `write`, the `content`; for
	// `bash`, the `command`) so the user can watch the file being
	// composed. Set to false as soon as EvToolUseEnd arrives.
	Streaming bool

	// RawJSONBuf is the accumulator of every EvToolUseArgs delta
	// the stream has delivered for this tool call. Used by the
	// partial-JSON extractor to peel off the live string value
	// of one named field on each render.
	RawJSONBuf string

	// LivePath is the `path` arg extracted as soon as it parses
	// out of RawJSONBuf. Shown next to the tool name in the header
	// so the user can see which file is being written to.
	LivePath string
}

ToolCallView is a pending tool invocation plus optional result.

type UsageStatsParams added in v0.19.0

type UsageStatsParams struct {
	Usage        provider.Usage
	Subscription bool
}

UsageStatsParams describes cumulative usage shown by compact status surfaces.

type View

type View struct {
	Theme      Theme
	ImageProto ImageProtocol // how to render inline images in this terminal
	Messages   []provider.Message
	// MessagesRevision is supplied by the owning transcript when available.
	// Append-only revisions let Build reuse prior message rows without
	// hashing every existing payload. Zero keeps the standalone hash cache
	// behaviour for callers that do not have a revision source.
	MessagesRevision uint64
	// RenderCacheRevision changes whenever a visual rendering dependency
	// changes. Interactive mode includes it in its stable transcript key so
	// the outer cache cannot bypass View's own theme/layout cache checks.
	RenderCacheRevision uint64

	Streaming       string // current assistant text delta
	StreamingActive bool
	ToolCalls       []ToolCallView // tool calls in flight or completed

	// Startup resource fields list host-loaded inputs before the transcript
	// without creating provider messages or persisted entries.
	StartupAgentName      string
	StartupContextPaths   []string
	StartupExtensionNames []string
	StartupSkillNames     []string
	StatusLine            string
	Err                   string

	// FlatTools renders tool calls without the bordered panel: a quiet
	// header line per call plus indented, frameless output. The
	// truncation/expand behaviour and theme colors are unchanged.
	// False (the default) keeps the bordered box.
	FlatTools bool

	// CompactUser renders sent user messages as a single quiet gutter
	// line per wrapped row instead of a tinted bubble with a blank
	// padding row above and below. False (the default) keeps the
	// padded, background-tinted bubble.
	CompactUser bool

	// CompactMode reduces visual chrome in the transcript. Tool calls
	// render with a flat header and no bordered panel, and sent user
	// messages render without padded background bubbles. False keeps
	// the current spacious rendering.
	CompactMode bool

	// ExpandAll forces every long tool result to render in full.
	// Toggled from the tui by ctrl+o. When false, results longer than
	// ToolCollapseLines collapse to ToolCollapsePreview lines plus a
	// "... (N more lines, M total, ctrl+o to expand)" footer.
	ExpandAll bool

	// TailLimit caps how many messages from the END of Messages are
	// rendered. Messages older than the limit emit a zero-row slice
	// so first paint after a session resume doesn't pay the markdown
	// / chroma cost for the entire transcript at once. 0 means no
	// limit (render everything, the historical behaviour). Interactive
	// raises the cap when the user scrolls past the top of the
	// already-rendered tail.
	TailLimit int
	// contains filtered or unexported fields
}

View turns a transcript + live state into a slice of styled lines, already wrapped to width.

func (*View) AdoptRenderCacheFrom added in v0.11.1

func (v *View) AdoptRenderCacheFrom(snapshot *View)

AdoptRenderCacheFrom transfers caches produced from a render snapshot back to the owning view. Callers must ensure the transcript revision is still current before adopting them.

func (*View) Build

func (v *View) Build(width int) []string

Build returns the chat log lines for the given width.

func (*View) BuildLive

func (v *View) BuildLive(width int) []string

BuildLive renders only in-flight assistant/tool/error state. Main-screen scrollback renderers can keep these rows outside the immutable transcript so native scrolling stays stable while a turn streams.

func (*View) BuildWithAnchors

func (v *View) BuildWithAnchors(width int) ([]string, []MessageAnchor)

BuildWithAnchors is like Build but additionally reports the first row occupied by each message in v.Messages. Callers that need to scroll to a specific turn (the /jump dialog) use the anchor slice to map a message index back to a row offset.

func (*View) CloneForRender added in v0.11.1

func (v *View) CloneForRender() *View

CloneForRender returns an immutable render snapshot. Its mutable cache maps are copied so a renderer owner can build outside the interactive state mutex without racing the next snapshot.

func (*View) InvalidateRenderCache

func (v *View) InvalidateRenderCache()

InvalidateRenderCache drops all cached message renders. The tui calls this when the transcript is replaced wholesale (/compact, /clear, session swap) since messages can be replaced in place and a content-hash miss alone doesn't reclaim the old entries.

func (*View) RenderToolCall

func (v *View) RenderToolCall(tc ToolCallView, width int) []string

RenderToolCall renders one live or completed tool call using the same presentation as tool calls in the main transcript. Dialogs with isolated agent loops use this to avoid maintaining a second tool renderer.

Jump to

Keyboard shortcuts

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