Documentation
¶
Overview ¶
Package tui implements Cogo's interactive Bubble Tea program.
This file defines the in-memory chat history used by the Model. History is intentionally simple: a slice of role-tagged Messages. Persistence across sessions is Slice 5 (FR-11.1 transcript writes).
Index ¶
Constants ¶
const ( ExitOK = 0 ExitRunError = 1 ExitConfigError = 2 )
Exit codes used by the headless package — re-exported here so cmd/cogo can use one set of constants regardless of which mode it dispatched to.
const MaxPaletteRows = 8
MaxPaletteRows caps how many items render at once. Bubble Tea viewports handle scrolling; we keep this short to stay readable.
Variables ¶
This section is empty.
Functions ¶
func HelpText ¶
func HelpText() string
HelpText returns the multi-line help message printed by /help.
func NewPrompter ¶
func NewPrompter(prog programSender) permissions.Prompter
NewPrompter returns a Prompter wired to send into prog.
func Run ¶
Run launches the TUI bound to cfg. It blocks until the user quits.
Resolution failures (no auth, bad config) return ExitConfigError so the caller can distinguish them from runtime errors. Runtime errors from Bubble Tea itself return ExitRunError.
agentsDir is the resolved .agents/ directory if one was found, or empty when running with built-in defaults; we use it to persist "Always allow" path-scope choices into config.json and to write session transcripts under sessions/.
Types ¶
type History ¶
type History struct {
// contains filtered or unexported fields
}
History is the append-only chat log for one session.
All operations take O(1) amortized time. Snapshot returns a defensive copy; mutators take an index returned by Append (no bounds-checking helpers are exposed since callers always use freshly returned indices).
func (*History) Append ¶
Append adds m and returns its index for later in-place mutation (AppendText, SetRendered).
func (*History) AppendText ¶
AppendText appends chunk to the Text of the message at i. Used to accumulate streaming partial events into one in-progress assistant message.
func (*History) Reset ¶
func (h *History) Reset()
Reset empties the history. Used by the /clear slash command.
func (*History) SetRendered ¶
SetRendered replaces the rendered form of the message at i. Called once per assistant turn after TurnComplete.
type KeyMap ¶
type KeyMap struct {
Submit key.Binding
Newline key.Binding
Cancel key.Binding // Ctrl+C — interrupts current turn or exits
ClearView key.Binding // Ctrl+L — clears viewport (history preserved)
ClearInput key.Binding // Ctrl+U — clears the textarea (shell convention)
ScrollUp key.Binding
ScrollDown key.Binding
LineUp key.Binding // Up arrow — recalls history when input empty
LineDown key.Binding // Down arrow — moves forward through recall
// Permission modal: y allow once, n deny, s allow for the session,
// a allow always (persisted).
ConfirmAllowOnce key.Binding
ConfirmDeny key.Binding
ConfirmAllowSession key.Binding
ConfirmAllowAlways key.Binding
}
KeyMap centralizes the keybindings the TUI handles directly. Bubble Tea's textarea consumes most other keys (typing, arrows, etc.).
type MarkdownRenderer ¶
type MarkdownRenderer struct {
// contains filtered or unexported fields
}
MarkdownRenderer wraps a Glamour TermRenderer to render assistant messages on TurnComplete. Streaming partial chunks bypass this renderer and display as plain text — Glamour can't gracefully render half-formed code fences or tables.
func NewMarkdownRenderer ¶
func NewMarkdownRenderer(width int, styleName string) (*MarkdownRenderer, error)
NewMarkdownRenderer constructs a renderer with a fixed style name and word wrap at width characters. Width <= 0 disables wrap.
styleName must be a recognized Glamour style ("dark", "light", "notty", etc.). We deliberately avoid glamour.WithAutoStyle() here: it issues an OSC-11 background-color query to the terminal every call, and once Bubble Tea is reading stdin, the terminal's response races into the textarea as input. The TUI detects light vs dark once at startup (before tea.NewProgram) and threads the result through this constructor.
Returns a usable (no-op) renderer plus a non-nil error if Glamour initialization fails so the TUI can keep running with raw markdown rather than crashing.
func (*MarkdownRenderer) Render ¶
func (m *MarkdownRenderer) Render(markdown string) string
Render applies Glamour to markdown. If the renderer failed to initialize, it returns markdown unchanged so the user still sees something usable.
type Message ¶
type Message struct {
Role Role
Text string
Rendered string // optional pre-rendered (Glamour) form for assistants
}
Message is one entry in the chat history.
Text holds the original payload — for assistant messages this is raw markdown streamed token-by-token; for everything else it is plain text. Rendered is optional and populated for assistant messages once the turn completes (see internal/tui/markdown.go).
type Model ¶
type Model struct {
// AlwaysAllow is invoked when the user picks "always allow" in the
// permission modal. The host (TUI launcher) plugs in a function
// that persists the pattern to .agents/config.json. May be nil in
// tests.
AlwaysAllow func(req permissions.PromptRequest) error
// contains filtered or unexported fields
}
Model is Cogo's Bubble Tea model. Mutated through *Model receivers so goroutine-driven Sends can update the same instance.
func NewModel ¶
NewModel constructs a fresh chat session bound to a configured agent. The program reference is set later via SetProgram.
mdStyle picks the Glamour style ("dark" or "light") for assistant markdown rendering. Detect this once before tea.NewProgram (see program.Run); resolving it during the program's lifetime causes Glamour's background-color query response to leak into the textarea.
func (*Model) Init ¶
Init returns the initial commands. The spinner is started so its Tick loop can animate when transitioning into the streaming state; the textarea blink keeps the cursor visible.
func (*Model) SetProgram ¶
func (m *Model) SetProgram(p programSender)
SetProgram wires the running tea.Program in so background goroutines (the agent runner) can Send messages back to the loop.
func (*Model) View ¶
View renders the model as a single string. Layout (top to bottom):
Header Viewport (scrollable history) Palette (slash / file picker — only when active) Input area (textarea inside a rounded border) — replaced by the permission modal when a request is pending Footer (status hint or spinner) bottomPad blank rows (breathing room)
type SlashAction ¶
type SlashAction string
SlashAction identifies the slash command typed by the user.
const ( SlashHelp SlashAction = "help" SlashClear SlashAction = "clear" SlashQuit SlashAction = "quit" SlashMemory SlashAction = "memory" SlashStats SlashAction = "stats" SlashModel SlashAction = "model" SlashMCP SlashAction = "mcp" SlashSkills SlashAction = "skills" SlashReload SlashAction = "reload" SlashMouse SlashAction = "mouse" SlashUnknown SlashAction = "unknown" )
func ParseSlash ¶
func ParseSlash(input string) (action SlashAction, command, args string, isSlash bool)
ParseSlash inspects input. If it looks like a slash command (leading `/` after trimming whitespace), returns the recognized action, the raw command name (without leading `/`, as the user typed it for error messages), the args (everything after the command token, trimmed), and isSlash=true. Otherwise returns ("", "", "", false).
Unrecognized slash commands return SlashUnknown so callers can show a friendly "unknown command" message in the chat without leaking input to the model.
type State ¶
type State int
State tracks the agent's current activity for input gating and View rendering.
type Styles ¶
type Styles struct {
Header lipgloss.Style
HeaderAccent lipgloss.Style
UserPrefix lipgloss.Style
UserText lipgloss.Style
Assistant lipgloss.Style
System lipgloss.Style
Error lipgloss.Style
InputBorder lipgloss.Style
Spinner lipgloss.Style
Confirm lipgloss.Style
}
Styles centralizes Lip Gloss styling so the View has a single source of truth for colors, padding, and borders. AdaptiveColor pairs let each style render correctly on light and dark terminals.
func DefaultStyles ¶
func DefaultStyles() Styles
DefaultStyles returns the Slice 2 visual identity. Explicit theme switching (light/dark forcing) is deferred to a later slice; for now every color is adaptive.