ui

package
v0.6.2 Latest Latest
Warning

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

Go to latest
Published: Oct 8, 2026 License: MIT Imports: 22 Imported by: 0

Documentation

Index

Constants

View Source
const (
	DefaultTitleRunningIcon = "⏳"
	DefaultTitleAskingIcon  = "💬"
	DefaultTitleWaitingIcon = "💤"
)

Default icons for the three states TerminalTitle shows. The hourglass means a turn is running, the speech balloon means something is waiting for you to answer it — a Question, a path or MCP confirmation, the pause prompt — and the sleep symbol means nothing is running and the composer is open. All three are configurable under ui.terminal_title in config.yaml, and any of them can be set to the empty string for no icon.

Variables

This section is empty.

Functions

func FormatChars

func FormatChars(chars int) string

FormatChars formats character count in a human-readable way (e.g., "1.5k")

func FormatContextStr

func FormatContextStr(totalTokens, contextLimit int) string

FormatContextStr formats context usage for display. It shows what is used and the share of the window in use, e.g. "8.4k 1%": the raw count on its own never said whether anything needed doing about it.

func FormatContextStrWithProcs

func FormatContextStrWithProcs(totalTokens, contextLimit, running int) string

FormatContextStrWithProcs adds the background-process count to the context status: "8.4k 1% ★2" when running > 0, the plain base otherwise. Only running processes count — finished ones already surface through the inbox — and zero keeps the header exactly as before. It counts every running process; use FormatContextStrWithProcs2 to split ephemeral from persistent.

func FormatContextStrWithProcs2

func FormatContextStrWithProcs2(totalTokens, contextLimit, ephemeral, persistent int) string

FormatContextStrWithProcs2 splits the count: blue "★N" for ephemeral processes (die at turn end), red "★N!" for persistent ones (survive, need stopping). Either zero hides its star; both zero keep the header exactly as before.

func FormatContextUsed

func FormatContextUsed(used, window int) string

FormatContextUsed reports how much of the window a turn's peak context reached, e.g. "8.4k / 1.0M (1%)". The used count and the window share the same k/M scale (FormatTokens), and the share rounds the way FormatContextStr rounds it. No configured window (window <= 0) leaves the bare used count, mirroring how the per-step status drops the percent.

func FormatDuration

func FormatDuration(d time.Duration) string

FormatDuration formats a duration in a human-readable way, omitting zero values

func FormatShellDisplay

func FormatShellDisplay(cmd, workingDir, workspaceRoot string) string

FormatShellDisplay renders shell command and working directory relative to workspace.

func FormatTokens

func FormatTokens(n int) string

FormatTokens renders a token count on a k/M scale: "8.4k", "128k", "1.0M". The M scale starts just under a million so 999999 rounds to "1.0M" rather than an unreadable "1000k".

func FormatToolArgs

func FormatToolArgs(args map[string]any) string

FormatToolArgs formats tool arguments for compact display. Keys are sorted so the terminal line is deterministic.

func GetResultSummary

func GetResultSummary(result any) string

GetResultSummary extracts meaningful size info from tool result

func HistoryPathForWorkspace added in v0.4.2

func HistoryPathForWorkspace(home, workspaceDir string) string

HistoryPathForWorkspace returns the input history file for one workspace directory. The path is under ~/.kvit-coder/history/ keyed by the sha256 of the absolute workspace path (same scheme as permission stores), so every directory gets its own arrow-up history and listing the directory shows every stored workspace. An empty workspaceDir resolves against the current working directory; when that also fails the legacy global path is returned so the caller still has somewhere to read and write.

func ImageLabel

func ImageLabel(n int) string

ImageLabel returns the in-text label for the nth staged image (1-based), e.g. "[image1]".

func LegacyHistoryPath added in v0.4.2

func LegacyHistoryPath(home string) string

LegacyHistoryPath is the pre-per-directory input history file: a single global file in the user's home. It is kept as the seed for per-directory histories, not as the live path.

func LoadHistory

func LoadHistory(path string) ([]string, error)

LoadHistory loads history from a file

func LoadWorkspaceHistory added in v0.4.2

func LoadWorkspaceHistory(home, workspaceDir string) ([]string, string)

LoadWorkspaceHistory loads the history for one workspace directory. When the per-directory file does not exist yet but the legacy global file does, the global entries seed the workspace so a first run in a new directory starts from the familiar history and then diverges on its own.

func MakePrompt

func MakePrompt(text string) string

MakePrompt creates a colored prompt with white text on gray background

func SaveHistory

func SaveHistory(path string, history []string) error

SaveHistory saves history to a file

func ShortenBlockMessage

func ShortenBlockMessage(blockMsg string) string

ShortenBlockMessage creates a user-friendly short summary of a block message. The full message (with diff) is still sent to the LLM.

func SingleLine

func SingleLine(s string, maxChars int) string

SingleLine collapses a message to one display line: newlines, carriage returns and tabs become spaces, runs of whitespace fold to one, and the result is trimmed and truncated to maxChars (with an ellipsis) when maxChars is positive.

func Transcript

func Transcript(card *report.Report, ans *CardAnswer) string

Transcript returns the scrollback copy of a report with the same colors as the card: headline, block lines, dim fields, bright options and muted instructions. Details are always expanded, so nothing the card kept folded is lost from the history.

func TranscriptWidth

func TranscriptWidth(card *report.Report, ans *CardAnswer, width int) string

TranscriptWidth is Transcript wrapped to an explicit width, so tests and callers that know the terminal can pin it.

Types

type CardAnswer

type CardAnswer struct {
	// BlockID and OptionID say what was answered, for the record.
	BlockID  string
	OptionID string
	// Effect is one of report.EffectDispatch, EffectCollect or EffectResolve.
	Effect string
	// Prompt is the text the next turn runs, for a dispatch. Empty otherwise.
	Prompt string
	// Number is the digit the user pressed (1-based, as the card showed it),
	// and Label is the option's label. Both are recorded so a scrollback copy
	// of the report can say what was picked without looking the report up
	// again.
	Number int
	Label  string
}

CardAnswer is what the user picked from the report card. The driver turns it into the next turn, or into nothing at all when the effect is "resolve".

func (*CardAnswer) Describe

func (a *CardAnswer) Describe() string

Describe returns the scrollback line for what the user picked, e.g. "[picked 1) Preserve hidden positions — filtered/preserve (dispatch, will run as next turn)]". It returns "" when there is nothing to describe.

type InputModel

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

InputModel wraps the textarea component for multi-line input

func NewInputModel

func NewInputModel(prompt string, history []string) InputModel

NewInputModel creates a new input model with the given prompt

func (InputModel) Cancelled

func (m InputModel) Cancelled() bool

Cancelled returns whether the input was cancelled

func (InputModel) CardAnswerPicked

func (m InputModel) CardAnswerPicked() *CardAnswer

CardAnswerPicked returns what the user chose from the card, or nil.

func (InputModel) Init

func (m InputModel) Init() tea.Cmd

Init initializes the input model

func (InputModel) PastedImages

func (m InputModel) PastedImages() []string

PastedImages returns the images staged by the paste key plus any seeded with SetStagedImages, in label order.

func (*InputModel) SetCompletionBaseDir

func (m *InputModel) SetCompletionBaseDir(dir string)

SetCompletionBaseDir tells path completion which directory relative paths resolve against (the workspace root). Empty falls back to the working directory at completion time.

func (*InputModel) SetImagePasteHandler

func (m *InputModel) SetImagePasteHandler(fn func() (string, error))

SetImagePasteHandler installs the clipboard-image stager behind the paste key. Passing nil disables image paste.

func (*InputModel) SetReport

func (m *InputModel) SetReport(rep *report.Report)

SetReport installs the report the last turn ended with. Passing nil, which is what happens after any turn that did not end with one, leaves the composer exactly as it was before reports existed.

func (*InputModel) SetStagedImages

func (m *InputModel) SetStagedImages(paths []string)

SetStagedImages seeds the composer with images staged before it opened (:image, :paste). They take the first labels, so a paste below continues the numbering instead of restarting at [image1].

func (*InputModel) SetWakePoll

func (m *InputModel) SetWakePoll(fn func() (int, string))

SetWakePoll installs the inbox waiter behind the composer. A nil poll disables wake.

func (InputModel) Submitted

func (m InputModel) Submitted() bool

Submitted returns whether the input was submitted

func (InputModel) Update

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

Update handles input events

func (InputModel) Value

func (m InputModel) Value() string

Value returns the submitted value

func (InputModel) View

func (m InputModel) View() string

View renders the input model

func (InputModel) WakeFired

func (m InputModel) WakeFired() bool

WakeFired reports whether the composer submitted itself on inbox activity.

type JSONOutput

type JSONOutput struct {
	Content string     `json:"content"`         // The final LLM response
	Stats   *JSONStats `json:"stats,omitempty"` // Statistics (tokens, cost, etc.)
	// Report is the structured report the turn ended with, when it ended with
	// one. A consumer that wants the status or the blocks reads it here rather
	// than parsing Content, which holds the same report as text.
	Report *report.Report `json:"report,omitempty"`
}

JSONOutput represents the structured output for --json mode

type JSONStats

type JSONStats struct {
	Session          string  `json:"session,omitempty"`
	PromptTokens     int     `json:"prompt_tokens"`
	CompletionTokens int     `json:"completion_tokens"`
	TotalTokens      int     `json:"total_tokens"`
	CacheReadTokens  int     `json:"cache_read_tokens,omitempty"`
	TotalCost        float64 `json:"total_cost_usd,omitempty"`
	CacheDiscount    float64 `json:"cache_discount_usd,omitempty"`
	// The turn's peak context, and the window it was measured against, so a
	// consumer of the document can tell a small turn from a near-full one.
	ContextUsed   int   `json:"context_used"`
	ContextWindow int   `json:"context_window"`
	DurationMs    int64 `json:"duration_ms"`
	Steps         int   `json:"steps"`
}

JSONStats represents statistics in JSON output

type TerminalTitle

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

TerminalTitle sets the terminal's window title, which a tabbed terminal such as the one in VS Code also uses as the tab label. It shows an icon for what is happening — a turn is running, a prompt is waiting for you to answer it, or nothing is running — followed by the session's title, so a window that is not on screen still says whether the agent is still working or has stopped to ask something.

Two processes write it. The front end owns it between turns and around a turn; the agent process borrows it while a prompt of its own is waiting, because the front end is blocked on the agent and cannot see the prompt. They never write at the same time: the front end is inside cmd.Run while the agent has it.

A terminal takes its title from the escape sequence ESC ] 0 ; text BEL, which sets the window title and the icon name in one go and is understood by every terminal in common use. It is a control sequence rather than output: writing it while a turn is printing changes nothing on screen.

Whether the terminal then shows that title is up to the terminal. VS Code in particular ignores it until "terminal.integrated.tabs.title" is set to "${sequence}", and tmux until "set -g set-titles on" is set.

Nothing is written unless the output is a terminal, so a piped or redirected run stays byte-clean.

func NewTerminalTitle

func NewTerminalTitle(out io.Writer) *TerminalTitle

NewTerminalTitle returns a title setter writing to out, which the front end passes as os.Stdout. When out is a file it has to be a terminal, and not a dumb one, or the setter is inert — that is what keeps the escape sequence out of a redirected stream. Setting KVIT_CODER_NO_TITLE in the environment makes it inert too. Anything that is not a file (a buffer in a test) is written to as asked.

func (*TerminalTitle) Asking

func (t *TerminalTitle) Asking(session string)

Asking shows that the turn has stopped to put something to you and is waiting for the answer.

func (*TerminalTitle) Clear

func (t *TerminalTitle) Clear()

Clear empties the title on the way out. There is no way to ask a terminal what its title was before, so the empty title is what a program that has finished leaves behind: shells that set a title from their prompt put their own back at the next prompt.

func (*TerminalTitle) Disable

func (t *TerminalTitle) Disable()

Disable stops the title being touched at all, for ui.terminal_title.enabled set to false.

func (*TerminalTitle) Running

func (t *TerminalTitle) Running(session string)

Running shows that a turn is under way in the named session.

func (*TerminalTitle) SetIcons

func (t *TerminalTitle) SetIcons(running, waiting, asking string)

SetIcons replaces the three state icons. An empty string means that state gets no icon, leaving the session title on its own.

func (*TerminalTitle) Waiting

func (t *TerminalTitle) Waiting(session string)

Waiting shows that the turn has finished and the composer is open.

type Writer

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

Writer provides formatted output with consistent prefixes and optional colors.

func NewWriter

func NewWriter(verboseLines int) *Writer

NewWriter creates a new Writer with the specified verbosity level. verboseLines: 0 = not verbose, >0 = show tool output up to this many lines (half top, half bottom if truncated)

func (*Writer) ActivePlan

func (w *Writer) ActivePlan(planText string)

ActivePlan renders a plan from the <active_plan> format string.

func (*Writer) Agent

func (w *Writer) Agent(msg string)

Agent prints an agent/system message with [agent] prefix.

func (*Writer) Assistant

func (w *Writer) Assistant(msg string)

Assistant prints an assistant message in white. In headless mode, this goes to stdout (the final answer). In JSON mode, it stores the content to be output later with WriteJSONOutput.

The message is markdown, and on a terminal it is styled as such (headings, lists, code, tables) instead of printed verbatim. Piped output, JSON mode, and NO_COLOR/TERM=dumb stay raw markdown, so scripts and logs see exactly what the model wrote. Rendering never fails the turn: on any error the raw message goes through.

func (*Writer) Debug

func (w *Writer) Debug(msg string)

Debug prints a debug message in gray, only if verbose mode is enabled.

func (*Writer) Divider

func (w *Writer) Divider(statsMsg string)

Divider separates the step progress from the final report. It carries the turn timing that used to print as a standalone "[39s: 39s llm + ...]" line after the report, so nothing needs to print after the report itself. A blank line leads so the divider never crowds the last step line. It goes to the progress stream (stderr when headless), keeping stdout clean for the final answer. Quiet and JSON modes print nothing.

func (*Writer) Error

func (w *Writer) Error(msg string)

Error prints an error as one indented line in dim red, same layout as Warn.

func (*Writer) Info

func (w *Writer) Info(msg string)

Info prints an info message in gray, indented like other step lines. Multi-line messages keep every continuation line indented.

func (*Writer) IsHeadless

func (w *Writer) IsHeadless() bool

IsHeadless returns true if headless mode is enabled.

func (*Writer) IsJSONMode

func (w *Writer) IsJSONMode() bool

IsJSONMode returns true if JSON mode is enabled.

func (*Writer) IsVerbose

func (w *Writer) IsVerbose() bool

IsVerbose returns true if verbose mode is enabled.

func (*Writer) Report

func (w *Writer) Report(rep *report.Report)

Report prints the structured report a turn ended with. It goes where the final answer goes — stdout when headless — but never through the markdown renderer: the card is already laid out, and styling it as Markdown would reflow the very columns that make it readable.

On a terminal the lines are wrapped to its width, with continuations hanging under the field they belong to. Piped output is left as it was written, the same rule the markdown renderer follows, so a log or a script sees the layout the agent produced rather than one shaped by whoever ran it.

In JSON mode nothing is printed; the report rides in the JSON document instead, as both the rendered text and the structure behind it.

func (*Writer) SetColorOutput

func (w *Writer) SetColorOutput(stdout io.Writer)

SetColorOutput sets the output for colored text (used by fatih/color package). This should be called when you want colored output to go somewhere other than os.Stdout.

func (*Writer) SetHeadless

func (w *Writer) SetHeadless(headless bool)

SetHeadless enables headless mode where progress goes to stderr and final answer to stdout.

func (*Writer) SetJSONContent

func (w *Writer) SetJSONContent(content string)

SetJSONContent sets the content to be output in JSON mode

func (*Writer) SetJSONMode

func (w *Writer) SetJSONMode(jsonMode bool)

SetJSONMode enables or disables JSON output mode.

func (*Writer) SetMarkdownMode

func (w *Writer) SetMarkdownMode(mode string)

SetMarkdownMode selects when the final answer is styled as markdown: "auto" (the default), "always", or "never". Empty and unknown values are auto.

func (*Writer) SetPromptWatcher

func (w *Writer) SetPromptWatcher(fn func() bool)

SetPromptWatcher installs a function reporting whether a question is waiting for an answer. Progress indicators redraw the line they sit on, so one drawn while a question is on screen erases it; with this set they hold off until the question has been answered.

func (*Writer) SetQuiet

func (w *Writer) SetQuiet(quiet bool)

SetQuiet enables or disables quiet mode (suppresses all output except Assistant messages).

func (*Writer) SetStderr

func (w *Writer) SetStderr(stderr io.Writer)

SetStderr sets a custom stderr writer.

func (*Writer) SetStderrIsTerminal

func (w *Writer) SetStderrIsTerminal(isTTY bool)

SetStderrIsTerminal overrides the detection of whether stderr can be redrawn in place, so a test can exercise both paths without a pseudo-terminal.

func (*Writer) SetStdout

func (w *Writer) SetStdout(stdout io.Writer)

SetStdout sets a custom stdout writer.

func (*Writer) SetStdoutIsTerminal

func (w *Writer) SetStdoutIsTerminal(isTTY bool)

SetStdoutIsTerminal overrides the detection of whether the final answer goes to a terminal, so a test can exercise the styled and raw markdown paths without a pseudo-terminal.

func (*Writer) SetVerbose

func (w *Writer) SetVerbose(lines int)

SetVerbose sets the verbose lines level.

func (*Writer) StartupInfo

func (w *Writer) StartupInfo(msg string)

StartupInfo prints startup information in brown.

func (*Writer) Steering

func (w *Writer) Steering(msg string)

Steering prints a steering prompt in bright green, indented like Info. It marks the moment typing steering becomes available, so it stands out from the gray progress lines around it. Layout and wrapping match Info.

func (*Writer) Thinking

func (w *Writer) Thinking(context, msg string)

Thinking prints one bullet-led step line per model turn: the bullet, the brownish context share with the blue background-process count ("★2") when any are running, and the message in full white. An empty message still prints "● (ctx)" so every step shows its status. Continuation lines of a multi-line message stay indented and wrapped.

func (*Writer) Tool

func (w *Writer) Tool(name, msg string)

Tool prints a tool execution message with [tool:name] prefix (unused, kept for compatibility).

func (*Writer) ToolCall

func (w *Writer) ToolCall(name, argsDisplay, context string)

ToolCall prints a compact gray tool-call line indented under the step header. The context share lives on the header already, so the context argument is accepted for compatibility and ignored. Success summaries are not shown (see ToolResult); only the call itself appears here.

func (*Writer) ToolContext

func (w *Writer) ToolContext(context string)

ToolContext is kept for compatibility. The context share already leads every step header, so printing it again per tool would repeat the status on each call line.

func (*Writer) ToolProgress

func (w *Writer) ToolProgress(dot string)

ToolProgress prints a progress indicator for a long wait: one mark per second, folded into "*" per finished minute (see maxDotsPerLine). Dots (".") are a tool running; colons (":") are the model thinking. Both waits share the indented row under the last tool-call line, so the mark is what tells them apart: a colon row under a Read line is the next model call, not the Read.

func (*Writer) ToolResult

func (w *Writer) ToolResult(summary, duration string)

ToolResult finishes a tool line. A slow tool keeps its "...2s" duration in gray; the per-result size summary ("34 lines, 5.2k chars") is intentionally not shown, so step output stays one header plus its calls. Error and pending summaries still print as indented single lines.

func (*Writer) VerboseLines

func (w *Writer) VerboseLines() int

VerboseLines returns the max lines to show in verbose mode.

func (*Writer) VerboseOutput

func (w *Writer) VerboseOutput(output string) bool

VerboseOutput prints tool output in verbose mode with truncation if needed. Returns true if output was printed.

func (*Writer) Warn

func (w *Writer) Warn(msg string)

Warn prints a warning as one indented line in dim brown. The message is collapsed to a single line and truncated, so a warning never breaks the step layout.

func (*Writer) WriteJSONOutput

func (w *Writer) WriteJSONOutput(stats *JSONStats)

WriteJSONOutput outputs the final JSON result to stdout

Jump to

Keyboard shortcuts

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