Documentation
¶
Overview ¶
Package tui provides a full-screen terminal UI framework for building interactive CLI applications, inspired by modern AI assistants.
Layout ¶
The screen is divided into three vertical regions:
┌─────────────────────────────────────────────────────────┐ │ Scrollable output / conversation history │ │ (auto-scrolls to bottom; Page Up/Down/wheel to scroll) │ ├─────────────────────────────────────────────────────────┤ │ Command palette (visible when / is typed) │ ├─────────────────────────────────────────────────────────┤ │ ┌─────────────────────────────────────────────────┐ │ │ │ > input goes here Ctrl+C to exit │ │ │ └─────────────────────────────────────────────────┘ │ │ myapp │ └─────────────────────────────────────────────────────────┘
Quick Start ¶
t := tui.New(tui.Config{
Theme: tui.ThemeAmber,
Commands: []*tui.Command{
{Name: "clear", Description: "Clear history", Handler: func(_ string) { t.ClearOutput() }},
},
OnSubmit: func(text string) {
t.AddMessage(tui.RoleUser, text)
t.AddMessage(tui.RoleAssistant, "Echo: "+text)
},
})
t.Run(context.Background())
Streaming ¶
For token-by-token responses:
t.StartStreamingAs("GPT-4o")
for chunk := range tokenCh {
t.StreamChunk(chunk)
}
t.StreamComplete()
Themes ¶
Seven built-in themes: ThemeAmber, ThemeBlue, ThemeGreen, ThemePurple, ThemeLight, ThemePlain, ThemeDefault. Look up by name with ThemeByName. Register custom themes with RegisterTheme or via Config.Themes.
Index ¶
- Constants
- Variables
- func CopyToClipboard(text string) string
- func Hyperlink(url, text string) string
- func Notify(message string) string
- func RegisterTheme(t *Theme)
- func SetWindowTitle(title string) string
- func Styled(color Color, text string) string
- func ThemeNames() []string
- type Color
- type Command
- type Config
- type Menu
- type MenuItem
- type MessageRole
- type Panel
- func (p *Panel) AddColumn(child *Panel)
- func (p *Panel) AddMessage(role MessageRole, content string)
- func (p *Panel) AddMessageAs(role MessageRole, label, content string)
- func (p *Panel) AddRow(child *Panel)
- func (p *Panel) Clear()
- func (p *Panel) ClearLine(row int)
- func (p *Panel) ClearRegion(startRow, startCol, endRow, endCol int)
- func (p *Panel) Color() Color
- func (p *Panel) ContentLines() int
- func (p *Panel) HasBorder() bool
- func (p *Panel) IsStreaming() bool
- func (p *Panel) Name() string
- func (p *Panel) ScrollDown(n int)
- func (p *Panel) ScrollOffset() int
- func (p *Panel) ScrollToBottom()
- func (p *Panel) ScrollToTop()
- func (p *Panel) ScrollUp(n int)
- func (p *Panel) Scrollable() bool
- func (p *Panel) SetBorder(enabled bool)
- func (p *Panel) SetColor(color Color)
- func (p *Panel) SetContent(s string)
- func (p *Panel) SetScrollable(scrollable bool)
- func (p *Panel) SetSkipFocus(skip bool)
- func (p *Panel) SetTitle(title string)
- func (p *Panel) Size() (width, height int)
- func (p *Panel) SkipFocus() bool
- func (p *Panel) StartStreaming()
- func (p *Panel) StartStreamingAs(label string)
- func (p *Panel) StartStreamingWithRole(role MessageRole, label string)
- func (p *Panel) StopStreaming()
- func (p *Panel) StreamChunk(chunk string)
- func (p *Panel) StreamComplete()
- func (p *Panel) Styled(color Color, text string) string
- func (p *Panel) StyledWith(name string, text string) string
- func (p *Panel) Title() string
- func (p *Panel) Write(b []byte) (int, error)
- func (p *Panel) WriteAt(row, col int, s string)
- func (p *Panel) WriteString(s string)
- type PanelConfig
- type TUI
- func (t *TUI) AddCommand(cmd *Command)
- func (t *TUI) AddLeft(panel *Panel)
- func (t *TUI) AddMessage(role MessageRole, content string)
- func (t *TUI) AddMessageAs(role MessageRole, label, content string)
- func (t *TUI) AddRight(panel *Panel)
- func (t *TUI) ClearLayout()
- func (t *TUI) ClearOutput()
- func (t *TUI) ClearProgress()
- func (t *TUI) CloseMenu()
- func (t *TUI) Context() context.Context
- func (t *TUI) CreatePanel(cfg PanelConfig) *Panel
- func (t *TUI) CycleFocus()
- func (t *TUI) Exit()
- func (t *TUI) FocusPanel(idx int)
- func (t *TUI) FocusedPanel() *Panel
- func (t *TUI) HasMultiplePanels() bool
- func (t *TUI) IsStreaming() bool
- func (t *TUI) OpenMenu(m *Menu)
- func (t *TUI) Panel(name string) *Panel
- func (t *TUI) RemoveCommand(name string)
- func (t *TUI) Run(ctx context.Context) error
- func (t *TUI) SetAuxLabels(thinking, tool string)
- func (t *TUI) SetInputEnabled(enabled bool)
- func (t *TUI) SetLabels(user, assistant, system string)
- func (t *TUI) SetProgress(label string, value float64)
- func (t *TUI) SetStatus(left, right string)
- func (t *TUI) SetStatusLeft(s string)
- func (t *TUI) SetStatusRight(s string)
- func (t *TUI) SetTheme(theme *Theme)
- func (t *TUI) StartSpinner(text string)
- func (t *TUI) StartStreaming()
- func (t *TUI) StartStreamingAs(label string)
- func (t *TUI) StartStreamingWithRole(role MessageRole, label string)
- func (t *TUI) StopSpinner()
- func (t *TUI) StopStreaming()
- func (t *TUI) StreamChunk(chunk string)
- func (t *TUI) StreamComplete()
- func (t *TUI) TerminalSize() (width, height int)
- func (t *TUI) Theme() *Theme
- func (t *TUI) WriteString(s string)
- type Theme
Constants ¶
const ( ThemePrimary = "primary" ThemeSecondary = "secondary" ThemeError = "error" ThemeDim = "dim" ThemeText = "text" ThemeUser = "user" )
Theme color names for use with Panel.StyledWith.
Variables ¶
var ( // ThemeDefault is the default theme — dark navy background, cyan primary, crimson secondary. ThemeDefault = &Theme{ Name: "default", Primary: 0x4EB8C8, Secondary: 0xC0395A, Text: 0xE8EAF0, UserText: 0x4EB8C8, Dim: 0x7A8492, CodeBG: 0x111A26, CodeText: 0xE8EAF0, Error: 0xC0395A, } // ThemeAmber — warm dark background, amber primary, teal secondary. ThemeAmber = &Theme{ Name: "amber", Primary: 0xE8A87C, Secondary: 0x7EC8A4, Text: 0xCDD6F4, UserText: 0xE8A87C, Dim: 0x6C6F85, CodeBG: 0x1E1A14, CodeText: 0xCDD6F4, Error: 0xF38BA8, } // ThemeBlue — deep dark background, periwinkle primary, sky secondary. ThemeBlue = &Theme{ Name: "blue", Primary: 0x7BA7E8, Secondary: 0x5BC8D8, Text: 0xD0D8F0, UserText: 0x7BA7E8, Dim: 0x5A6070, CodeBG: 0x0D1117, CodeText: 0xD0D8F0, Error: 0xE06C75, } // ThemeGreen — dark terminal, mint primary, gold secondary. ThemeGreen = &Theme{ Name: "green", Primary: 0x7EC87A, Secondary: 0xD4A843, Text: 0xD8E0D0, UserText: 0x7EC87A, Dim: 0x5A6650, CodeBG: 0x0D1A0F, CodeText: 0xD8E0D0, Error: 0xE05050, } // ThemePurple — dark background, lavender primary, rose secondary. ThemePurple = &Theme{ Name: "purple", Primary: 0xB48EE8, Secondary: 0xE87EB4, Text: 0xE0D8F0, UserText: 0xB48EE8, Dim: 0x6A6080, CodeBG: 0x130D1E, CodeText: 0xE0D8F0, Error: 0xF07070, } // ThemeLight — light background, blue primary, green secondary. ThemeLight = &Theme{ Name: "light", Primary: 0x1A56CC, Secondary: 0x0A7A50, Text: 0x1A1A2E, UserText: 0x1A56CC, Dim: 0x666677, CodeBG: 0xE8EAF0, CodeText: 0x1A1A2E, Error: 0xCC2020, } // ThemePlain uses no colors (monochrome). ThemePlain = &Theme{ Name: "plain", } )
Built-in themes.
Functions ¶
func CopyToClipboard ¶ added in v0.7.12
CopyToClipboard writes text to the system clipboard via OSC 52. Supported by: iTerm2, Ghostty, kitty, WezTerm, tmux (with set-clipboard on).
func Hyperlink ¶ added in v0.7.12
Hyperlink wraps text in an OSC 8 hyperlink. Supported by: iTerm2, Kitty, WezTerm, Alacritty, Windows Terminal, VTE (GNOME Terminal), Ghostty, Terminal.app (macOS 10.15+). If url is empty, returns text unchanged.
func Notify ¶ added in v0.7.12
Notify returns an OSC 9 sequence for a desktop notification. Supported by: iTerm2, Ghostty, ConEmu.
func RegisterTheme ¶
func RegisterTheme(t *Theme)
RegisterTheme adds a custom theme to the global registry, keyed by Theme.Name.
func SetWindowTitle ¶ added in v0.7.12
SetWindowTitle returns an OSC 0 sequence to set the window title and icon. Supported by virtually all terminal emulators.
func Styled ¶
Styled returns text wrapped in the given color, reset after. Use the theme color fields directly: t.Theme().Primary, t.Theme().Secondary, etc.
func ThemeNames ¶
func ThemeNames() []string
ThemeNames returns a sorted slice of all registered theme names.
Types ¶
type Color ¶
type Color uint32
Color is a 24-bit RGB color packed as 0xRRGGBB. Zero means "default terminal color".
type Command ¶
type Command struct {
Name string
Description string
Args []string // Optional sub-options shown in palette after a space
Handler func(args string)
}
Command is a slash command that can be registered with the TUI.
type Config ¶
type Config struct {
// Theme controls colors. Defaults to ThemeAmber if nil.
Theme *Theme
// Commands are the slash commands available in the palette.
Commands []*Command
// Themes registers additional themes into the global theme registry,
// making them available via ThemeByName.
Themes []*Theme
// OnSubmit is called when the user presses Enter to submit input.
// The TUI does NOT add a user message automatically; the caller decides.
OnSubmit func(text string)
// OnEscape is called when Escape is pressed and the palette is not active.
OnEscape func()
// OnInterrupt is called when Ctrl+C is pressed. If nil, the TUI exits.
OnInterrupt func()
// UserLabel is the label shown for user messages. Defaults to "You".
UserLabel string
// AssistantLabel is the default label for assistant messages. Defaults to "Assistant".
AssistantLabel string
// SystemLabel is the label shown for system messages. Defaults to "System".
SystemLabel string
// ThinkingLabel is the label shown for thinking messages. Defaults to "Thinking".
ThinkingLabel string
// ToolLabel is the label shown for tool messages. Defaults to "Tool".
ToolLabel string
// HideHeaders suppresses the role header line between messages.
HideHeaders bool
// StatusLeft is optional text shown in the bottom-left status bar.
StatusLeft string
// StatusRight is optional text shown in the bottom-right status bar.
StatusRight string
// ShowCharCount enables the character counter below the input box. Defaults to false.
ShowCharCount bool
// InputEnabled controls whether the input box is shown. Defaults to true.
// When false, the input box, char count, and palette are hidden and
// keyboard input only handles scrolling and Ctrl+C.
InputEnabled *bool
// OnFocusChange is called when panel focus changes via Tab cycling.
// The callback receives the newly focused panel.
OnFocusChange func(panel *Panel)
}
Config holds the configuration for a TUI instance.
type MenuItem ¶
type MenuItem struct {
Label string
Value string // optional value passed to OnSelect
Prompt string // if set, selecting this item opens a text-entry prompt with this label
Children []*MenuItem // non-nil → sub-menu
OnSelect func(item *MenuItem, input string) // input is non-empty only for Prompt items
}
MenuItem is a single entry in a Menu.
type MessageRole ¶
type MessageRole int
MessageRole identifies who sent a message.
const ( RoleAssistant MessageRole = iota RoleUser RoleSystem RoleThinking RoleTool )
type Panel ¶ added in v0.8.0
type Panel struct {
// contains filtered or unexported fields
}
Panel represents a content area within the TUI. It implements io.Writer for easy integration with other systems. Panels render raw text content via WriteString, SetContent, etc. For chat-like message history, use the TUI's main output methods instead.
func (*Panel) AddColumn ¶ added in v0.8.0
AddColumn appends a child panel as a horizontal column (left to right). No-op if the panel already has rows.
func (*Panel) AddMessage ¶ added in v0.8.0
func (p *Panel) AddMessage(role MessageRole, content string)
AddMessage appends a complete message with role.
func (*Panel) AddMessageAs ¶ added in v0.8.0
func (p *Panel) AddMessageAs(role MessageRole, label, content string)
AddMessageAs appends a complete message with a custom label.
func (*Panel) AddRow ¶ added in v0.8.0
AddRow appends a child panel as a vertical row (top to bottom). No-op if the panel already has columns.
func (*Panel) Clear ¶ added in v0.8.0
func (p *Panel) Clear()
Clear removes all content from the panel.
func (*Panel) ClearLine ¶ added in v0.8.0
ClearLine clears a specific line in raw content. The panel must be in ModeRaw for this to have visible effect.
func (*Panel) ClearRegion ¶ added in v0.8.0
ClearRegion clears a rectangular region in raw content. The panel must be in ModeRaw for this to have visible effect.
func (*Panel) ContentLines ¶ added in v0.8.0
ContentLines returns the total number of content lines.
func (*Panel) IsStreaming ¶ added in v0.8.0
IsStreaming returns true if a streaming message is in progress.
func (*Panel) ScrollDown ¶ added in v0.8.0
ScrollDown scrolls down by n lines.
func (*Panel) ScrollOffset ¶ added in v0.8.0
ScrollOffset returns the current scroll offset.
func (*Panel) ScrollToBottom ¶ added in v0.8.0
func (p *Panel) ScrollToBottom()
ScrollToBottom scrolls to the bottom of the content.
func (*Panel) ScrollToTop ¶ added in v0.8.0
func (p *Panel) ScrollToTop()
ScrollToTop scrolls to the top of the content.
func (*Panel) Scrollable ¶ added in v0.8.0
Scrollable returns whether the panel is scrollable.
func (*Panel) SetContent ¶ added in v0.8.0
SetContent replaces all raw content with the given string. The panel must be in ModeRaw to render this content.
func (*Panel) SetScrollable ¶ added in v0.8.0
SetScrollable sets whether the panel content scrolls.
func (*Panel) SetSkipFocus ¶ added in v0.8.0
SetSkipFocus sets whether the panel is excluded from Tab focus cycling.
func (*Panel) SetTitle ¶ added in v0.8.0
SetTitle sets the title shown in the border. Empty string = no title.
func (*Panel) Size ¶ added in v0.8.0
Size returns the panel's content dimensions (width, height). Returns 0, 0 if the panel has not been rendered yet.
func (*Panel) SkipFocus ¶ added in v0.8.0
SkipFocus returns whether the panel is excluded from focus cycling.
func (*Panel) StartStreaming ¶ added in v0.8.0
func (p *Panel) StartStreaming()
StartStreaming begins a new assistant message built incrementally.
func (*Panel) StartStreamingAs ¶ added in v0.8.0
StartStreamingAs begins a new streaming message with a custom label.
func (*Panel) StartStreamingWithRole ¶ added in v0.8.2
func (p *Panel) StartStreamingWithRole(role MessageRole, label string)
StartStreamingWithRole begins a new streaming message with a custom role and label.
func (*Panel) StopStreaming ¶ added in v0.8.0
func (p *Panel) StopStreaming()
StopStreaming finalises any in-progress streaming message.
func (*Panel) StreamChunk ¶ added in v0.8.0
StreamChunk appends a chunk to the in-progress streaming message.
func (*Panel) StreamComplete ¶ added in v0.8.0
func (p *Panel) StreamComplete()
StreamComplete finalises the streaming message.
func (*Panel) StyledWith ¶ added in v0.8.0
StyledWith returns text wrapped in a named theme color. Name can be: "primary", "secondary", "error", "dim", "text", "user"
func (*Panel) Write ¶ added in v0.8.0
Write implements io.Writer. It appends bytes to the panel's raw content.
func (*Panel) WriteAt ¶ added in v0.8.0
WriteAt writes text at a specific position (0-indexed, relative to panel). If the position is outside current content, content is extended with blank lines. The panel must be in ModeRaw to render this content.
func (*Panel) WriteString ¶ added in v0.8.0
WriteString appends a string to the panel's raw content. The panel must be in ModeRaw to render this content.
type PanelConfig ¶ added in v0.8.0
type PanelConfig struct {
Name string // Panel identifier
Width int // Positive = columns, negative = percentage (e.g., -30 = 30%)
Height int // Positive = rows, negative = percentage (e.g., -50 = 50%). Used when panel is split vertically
MinWidth int // Minimum width to render; 0 = always show
Scrollable bool // True = content scrolls, false = fixed viewport
Title string // Optional title for border; empty = no title
Color *Color // Optional border/accent color; nil = auto-assign
NoBorder bool // True = hide border for this panel
SkipFocus bool // True = exclude from Tab focus cycle
}
PanelConfig configures a panel in the layout.
type TUI ¶
type TUI struct {
// contains filtered or unexported fields
}
TUI is the main terminal UI instance.
func (*TUI) AddCommand ¶ added in v0.7.6
AddCommand registers a new slash command at runtime.
func (*TUI) AddLeft ¶ added in v0.8.0
AddLeft attaches the given panel (and any children) to the left of the main panel.
func (*TUI) AddMessage ¶
func (t *TUI) AddMessage(role MessageRole, content string)
AddMessage appends a complete message to the output region.
func (*TUI) AddMessageAs ¶
func (t *TUI) AddMessageAs(role MessageRole, label, content string)
AddMessageAs appends a complete message with a custom label.
func (*TUI) AddRight ¶ added in v0.8.0
AddRight attaches the given panel (and any children) to the right of the main panel.
func (*TUI) ClearLayout ¶ added in v0.8.0
func (t *TUI) ClearLayout()
ClearLayout removes the layout tree but keeps all panels and their content.
func (*TUI) ClearOutput ¶
func (t *TUI) ClearOutput()
ClearOutput removes all messages from the output region.
func (*TUI) ClearProgress ¶
func (t *TUI) ClearProgress()
ClearProgress removes the progress bar from the separator.
func (*TUI) CloseMenu ¶
func (t *TUI) CloseMenu()
CloseMenu closes the menu and restores the input box.
func (*TUI) Context ¶
Context returns the context that was passed to Run. Returns nil if Run has not been called yet.
func (*TUI) CreatePanel ¶ added in v0.8.0
func (t *TUI) CreatePanel(cfg PanelConfig) *Panel
CreatePanel creates a new panel without adding it to the layout. The panel is stored in the TUI's panel map by name (if name is non-empty). Use AddLeft or AddRight to attach it to the layout.
func (*TUI) CycleFocus ¶ added in v0.8.0
func (t *TUI) CycleFocus()
CycleFocus moves focus to the next panel.
func (*TUI) Exit ¶
func (t *TUI) Exit()
Exit cleanly shuts down the TUI event loop. Useful as a /exit command handler: func(_ string) { t.Exit() }
func (*TUI) FocusPanel ¶ added in v0.8.0
FocusPanel sets the focused panel by index.
func (*TUI) FocusedPanel ¶ added in v0.8.0
FocusedPanel returns the currently focused panel. Returns nil if no panel is focused (shouldn't happen in normal use).
func (*TUI) HasMultiplePanels ¶ added in v0.8.0
HasMultiplePanels returns true if there's more than one visible panel.
func (*TUI) IsStreaming ¶
IsStreaming returns true if a streaming message is in progress.
func (*TUI) Panel ¶ added in v0.8.0
Panel returns the panel with the given name. The special name "main" returns the main panel. Returns nil if no panel with that name exists.
func (*TUI) RemoveCommand ¶ added in v0.7.6
RemoveCommand removes a slash command by name.
func (*TUI) Run ¶
Run enters the event loop. It blocks until the user exits (Ctrl+C, t.Exit(), or ctx cancellation).
func (*TUI) SetAuxLabels ¶ added in v0.8.2
SetAuxLabels updates the default labels for thinking and tool messages.
func (*TUI) SetInputEnabled ¶ added in v0.7.9
SetInputEnabled toggles the input box at runtime.
func (*TUI) SetLabels ¶ added in v0.7.6
SetLabels updates the default role labels shown in message headers. Empty strings leave the corresponding label unchanged.
func (*TUI) SetProgress ¶
SetProgress shows a labelled progress bar in the separator (0.0–1.0). Stops any active spinner. Call ClearProgress to remove it.
func (*TUI) SetStatusLeft ¶
SetStatusLeft updates the left status bar text.
func (*TUI) SetStatusRight ¶
SetStatusRight updates the right status bar text.
func (*TUI) StartSpinner ¶
StartSpinner shows an animated spinner in the separator with the given text. Calling StartSpinner while one is running replaces the text.
func (*TUI) StartStreaming ¶
func (t *TUI) StartStreaming()
StartStreaming begins a new streaming assistant message.
func (*TUI) StartStreamingAs ¶
StartStreamingAs begins a new streaming assistant message with a custom label.
func (*TUI) StartStreamingWithRole ¶ added in v0.8.2
func (t *TUI) StartStreamingWithRole(role MessageRole, label string)
StartStreamingWithRole begins a new streaming assistant/system/thinking/tool message with a custom label.
func (*TUI) StopSpinner ¶
func (t *TUI) StopSpinner()
StopSpinner stops the spinner and clears it from the separator.
func (*TUI) StopStreaming ¶
func (t *TUI) StopStreaming()
StopStreaming finalises any in-progress streaming message.
func (*TUI) StreamChunk ¶
StreamChunk appends a chunk to the current streaming message.
func (*TUI) StreamComplete ¶
func (t *TUI) StreamComplete()
StreamComplete finalises the streaming message.
func (*TUI) TerminalSize ¶ added in v0.8.0
TerminalSize returns the full terminal dimensions (width, height). Returns 0, 0 if the terminal size has not been determined yet.
func (*TUI) Theme ¶
Theme returns the active theme, allowing callers to access color values for use with Styled.
func (*TUI) WriteString ¶ added in v0.8.0
type Theme ¶
type Theme struct {
Name string
Primary Color // Accents, prompt >
Secondary Color // Muted text, hints
Text Color // Normal content
UserText Color // User message text
Dim Color // Very muted (scrollbar, borders)
CodeBG Color // Code block background
CodeText Color // Code block text
Error Color // Error messages
}
Theme defines the visual identity of the TUI.
func ThemeByName ¶
ThemeByName returns the theme registered under name, or (nil, false).
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Example TUI application.
|
Example TUI application. |
|
Example TUI application demonstrating OnInterrupt with quit confirmation.
|
Example TUI application demonstrating OnInterrupt with quit confirmation. |
|
OSC sequence test - outputs directly to terminal without TUI.
|
OSC sequence test - outputs directly to terminal without TUI. |
|
Example TUI with multiple panels — demonstrates the builder panel API.
|
Example TUI with multiple panels — demonstrates the builder panel API. |
|
Example TUI log viewer — output-only mode.
|
Example TUI log viewer — output-only mode. |