tui

package
v0.3.0 Latest Latest
Warning

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

Go to latest
Published: Jun 26, 2026 License: Apache-2.0 Imports: 29 Imported by: 0

Documentation

Overview

Package tui implements baifo's terminal UI on top of charmbracelet's BubbleTea v2 stack. The single source of truth for visual decisions (palette, glyphs, spacing) is this file; components must import it and never hardcode colours or characters.

See .agents/TUI_DESIGN.md for the spec; the values here mirror it exactly.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Accent

type Accent struct {
	Name    string
	Primary color.Color
	Focus   color.Color
	Subtle  color.Color
}

Accent bundles the three palette values that drive baifo's warm highlights: a primary tone, a brighter focus tone, and a desaturated subtle tone used as background tint on hover/selected rows. There is exactly one accent (canariasAccent); it is not user-configurable.

type LayoutMode

type LayoutMode int

LayoutMode enumerates the responsive break-points.

const (
	// LayoutTooSmall means the terminal is unusable (height too low).
	// The TUI shows a single-line "terminal too small" message and no
	// other content.
	LayoutTooSmall LayoutMode = iota

	// LayoutNarrow is < 80 cols. Tabs collapse into a single-tab view,
	// sidebars hide, and panes stack vertically.
	LayoutNarrow

	// LayoutNormal is 80..119 cols. The right sidebar is hidden by default.
	LayoutNormal

	// LayoutWide is >= 120 cols. Full layout with a permanent right
	// sidebar on the Chat tab.
	LayoutWide
)

func Classify

func Classify(width, height int) LayoutMode

Classify maps a terminal size to a LayoutMode following the rules in TUI_DESIGN.md. Height is checked first because it gates the entire rendering pipeline.

func (LayoutMode) SidebarVisible

func (m LayoutMode) SidebarVisible() bool

SidebarVisible returns true when the responsive mode should show the right-hand workers sidebar by default. Only LayoutWide.

func (LayoutMode) TabsCollapsed

func (m LayoutMode) TabsCollapsed() bool

TabsCollapsed returns true when the responsive mode should hide the tab strip. Only LayoutNarrow.

type Message

type Message struct {
	Kind MessageKind
	Time time.Time
	Text string

	// ToolName is set on MessageToolCall and MessageToolResult rows.
	// Carries the namespaced tool name (e.g. "filesystem.read_file").
	ToolName string

	// ToolCallID pairs a MessageToolResult with its preceding
	// MessageToolCall. Empty for other kinds.
	ToolCallID string

	// ToolArgs is the argument map of a MessageToolCall. Nil for
	// other kinds. Stored verbatim so a future expand-on-click view
	// can pretty-print it without re-parsing.
	ToolArgs map[string]any

	// ToolResult is the response map of a MessageToolResult. Nil for
	// other kinds.
	ToolResult map[string]any

	// Expanded controls whether a tool row renders only its single
	// dimmed header line (false, default) or also unfolds the
	// args+result block beneath it (true). Plain user / root / system
	// rows ignore this flag.
	//
	// Today nothing flips it — the wiring is in place so a future
	// "Enter on the focused row" interaction can toggle without
	// touching the renderer.
	Expanded bool

	// StreamID ties this row to a single in-flight streaming turn.
	// Only set on the MessageRoot bubble that an active stream is
	// writing into; empty on every other row. Coalescing finds the
	// bubble by this ID instead of by array position, so anything
	// inserted after it (tool rows, lifecycle notices, errors) can
	// never displace it and split the reply.
	StreamID string
}

Message is one item in the chat history. The TUI keeps an in-memory slice and renders the full transcript into a viewport on every change. Persistence lives in Phase 4 (sessions); for the in-memory history we re-render the whole slice every Update, which is fine because the slice stays small for typical conversations and the viewport handles wrapping + scrolling.

func (Message) CopyableText

func (m Message) CopyableText() string

type MessageKind

type MessageKind int

MessageKind classifies a chat row so the renderer knows which colour to use for the author label.

const (
	// MessageUser is what the user typed in the composer.
	MessageUser MessageKind = iota

	// MessageRoot is the root agent's reply text.
	MessageRoot

	// MessageSystem is an italic, dim notice (e.g. "session resumed").
	MessageSystem

	// MessageError surfaces a failure from the runner or a tool.
	MessageError

	// MessageNotice is a prominent, highlighted notice rendered with a
	// filled colour header band (e.g. "context guard"). Unlike
	// MessageSystem (a quiet dim line) it is meant to catch the eye in
	// the flow of the transcript. Its body (Text) is treated as an
	// expandable detail block — collapsed to just the band by default,
	// unfolded under it when Expanded is true.
	MessageNotice

	// MessageAgentError surfaces a failed agent turn (the executor
	// reported a failure as a task-failed event rather than an
	// iterator error). Rendered with the same special-row treatment as
	// MessageNotice but in the error colour, so the two read as a
	// consistent family of "special" rows while staying visually
	// distinct by colour.
	MessageAgentError

	// MessageToolCall is the agent invoking a tool. Rendered as a
	// single dim line summarising the call (name + truncated args).
	MessageToolCall

	// MessageToolResult is the result of a previous MessageToolCall.
	// Rendered as a single dim line summarising the response.
	MessageToolResult
)

type Model

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

func NewModel

func NewModel(facade facade.Facade, useNerdFont bool, version string) Model

NewModel constructs the top-level Model. The Facade may be nil so the TUI can boot in an error-state when the app couldn't (e.g. no root agent configured).

func NewModelWithAutoScroll

func NewModelWithAutoScroll(facade facade.Facade, useNerdFont bool, version string, autoScroll bool, keepToolsExpanded bool) Model

NewModelWithAutoScroll is like NewModel but lets the caller seed the chat's auto-scroll behaviour and keep-tools-expanded behaviour.

func (Model) Init

func (m Model) Init() tea.Cmd

Init implements tea.Model. We schedule a tea.Tick to dismiss the splash screen after a short delay, and start listening on the reload channel so config-file changes refresh open overlays.

func (Model) Update

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

Update implements tea.Model.

func (Model) View

func (m Model) View() tea.View

View implements tea.Model.

type Theme

type Theme struct {
	Accent      Accent
	UseNerdFont bool
}

Theme is the runtime view of the palette + glyph configuration. The palette is fixed (Canarias); only the Nerd-Font flag varies, set once at construction via NewTheme.

func NewTheme

func NewTheme(useNerdFont bool) Theme

NewTheme builds a Theme from the user's preferences. The accent is NewTheme builds a Theme from the user's preferences. The palette is fixed (the Canarias accent); the only preference is whether to use Nerd Font glyphs.

func (Theme) AccentText

func (t Theme) AccentText() lipgloss.Style

AccentText returns text painted in the active accent's primary tone.

func (Theme) DimText

func (t Theme) DimText() lipgloss.Style

DimText is the muted variant — used for labels and timestamps.

func (Theme) EntityText

func (t Theme) EntityText(kind string) lipgloss.Style

EntityText returns text painted in the colour assigned to the given entity kind. Unknown kinds fall back to the dim foreground.

func (Theme) FaintText

func (t Theme) FaintText() lipgloss.Style

FaintText is the most muted — placeholder hints and divider labels.

func (Theme) Glyph

func (t Theme) Glyph(name string) string

Glyph returns the glyph for name, picking the right variant for the active font mode. Unknown names return "?" so a missing entry is visible during development without breaking the layout.

func (Theme) PanelBorder

func (t Theme) PanelBorder() lipgloss.Style

PanelBorder returns the border style for an unfocused panel.

func (Theme) PanelBorderFocused

func (t Theme) PanelBorderFocused() lipgloss.Style

PanelBorderFocused returns the border style for the focused panel. It only differs from PanelBorder in the border colour, which uses the active accent's primary tone.

func (Theme) PrimaryText

func (t Theme) PrimaryText() lipgloss.Style

PrimaryText returns the style for ordinary body text.

func (Theme) SpecialHeaderBand

func (t Theme) SpecialHeaderBand(bg color.Color) lipgloss.Style

SpecialHeaderBand returns the style for the colored header band that marks a "special" chat row (context-guard notice, agent error, and any future prominent in-flow event). The band paints the given colour as the background with the dark base as the foreground so the label reads clearly on top of it — a filled bar that catches the eye without wrapping the row in a border box. Bold for extra weight.

func (Theme) StatusError

func (t Theme) StatusError() lipgloss.Style

StatusError returns the red "error" style.

func (Theme) StatusInfo

func (t Theme) StatusInfo() lipgloss.Style

StatusInfo returns the amber "info" style.

func (Theme) StatusOK

func (t Theme) StatusOK() lipgloss.Style

StatusOK returns the green "success" style.

func (Theme) StatusWarning

func (t Theme) StatusWarning() lipgloss.Style

StatusWarning returns the amber "warning" style.

func (Theme) TabActive

func (t Theme) TabActive() lipgloss.Style

TabActive returns the style for the active tab label.

func (Theme) TabInactive

func (t Theme) TabInactive() lipgloss.Style

TabInactive returns the style for an inactive tab label.

type Toast

type Toast struct {
	Kind  ToastKind
	Title string
	Body  string
}

Toast is one floating notification. Body is two-line max; longer strings are truncated.

type ToastKind

type ToastKind int

ToastKind drives the border colour.

const (
	ToastInfo ToastKind = iota
	ToastSuccess
	ToastWarning
	ToastError
)

type Zones

type Zones struct {
	Tabs         int // zone 1: tab strip
	Main         int // zone 2: tab-specific content
	StreamingBar int // zone 2.5: thin one-line bar that shows the spinner during LLM streaming
	Composer     int // zone 3: composer (zero on tabs that hide it)
	Status       int // zone 4: status bar
}

Zones is the four-band vertical decomposition of the screen described in TUI_DESIGN.md > Layout. Heights are absolute row counts; widths are inherited from the terminal.

Zone heights add up to the terminal height. The Main zone absorbs any leftover rows because it is the only one whose contents scale.

func ZonesFor

func ZonesFor(height int, composerVisible bool) Zones

ZonesFor computes Zones for a given terminal height and whether the composer should be visible. The composer is shown only on Chat and on the Worker spy view.

The Tabs zone is sized to fit the new header (logo + tab strip + rule). The Status zone is sized to fit the chip-based footer (rounded-border chips are 3 rows tall). When either constant changes (header.go, chips.go), this is the only place that needs updating.

Directories

Path Synopsis
components
editor
Package editor is baifo's embedded text editor component.
Package editor is baifo's embedded text editor component.
editor/mdhl
Package mdhl provides a per-line Markdown highlighter for the embedded editor.
Package mdhl provides a per-line Markdown highlighter for the embedded editor.
editor/yamlhl
Package yamlhl provides a single-line YAML syntax highlighter for the embedded editor.
Package yamlhl provides a single-line YAML syntax highlighter for the embedded editor.

Jump to

Keyboard shortcuts

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