Documentation
¶
Overview ¶
Package ui contains the lean TUI presentation layer: terminal rendering, input widgets, reusable views, and plain view-models.
This package may use the shared TUI rendering vocabulary, but it must not directly import docker-agent runtime or driver packages. Runtime events and app commands are translated by the parent leantui controller package.
Index ¶
- Constants
- func ComposeLine(left, right string, width int) string
- func DisplayWidth(s string) int
- func EnsureToolDefinition(toolCall tools.ToolCall, toolDef tools.Tool) tools.Tool
- func FormatTokens(n int64) string
- func KittyImageSequence(pngData []byte, cols, rows int) string
- func PadRight(s string, w int) string
- func RenderAssistantLines(text string, width int) []string
- func RenderBar(pct float64) string
- func RenderContext(d StatusModel) string
- func RenderInlineImage(img InlineImage, width int) []string
- func RenderNoticeLines(prefix, text string, width int, style lipgloss.Style) []string
- func RenderPendingUserLines(text string, width int) []string
- func RenderReasoningLines(text string, width int) []string
- func RenderStatus(d StatusModel, width int) []string
- func RenderTool(t ToolView, width int) []string
- func RenderToolOutput(output string, width int) []string
- func RenderToolWithState(t *ToolView, width, frame int, sessionState service.SessionStateReader) []string
- func RenderUserLines(text string, width int) []string
- func RenderUserLinesWith(text string, width int, promptStyle, textStyle lipgloss.Style) []string
- func RuneWidth(r rune) int
- func StAccent() lipgloss.Style
- func StBold() lipgloss.Style
- func StError() lipgloss.Style
- func StMuted() lipgloss.Style
- func StPlaceholder() lipgloss.Style
- func StPrimary() lipgloss.Style
- func StReasoning() lipgloss.Style
- func StSecondary() lipgloss.Style
- func StSuccess() lipgloss.Style
- func StToolBox(width int) lipgloss.Style
- func StWarning() lipgloss.Style
- func ToolViewID(toolCall tools.ToolCall) string
- func Truncate(s string, w int) string
- func WrapANSI(s string, w int) []string
- type Autocomplete
- type Command
- type CommandKind
- type ConfirmModel
- type Editor
- func (e *Editor) Backspace()
- func (e *Editor) DeleteForward()
- func (e *Editor) DeleteToLineEnd()
- func (e *Editor) DeleteToLineStart()
- func (e *Editor) DeleteWordBack()
- func (e *Editor) Down(termWidth int) bool
- func (e *Editor) HistoryNext()
- func (e *Editor) HistoryPrev()
- func (e *Editor) Insert(runes []rune)
- func (e *Editor) InsertNewline()
- func (e *Editor) IsEmpty() bool
- func (e *Editor) Layout(termWidth int) (lines []string, curRow, curCol int)
- func (e *Editor) MoveLeft()
- func (e *Editor) MoveLineEnd()
- func (e *Editor) MoveLineStart()
- func (e *Editor) MoveRight()
- func (e *Editor) MoveWordLeft()
- func (e *Editor) MoveWordRight()
- func (e *Editor) RememberHistory(s string)
- func (e *Editor) Reset()
- func (e *Editor) SetText(s string)
- func (e *Editor) Text() string
- func (e *Editor) Up(termWidth int) bool
- type InlineImage
- type InputParser
- type Key
- type KeyType
- type PendingUserKind
- type PendingUserMessage
- type Renderer
- type Screen
- type StatusModel
- type Terminal
- type ToolResult
- type ToolTracker
- func (t *ToolTracker) ByIDLen() int
- func (t *ToolTracker) Empty() bool
- func (t *ToolTracker) Finish(id string, result ToolResult) *ToolView
- func (t *ToolTracker) ForEach(fn func(*ToolView))
- func (t *ToolTracker) Get(id string) *ToolView
- func (t *ToolTracker) Len() int
- func (t *ToolTracker) Remove(id string)
- func (t *ToolTracker) Reset()
- func (t *ToolTracker) Upsert(agentName string, toolCall tools.ToolCall, toolDef tools.Tool, ...)
- type ToolView
- type Transcript
- func (t *Transcript) AddBlock(render func(width int) []string)
- func (t *Transcript) AppendAssistant(content string)
- func (t *Transcript) AppendReasoning(content string)
- func (t *Transcript) BlockCount() int
- func (t *Transcript) BlockLines(index, width int) []string
- func (t *Transcript) Clear()
- func (t *Transcript) ClearActive()
- func (t *Transcript) FinishTool(id string, result ToolResult, sessionState service.SessionStateReader)
- func (t *Transcript) FlushPending()
- func (t *Transcript) Lines(width, spinnerFrame int, busy bool, sessionState service.SessionStateReader, ...) []string
- func (t *Transcript) RemoveTool(id string)
- func (t *Transcript) Tool(id string) *ToolView
- func (t *Transcript) ToolByIDCount() int
- func (t *Transcript) ToolCount() int
- func (t *Transcript) UpsertTool(agentName string, toolCall tools.ToolCall, toolDef tools.Tool, ...)
- type UsageSnapshot
- type UsageTracker
- func (u *UsageTracker) Active() (UsageSnapshot, bool)
- func (u *UsageTracker) Empty() bool
- func (u *UsageTracker) Record(sessionID string, snapshot UsageSnapshot)
- func (u *UsageTracker) Reset()
- func (u *UsageTracker) RootSessionID() string
- func (u *UsageTracker) StreamStarted(sessionID string)
- func (u *UsageTracker) StreamStopped()
- func (u *UsageTracker) TotalCost() float64
Constants ¶
const ( PromptText = "❯ " PromptWidth = 2 Continuation = " " )
const ContextBarWidth = 10
ContextBarWidth is the cell width of the context-usage gauge.
const MaxToolOutputLines = 12
Variables ¶
This section is empty.
Functions ¶
func ComposeLine ¶
ComposeLine right-aligns right within width, truncating left if necessary.
func DisplayWidth ¶
DisplayWidth returns the rendered cell width of s, ignoring ANSI escape sequences.
func EnsureToolDefinition ¶
EnsureToolDefinition fills a missing tool definition name from the call.
func FormatTokens ¶
FormatTokens formats a token count for compact status display.
func KittyImageSequence ¶
KittyImageSequence encodes PNG data as a kitty graphics escape sequence.
func RenderAssistantLines ¶
RenderAssistantLines renders an assistant message as markdown. Each returned line is guaranteed to fit within width so the differential renderer's row accounting stays correct.
func RenderContext ¶
func RenderContext(d StatusModel) string
RenderContext renders the context and cost portion of the status.
func RenderInlineImage ¶
func RenderInlineImage(img InlineImage, width int) []string
RenderInlineImage renders an inline image label and kitty image sequence.
func RenderNoticeLines ¶
func RenderPendingUserLines ¶
func RenderReasoningLines ¶
RenderReasoningLines renders agent reasoning as dimmed italic text.
func RenderStatus ¶
func RenderStatus(d StatusModel, width int) []string
RenderStatus builds the two-line footer:
<working dir> ⎇ <branch> <agent> <context bar> <pct> · <tokens> · <cost> <model> · <effort>
func RenderTool ¶
RenderTool renders a tool call with the same renderer registry used by the full TUI. This keeps built-in tools and registered custom renderers visually consistent between the normal and lean interfaces.
func RenderToolOutput ¶
RenderToolOutput renders plain streamed shell output.
func RenderToolWithState ¶
func RenderToolWithState(t *ToolView, width, frame int, sessionState service.SessionStateReader) []string
RenderToolWithState renders a tool call using session state.
func RenderUserLines ¶
RenderUserLines renders a submitted user message as committed scrollback, echoing it with the same prompt marker used by the input box.
func RenderUserLinesWith ¶
func StPlaceholder ¶
func StReasoning ¶
func StSecondary ¶
func ToolViewID ¶
ToolViewID returns a stable id for a tool call view.
Types ¶
type Autocomplete ¶
type Autocomplete struct {
Active bool
// contains filtered or unexported fields
}
Autocomplete drives the slash-command popup. It is active whenever the input is a partial command: a single token starting with "/" and no spaces yet.
func NewAutocomplete ¶
func NewAutocomplete() *Autocomplete
func (*Autocomplete) Current ¶
func (a *Autocomplete) Current() (Command, bool)
func (*Autocomplete) Dismiss ¶
func (a *Autocomplete) Dismiss()
func (*Autocomplete) MoveDown ¶
func (a *Autocomplete) MoveDown()
func (*Autocomplete) MoveUp ¶
func (a *Autocomplete) MoveUp()
func (*Autocomplete) Render ¶
func (a *Autocomplete) Render(width int) []string
Render returns the popup rows (top to bottom) for the given width.
func (*Autocomplete) SetCommands ¶
func (a *Autocomplete) SetCommands(cmds []Command)
func (*Autocomplete) Sync ¶
func (a *Autocomplete) Sync(input string) bool
Sync recomputes the popup state from the current editor text. It returns true while the popup is showing.
type Command ¶
type Command struct {
Name string
Desc string
Kind CommandKind
}
func FilterCommands ¶
FilterCommands returns the commands whose name has the given prefix, built-in commands first, then agent commands, each group alphabetically sorted.
type CommandKind ¶
type CommandKind int
CommandKind distinguishes built-in lean-TUI commands (handled locally) from agent-provided commands and skills (resolved and sent to the agent).
const ( CmdBuiltin CommandKind = iota CmdAgent )
type ConfirmModel ¶
ConfirmModel holds a pending tool-approval prompt.
func (*ConfirmModel) Render ¶
func (c *ConfirmModel) Render(width int) []string
type Editor ¶
type Editor struct {
// contains filtered or unexported fields
}
Editor is a multi-line text input. The buffer is a flat rune slice with the cursor expressed as an index into it; newlines are stored literally so the same structure handles single-line prompts and pasted multi-line text. All visual wrapping is derived on demand from a given content width.
func (*Editor) DeleteForward ¶
func (e *Editor) DeleteForward()
func (*Editor) DeleteToLineEnd ¶
func (e *Editor) DeleteToLineEnd()
func (*Editor) DeleteToLineStart ¶
func (e *Editor) DeleteToLineStart()
func (*Editor) DeleteWordBack ¶
func (e *Editor) DeleteWordBack()
func (*Editor) HistoryNext ¶
func (e *Editor) HistoryNext()
func (*Editor) HistoryPrev ¶
func (e *Editor) HistoryPrev()
func (*Editor) InsertNewline ¶
func (e *Editor) InsertNewline()
func (*Editor) Layout ¶
Layout renders the editor for the given terminal width, returning one styled string per physical row along with the hardware cursor position (row within the returned slice, column in terminal cells).
func (*Editor) MoveLineEnd ¶
func (e *Editor) MoveLineEnd()
func (*Editor) MoveLineStart ¶
func (e *Editor) MoveLineStart()
func (*Editor) MoveWordLeft ¶
func (e *Editor) MoveWordLeft()
func (*Editor) MoveWordRight ¶
func (e *Editor) MoveWordRight()
func (*Editor) RememberHistory ¶
RememberHistory records a submitted entry and resets the history cursor.
type InlineImage ¶
InlineImage is a PNG image prepared for inline kitty-protocol rendering.
type InputParser ¶
type InputParser struct {
// contains filtered or unexported fields
}
InputParser turns raw terminal bytes into key events. It is stateful only to reassemble bracketed-paste payloads, which may span several reads.
func (*InputParser) Feed ¶
func (p *InputParser) Feed(b []byte) []Key
type Key ¶
Key is a single decoded keyboard event. For KeyRune and KeyPaste the decoded characters are carried in runes; every other key type carries no payload.
type KeyType ¶
type KeyType int
const ( KeyNone KeyType = iota KeyRune KeyPaste KeyEnter KeyAltEnter // insert a literal newline (multi-line input) KeyTab KeyShiftTab KeyBackspace KeyDelete KeyUp KeyDown KeyLeft KeyRight KeyWordLeft KeyWordRight KeyHome KeyEnd KeyEsc KeyCtrlC KeyCtrlD KeyCtrlU // delete to start of line KeyCtrlK // delete to end of line KeyCtrlW // delete word backwards KeyCtrlL // redraw )
type PendingUserKind ¶
type PendingUserKind int
const ( PendingUserSteer PendingUserKind = iota PendingUserFollowUp )
type PendingUserMessage ¶
type PendingUserMessage struct {
Display string
Content string
Kind PendingUserKind
}
type Renderer ¶
type Renderer struct {
// contains filtered or unexported fields
}
Renderer draws the whole conversation as a single, growing array of lines and keeps it in sync with the terminal using minimal, differential updates. It writes to the normal screen buffer (never the alternate screen): content that scrolls off the top becomes immutable terminal scrollback, exactly like a regular program's output, while the bottom of the array (the input box and status footer) is rewritten in place.
The model mirrors how Claude Code / pi render: each frame the controller produces the full set of lines; the renderer diffs them against the previous frame and rewrites only the changed rows within the visible viewport, letting the terminal scroll naturally when content is appended past the bottom.
func (*Renderer) EraseBelow ¶
EraseBelow drops everything from buffer row `line` downward (the interactive chrome), leaving the cursor on that now-blank row so the shell prompt returns directly beneath the conversation. Used on exit.
func (*Renderer) Frame ¶
Frame reconciles the screen with newLines and places the hardware cursor at (cursorLine, cursorCol), where cursorLine is an index into newLines.
func (*Renderer) Repaint ¶
func (r *Renderer) Repaint()
Repaint forces the next frame to clear the screen and redraw from scratch.
func (*Renderer) SetSize ¶
SetSize records a new terminal size and forces a clean repaint on the next frame, since wrapping and the viewport both change with the dimensions.
func (*Renderer) ViewportTop ¶
type Screen ¶
type Screen struct {
Transcript *Transcript
Editor *Editor
Autocomplete *Autocomplete
Status StatusModel
Confirm *ConfirmModel
}
Screen aggregates the lean TUI presentation models and lays out a full frame.
func (*Screen) Frame ¶
func (s *Screen) Frame(width, _, spinnerFrame int, busy bool, sessionState service.SessionStateReader, pendingUsers []PendingUserMessage) (lines []string, cursorLine, cursorCol int)
Frame produces the full terminal frame and cursor position.
type StatusModel ¶
type StatusModel struct {
WorkingDir string
Branch string
Agent string
Model string
Thinking string
ContextLength int64
ContextLimit int64
Tokens int64 // input + output tokens used so far
Cost float64
CostKnown bool
}
StatusModel is the snapshot of run state shown in the footer.
type Terminal ¶
type Terminal struct {
// contains filtered or unexported fields
}
Terminal owns the raw-mode TTY: it switches the input file descriptor into raw mode, exposes a cancelable reader for keyboard input, a buffered writer for output, and the current window size. It deliberately never enters the alternate screen so the conversation is written to (and scrolls in) the normal terminal buffer.
func (*Terminal) Reader ¶
func (t *Terminal) Reader() cancelreader.CancelReader
func (*Terminal) Resized ¶
Resized blocks until the terminal is resized, then refreshes the cached dimensions and reports the new size. It returns ok=false once the resize channel is closed during shutdown.
type ToolResult ¶
type ToolResult struct {
Response string
Result *tools.ToolCallResult
AgentName string
ToolDefinition tools.Tool
Images []InlineImage
}
ToolResult is the runtime-free data needed to finish a tool call view.
type ToolTracker ¶
type ToolTracker struct {
// contains filtered or unexported fields
}
ToolTracker holds the render state of in-flight tool calls, keyed by id and kept in call order so the conversation shows them as they arrive.
func NewToolTracker ¶
func NewToolTracker() *ToolTracker
func (*ToolTracker) ByIDLen ¶
func (t *ToolTracker) ByIDLen() int
ByIDLen reports the number of tracked tool-call ids.
func (*ToolTracker) Empty ¶
func (t *ToolTracker) Empty() bool
Empty reports whether there are no tracked tool calls.
func (*ToolTracker) Finish ¶
func (t *ToolTracker) Finish(id string, result ToolResult) *ToolView
Finish marks a tool call complete and returns an immutable snapshot. It returns nil when there is nothing to render.
func (*ToolTracker) ForEach ¶
func (t *ToolTracker) ForEach(fn func(*ToolView))
ForEach visits the tracked tools in call order, skipping nil entries.
func (*ToolTracker) Get ¶
func (t *ToolTracker) Get(id string) *ToolView
Get returns a tracked tool call by id.
func (*ToolTracker) Len ¶
func (t *ToolTracker) Len() int
Len reports the number of tracked tool calls.
func (*ToolTracker) Remove ¶
func (t *ToolTracker) Remove(id string)
Remove deletes a tracked tool call by id.
func (*ToolTracker) Upsert ¶
func (t *ToolTracker) Upsert(agentName string, toolCall tools.ToolCall, toolDef tools.Tool, status tuitypes.ToolStatus)
Upsert creates or updates a tracked tool call. Argument fragments streamed while the call is still pending are concatenated.
type ToolView ¶
type ToolView struct {
// contains filtered or unexported fields
}
ToolView is the render state of a single tool call. It deliberately stores the same TUI message shape used by the full-screen TUI so the lean renderer can delegate the visual representation to pkg/tui/components/tool.
func NewToolView ¶
func NewToolView(agentName string, toolCall tools.ToolCall, toolDef tools.Tool, status tuitypes.ToolStatus) *ToolView
NewToolView creates a tool call render model.
func (*ToolView) SetImages ¶
func (t *ToolView) SetImages(images []InlineImage)
type Transcript ¶
type Transcript struct {
// contains filtered or unexported fields
}
Transcript owns everything that scrolls: the finalized conversation blocks, the in-progress streamed block, and the in-flight tool calls. Committed blocks are immutable scrollback; the pending block and tool calls are the live region that changes each frame until they finalize into blocks.
func (*Transcript) AddBlock ¶
func (t *Transcript) AddBlock(render func(width int) []string)
AddBlock appends a finalized, lazily-rendered block to the conversation.
func (*Transcript) AppendAssistant ¶
func (t *Transcript) AppendAssistant(content string)
AppendAssistant appends streamed assistant text.
func (*Transcript) AppendReasoning ¶
func (t *Transcript) AppendReasoning(content string)
AppendReasoning appends streamed reasoning text.
func (*Transcript) BlockCount ¶
func (t *Transcript) BlockCount() int
BlockCount reports the number of committed transcript blocks.
func (*Transcript) BlockLines ¶
func (t *Transcript) BlockLines(index, width int) []string
BlockLines renders a committed block by index.
func (*Transcript) Clear ¶
func (t *Transcript) Clear()
Clear drops all transcript content and active tool state.
func (*Transcript) ClearActive ¶
func (t *Transcript) ClearActive()
ClearActive drops the live region (the streamed block and any in-flight tool calls) while keeping the committed scrollback intact. Used when starting a new session.
func (*Transcript) FinishTool ¶
func (t *Transcript) FinishTool(id string, result ToolResult, sessionState service.SessionStateReader)
FinishTool commits a completed tool call as an immutable block.
func (*Transcript) FlushPending ¶
func (t *Transcript) FlushPending()
FlushPending finalizes the in-progress streamed block into the conversation.
func (*Transcript) Lines ¶
func (t *Transcript) Lines(width, spinnerFrame int, busy bool, sessionState service.SessionStateReader, pendingUsers []PendingUserMessage) []string
Lines renders everything that scrolls: finalized blocks, the in-progress streamed block, running tool calls, and user messages waiting to be accepted by the runtime. A blank line separates each entry. The spinner is shown only while busy with nothing yet streaming.
func (*Transcript) RemoveTool ¶
func (t *Transcript) RemoveTool(id string)
RemoveTool removes an in-flight tool call by id.
func (*Transcript) Tool ¶
func (t *Transcript) Tool(id string) *ToolView
Tool returns an in-flight tool call by id.
func (*Transcript) ToolByIDCount ¶
func (t *Transcript) ToolByIDCount() int
ToolByIDCount reports the number of tracked tool-call ids.
func (*Transcript) ToolCount ¶
func (t *Transcript) ToolCount() int
ToolCount reports the number of in-flight tool calls.
func (*Transcript) UpsertTool ¶
func (t *Transcript) UpsertTool(agentName string, toolCall tools.ToolCall, toolDef tools.Tool, status tuitypes.ToolStatus)
UpsertTool creates or updates an in-flight tool call.
type UsageSnapshot ¶
UsageSnapshot is the per-session token and cost usage summarized in the status footer.
type UsageTracker ¶
type UsageTracker struct {
// contains filtered or unexported fields
}
UsageTracker aggregates per-session token usage so the footer can show the active session's context window alongside the total cost of the whole run (the root session plus any nested agent sessions). It keeps a stack of in-flight sessions so the "active" session is whichever stream is on top.
func NewUsageTracker ¶
func NewUsageTracker() *UsageTracker
func (*UsageTracker) Active ¶
func (u *UsageTracker) Active() (UsageSnapshot, bool)
Active returns the usage of the session whose context the footer should show: the top of the active stack, else the root, else the most recent, else the sole recorded session.
func (*UsageTracker) Empty ¶
func (u *UsageTracker) Empty() bool
func (*UsageTracker) Record ¶
func (u *UsageTracker) Record(sessionID string, snapshot UsageSnapshot)
Record stores usage for a session, adopting the first session seen as the root when no stream has started yet.
func (*UsageTracker) Reset ¶
func (u *UsageTracker) Reset()
func (*UsageTracker) RootSessionID ¶
func (u *UsageTracker) RootSessionID() string
func (*UsageTracker) StreamStarted ¶
func (u *UsageTracker) StreamStarted(sessionID string)
StreamStarted pushes a newly-started session onto the active stack, adopting the first one as the root session.
func (*UsageTracker) StreamStopped ¶
func (u *UsageTracker) StreamStopped()
StreamStopped pops the most recently-started session off the active stack.
func (*UsageTracker) TotalCost ¶
func (u *UsageTracker) TotalCost() float64