tui

package
v1.0.2 Latest Latest
Warning

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

Go to latest
Published: Jul 6, 2026 License: MIT Imports: 37 Imported by: 0

Documentation

Overview

Package tui provides the Bubble Tea v2 TUI adapter for clyde. Real data is wired via the LiveSession use case when --demo is not specified. Run cmd/clyde to start the TUI; pass --demo for deterministic mock mode.

Package proto provides the visual prototype of the clyde TUI. It runs on mock data only — no real adapters wired.

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func ComposeColumns

func ComposeColumns(height int, columns ...Column) string

ComposeColumns joins columns horizontally, padding each to its declared width and padding to `height` rows.

func MockFileContent

func MockFileContent(path string, _ MockData) string

MockFileContent returns the mock content for a given file path. Exported for use by the viewport loader in model.go.

func NormalizeCwd

func NormalizeCwd(cwd string) string

NormalizeCwd resolves a path to its canonical absolute form, expanding symlinks where possible. Used as the per-project lookup key so symlinked or relative paths still match their config section.

func SupportsKittyGraphics

func SupportsKittyGraphics() bool

SupportsKittyGraphics returns true if the terminal supports the Kitty graphics protocol (either native Kitty or Ghostty in Kitty-compat mode).

Types

type AgentGroup

type AgentGroup struct {
	AgentID    string
	AgentName  string
	IsSubagent bool
	Calls      []ToolCall
	Active     bool // whether this agent is currently running
}

AgentGroup groups tool calls by the agent that made them.

type BashRow

type BashRow struct {
	Time     string    // "14:32:08" — short time-of-day
	Command  string    // "go test ./auth/..."
	Duration string    // "12s" / "<1s" / "" when running
	State    CallState // CallDone / CallActive / CallFailed
}

BashRow is one Bash command entry rendered in the bash audit panel.

type BootScreen

type BootScreen struct {
	// Active is true while the splash is on screen. The model gates the
	// regular View() pipeline on this and routes the next FrameMsg into
	// Advance(). Cleared by Dismiss() (any key) or auto-finish.
	Active bool
	// Tick counts FrameMsg ticks since the splash was triggered.
	Tick int
}

BootScreen drives the animated startup splash. The renderer is pure: it reads the tick counter and paints whatever the current phase requires, so tests can drive it deterministically by setting Tick directly.

func (BootScreen) Advance

func (b BootScreen) Advance() BootScreen

Advance returns a new BootScreen with the tick incremented and Active cleared once the auto-finish window passes.

func (BootScreen) Dismiss

func (b BootScreen) Dismiss() BootScreen

Dismiss flips Active off — used when the user hits any key during boot.

type Breakpoint

type Breakpoint int

Breakpoint describes the width-driven layout mode.

const (
	// BreakpointNarrow is single-column stack only (< 80 cols).
	BreakpointNarrow Breakpoint = iota
	// BreakpointMedium is two-column layout (80-159 cols):
	// left = explorer + servers, right = now/tasks/diff/usage stacked.
	BreakpointMedium
	// BreakpointWide is the full 3-column design (160+ cols).
	BreakpointWide
)

func DetectBreakpoint

func DetectBreakpoint(width int) Breakpoint

DetectBreakpoint maps a terminal width to a Breakpoint.

type CacheStatsView

type CacheStatsView struct {
	HitRatio          float64
	FromCache         int64
	Recomputed        int64
	BiggestMissTokens int64
	BiggestMissAt     string // formatted "HH:MM"
	Trend             []float64
	TurnCount         int
}

CacheStatsView mirrors livesession.CacheStats for the TUI layer.

type CallState

type CallState int

CallState describes the state of a tool call.

const (
	CallDone   CallState = iota // completed call
	CallActive                  // currently running call
	CallFailed                  // failed call
)

Call state constants.

type CollapseSpring

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

CollapseSpring is a harmonica-backed spring for animating panel height. Each panel has its own CollapseSpring tracking current height, velocity, and the target height driven by collapsed/expanded state.

func NewCollapseSpring

func NewCollapseSpring(h float64) CollapseSpring

NewCollapseSpring creates a spring starting at h (already settled).

func (*CollapseSpring) Advance

func (cs *CollapseSpring) Advance()

Advance runs one frame of the spring simulation. At 100ms tick cadence we call this once per FrameMsg.

func (*CollapseSpring) Height

func (cs *CollapseSpring) Height() int

Height returns the current integer height for rendering.

func (*CollapseSpring) IsSettled

func (cs *CollapseSpring) IsSettled() bool

IsSettled reports whether the animation has converged.

func (*CollapseSpring) SetTarget

func (cs *CollapseSpring) SetTarget(h float64)

SetTarget changes the target height and marks the spring as unsettled.

type Column

type Column struct {
	Content string
	Width   int
}

Column is a rendered panel block ready for horizontal joining.

type CompactionState

type CompactionState int

CompactionState describes how close a session is to the context window limit.

const (
	// CompactionOK — tokens < 75% of context limit. No warning shown.
	CompactionOK CompactionState = iota
	// CompactionWarn — tokens 75–90% of context limit. Inline warning in usage panel.
	CompactionWarn
	// CompactionDanger — tokens > 90% of context limit. Danger indicator + notification banner.
	CompactionDanger
)

type Config

type Config struct {
	Layout   LayoutConfig               `toml:"layout"`
	Panels   PanelsConfig               `toml:"panels"`
	Projects map[string]ProjectOverride `toml:"projects,omitempty"`

	// AutoSwitchToAllOnNewSession controls what happens when a brand-new
	// claude code session appears in the same cwd while clyde is running.
	// When true (default), clyde flips the focused tab to Σ all so the
	// user sees both sessions at a glance and can pick. When false, the
	// user's currently-focused tab is preserved — useful if they're
	// concentrating on one session and don't want their cursor stolen.
	AutoSwitchToAllOnNewSession bool `toml:"auto_switch_to_all_on_new_session"`

	// RememberLayout controls whether runtime panel-layout changes
	// (collapse toggles via space, manual height resize via +/-) are
	// written back to the config so the next session restores the same
	// layout. When false, runtime changes stay in-session and panels
	// fall back to PanelConfig.DefaultCollapsed on next launch.
	RememberLayout bool `toml:"remember_layout"`

	// NotificationStyle chooses the visual treatment for live hook
	// prompts and compaction warnings. Defaults to fullscreen so the
	// signal is impossible to miss; the user can downgrade to banner
	// or disable entirely from the settings overlay.
	NotificationStyle NotificationStyle `toml:"notification_style"`

	// NotifyCostThresholdUSD fires a quota notification when the
	// current session's accumulated cost crosses this dollar amount.
	// Zero (default) disables cost-based alerts — plan-quota %
	// notifications still fire on their own thresholds.
	NotifyCostThresholdUSD float64 `toml:"notify_cost_threshold_usd"`

	// Theme picks the active color palette. See theme.go for the registry
	// of supported themes; defaults to ThemeTokyoNight. Cycled live from
	// the settings overlay; the choice persists in config.toml so the
	// next launch starts in the same theme.
	Theme Theme `toml:"theme"`

	// MascotPersona picks which character drives the now-panel mascot. The
	// default ("meowl") is the cat shipped in v23+; "bowl" reverts to the
	// v9–v22 rabbit; "off" hides the mascot block entirely for users who'd
	// rather see the now panel as a flat status line. The legacy "kitten" /
	// "bunny" values are still accepted on read and normalized to
	// "meowl" / "bowl" (see MascotPersona.Normalize).
	MascotPersona MascotPersona `toml:"mascot_persona"`

	// BootScreenEnabled toggles the animated splash screen shown for
	// ~1.5s on launch. Enabled by default; users who launch clyde a lot
	// in CI scripts or back-to-back can flip it off from the settings
	// overlay.
	BootScreenEnabled bool `toml:"boot_screen_enabled"`
}

Config is the root config struct for the clyde TUI.

Resolution order at runtime is:

  1. Built-in defaults (DefaultConfig).
  2. Global user file (~/.config/clyde/config.toml).
  3. Per-project override under [projects."<absolute cwd>"] (V22+).

The global file may also contain a [projects."<cwd>"] section; the project layer is applied last via EffectiveFor.

func DefaultConfig

func DefaultConfig() Config

DefaultConfig returns hardcoded defaults matching the spec.

V22 defaults: Diff is OFF (the standalone diff panel is opt-in; hunks surface inline under Edit calls in the activity panel). Every other panel is on by default.

func LoadConfig

func LoadConfig() Config

LoadConfig reads the TOML config at configDir()/config.toml and merges it over the defaults. If the file doesn't exist, defaults are returned.

func (Config) EffectiveFor

func (cfg Config) EffectiveFor(cwd string) Config

EffectiveFor returns a copy of the config with the per-project override for cwd applied on top of the global panel settings. cwd is normalized before lookup so symlinked or relative paths resolve to the same key.

type DiffKind

type DiffKind int

DiffKind describes the type of a diff line.

const (
	DiffCtxKind  DiffKind = iota // context line
	DiffAddKind                  // added line
	DiffRemKind                  // removed line
	DiffHunkKind                 // @@ header line
)

Diff line kind constants.

type DiffLine

type DiffLine struct {
	Kind   DiffKind
	LineNo string
	Text   string
}

DiffLine is a rendered diff line.

type DiffSource

type DiffSource interface {
	// Diff returns parsed unified-diff hunks for the given file in cwd.
	// If file is empty, all changed files are included.
	// Returns (nil, nil) when cwd is not a git repo or git is unavailable.
	Diff(cwd, file string) ([]livesession.DiffHunk, error)
}

DiffSource is the minimal interface for fetching git diff hunks. The concrete implementation is git.Source from the adapters/git package. Defined here (TUI layer) so model.go can hold a DiffSource without importing the adapter package directly.

type ExplorerRow

type ExplorerRow struct {
	// DisplayName is the rendered node name (▼ src, Sidebar.tsx, etc.)
	DisplayName string
	// Path is the full relative path for files, dir-name for dirs.
	Path string
	// IsDir signals whether this row is a directory.
	IsDir bool
	// IsAct flags the currently active file.
	IsAct bool
	// Mark is "M", "+", or "".
	Mark string
	// Indent prefix (│ characters).
	Indent string
	// DirKey is the canonical dir name used to look up collapse state.
	DirKey string
}

ExplorerRow is a visible row in the interactive tree.

type ExplorerSearch

type ExplorerSearch struct {
	Active  bool
	Query   string
	Matches []ExplorerSearchMatch
	Idx     int
}

ExplorerSearch holds the search-overlay state for the explorer panel. Zero value = inactive (no overlay rendered, original tree visible).

When Active is true the explorer hides its normal tree+modified split and shows a flat list of matches scoped to the current Query. Up/Down navigate Idx through Matches; Enter opens Matches[Idx]. The cursor index auto-clamps to the match count so a query that narrows from 5 → 1 doesn't land on a stale row.

type ExplorerSearchMatch

type ExplorerSearchMatch struct {
	Path    string
	Display string
	IsMod   bool
}

ExplorerSearchMatch is a single hit inside the explorer's search overlay. Carries enough info to render a row (display text) and to act on Enter (full path + whether it came from the modified-files window).

type ExplorerSection

type ExplorerSection int

ExplorerSection identifies which of the two scrollable regions in the explorer panel currently owns the keyboard cursor.

const (
	// SectionTree — cursor is in the tree below the modified-files window.
	SectionTree ExplorerSection = iota
	// SectionMod — cursor is in the modified-files window above the tree.
	SectionMod
)

type ExplorerState

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

ExplorerState holds the interactive explorer panel state. All fields are value-type — no pointers — to keep Model copyable.

func NewExplorerState

func NewExplorerState(d MockData) ExplorerState

NewExplorerState builds an ExplorerState from mock data. All top-level directories start collapsed so the explorer is compact on startup; the user expands individual directories with Enter or Space.

func (*ExplorerState) HighlightedNode

func (es *ExplorerState) HighlightedNode() *ExplorerRow

HighlightedNode returns a pointer to the currently highlighted row, or nil.

func (*ExplorerState) HighlightedPath

func (es *ExplorerState) HighlightedPath() string

HighlightedPath returns the path of the highlighted row, normalized for mock lookup.

func (*ExplorerState) MoveDown

func (es *ExplorerState) MoveDown(modCount int)

MoveDown moves the highlight one row down across the unified modified+tree cursor. ↓ at the bottom of mod crosses into the tree at row 0; ↓ at the bottom of tree clamps (no wrap).

func (*ExplorerState) MoveUp

func (es *ExplorerState) MoveUp(modCount int)

MoveUp moves the highlight one row up across the unified modified+tree cursor. modCount is the number of modified files in the current view — when the cursor is at the top of the tree and any modified files exist, the cursor crosses into the modified section. Wrapping at the top of mod is intentionally disabled: knowing "I'm at the very top" is more useful than wrap-around in a small list.

func (*ExplorerState) RefreshRows

func (es *ExplorerState) RefreshRows(d MockData)

RefreshRows rebuilds the visible row list from mock data (call after toggle).

func (*ExplorerState) ToggleDir

func (es *ExplorerState) ToggleDir(dirKey string)

ToggleDir expands or collapses a directory by its dirKey. After the toggle, the visible row list is rebuilt from the data stored in MockData — so we need it passed in.

type FileIcon

type FileIcon struct {
	Glyph string
	Color color.Color
}

FileIcon describes a single-cell glyph plus its accent color for a file type. Glyphs are chosen from Unicode Geometric Shapes / Block Elements so they render in any modern terminal font — no Nerd Font dependency.

type FrameMsg

type FrameMsg struct {
	Gen uint64
}

FrameMsg is the tick message driving all frame-counter animations.

Gen identifies the tick chain that scheduled this message. The model tracks the current generation in tickGen and bumps it whenever the next interval should change (user input, animation start). A FrameMsg whose Gen does not match the current tickGen is a stale tick from a superseded chain and is silently dropped — without this, switching from idle (1Hz) to active (20Hz) would create racing parallel chains.

type FrameState

type FrameState struct {
	Tick uint64

	// Bunny FSM — drives all mascot states and transitions.
	Mascot MascotFSM

	// Derived phases — recomputed on each tick.
	SpinnerFrame  int  // 0-3  (~333ms per frame)
	LiveDotDim    bool // alternates every 0.8s
	CursorVisible bool // alternates every 0.4s
	ChevronDim    bool // alternates every 0.7s
}

FrameState holds per-animation phase counters derived from a monotonic tick counter. All animations are driven by the FrameMsg tick.

func AdvanceTick

func AdvanceTick(prev FrameState) FrameState

AdvanceTick returns a new FrameState with Tick incremented and all derived phases recomputed. Dividers are sized for the active tick interval so every wall-clock period below stays the same regardless of the tick rate.

func InitFrameState

func InitFrameState() FrameState

InitFrameState returns an initial FrameState (tick=0).

func (FrameState) SpinnerGlyph

func (f FrameState) SpinnerGlyph() string

SpinnerGlyph returns the current spinner glyph from frame counter.

type HookNotification

type HookNotification struct {
	// Active is true when an unanswered hook event is in flight.
	Active bool

	// Tool is the tool name, e.g. "Bash", "Edit".
	Tool string

	// KeyArg is the main argument for the tool (command, path, etc.).
	KeyArg string

	// Cwd is the working directory context for the call.
	Cwd string
}

HookNotification holds the live hook event currently displayed in the banner. Zero value means no live event (fall through to mock data or hidden).

type KeyMap

type KeyMap struct {
	Quit      key.Binding
	Tab       key.Binding
	ShiftTab  key.Binding
	Space     key.Binding
	Up        key.Binding
	Down      key.Binding
	Left      key.Binding
	Right     key.Binding
	Enter     key.Binding
	Backspace key.Binding
	Esc       key.Binding

	// ⌃-prefixed chords dispatched by handleCtrl.
	CycleMode     key.Binding // ⌃l — cycle layout mode
	CollapseAll   key.Binding // ⌃0 — collapse every panel except the focused one
	FocusExplorer key.Binding // ⌃e — focus explorer
	FocusCalls    key.Binding // ⌃a — focus activity/calls
	FocusDiff     key.Binding // ⌃d — focus diff

	// Resize bindings — grow/shrink the focused expanded panel
	PanelGrow   key.Binding // + or = → increase panel height by 1
	PanelShrink key.Binding // - → decrease panel height by 1

	// Session cycling across the footer session-tab strip ([ / ]).
	SessionPrev key.Binding // [ — previous session tab
	SessionNext key.Binding // ] — next session tab

	// PanelJump documents the plain-letter panel jumps dispatched by
	// handlePanelJumpKey (e/a/d/u/s/b/c).
	PanelJump key.Binding

	// Overlays.
	Help     key.Binding // h — per-panel help cheat-sheet
	Settings key.Binding // ? — settings overlay (also cycles layout)

	// Numeric tab jumps for tabs mode (1..N).
	Jump1 key.Binding
	Jump2 key.Binding
	Jump3 key.Binding
	Jump4 key.Binding
}

KeyMap is the central keybinding registry for clyde. It documents the bindings the dashboard actually dispatches — every entry here must have a live handler (handleKey/handleCtrl/handlePanelJumpKey); dead declarations mislead the help surfaces.

func DefaultKeyMap

func DefaultKeyMap() KeyMap

DefaultKeyMap returns the default key bindings.

func (KeyMap) FullHelp

func (k KeyMap) FullHelp() [][]key.Binding

FullHelp implements help.KeyMap. Explorer note: when explorer is focused+expanded, ↑/↓ moves tree highlight instead of scrolling the viewport (special-cased in handleExplorerKey).

func (KeyMap) ShortHelp

func (k KeyMap) ShortHelp() []key.Binding

ShortHelp implements help.KeyMap for the bubbles/v2/help widget. Lists only keys that are actually dispatched today.

type Layout

type Layout struct {
	// Mode + breakpoint + width snapshot.
	Mode  LayoutMode
	BP    Breakpoint
	Width int

	// Chrome rows. NotifH only takes height when an actual hook or
	// compaction warning is active; StatusH grows when help is open.
	TitleH  int
	NotifH  int
	StatusH int

	// Vertical budget for panels = Height - TitleH - NotifH - StatusH,
	// floored at a per-mode minimum so degenerate window sizes don't
	// produce negative dimensions downstream.
	GridH int

	// ── 2-col (BreakpointMedium with stack mode) ────────────────────
	// LeftW + RightW = Width. ExplorerH and ServersH split the left
	// column vertically; the right column uses panelStackHeight per
	// panel (collapse spring or panelHeight()).
	LeftW     int
	RightW    int
	ExplorerH int
	ServersH  int

	// ── Multi-col (3 columns) ───────────────────────────────────────
	// MultiExplorerW takes the full GridH on the left. The middle
	// column stacks Now → Calls → optional Diff. The right column
	// stacks Usage → Servers → optional Bash → optional Cache.
	MultiExplorerW int
	MultiMiddleW   int
	MultiRightW    int
	MultiNowH      int
	MultiCallsH    int
	MultiDiffH     int
	MultiUsageH    int
	MultiServersH  int
	MultiBashH     int
	MultiCacheH    int
}

Layout holds the geometry derived from the current model snapshot. Never store this — always recompute via m.computeLayout() at the top of the consumer function. Stale Layouts (across model updates) are silently wrong; recomputing is cheap.

type LayoutConfig

type LayoutConfig struct {
	DefaultMode         LayoutMode `toml:"default_mode"`
	AutoSwitchThreshold int        `toml:"auto_switch_threshold"`
	CycleHotkey         string     `toml:"cycle_hotkey"`
}

LayoutConfig holds the layout section of the config.

type LayoutMode

type LayoutMode string

LayoutMode is a named layout mode.

const (
	LayoutStack    LayoutMode = "stack"
	LayoutTabs     LayoutMode = "tabs"
	LayoutMultiCol LayoutMode = "multi-col"
)

Layout mode constants.

func ResolveMode

func ResolveMode(preferred LayoutMode, threshold, width int) LayoutMode

ResolveMode returns the effective LayoutMode for a given terminal width, respecting the auto-switch threshold and the user's preferred mode. Multi-col requires at least 160 cols; below that it falls back to tabs.

func (LayoutMode) IsValid

func (m LayoutMode) IsValid() bool

IsValid reports whether m is a recognized LayoutMode. Used when loading config to fall back to the default if an unknown string slipped in via a hand-edited TOML file (consistent with Theme / NotificationStyle / MascotPersona validation).

type MascotBaseState

type MascotBaseState int

MascotBaseState identifies the named base states of the mascot FSM. Intermediate / transitional frames are encoded as additional constants.

Kitten shape reference (Stand state):

 /\_/\     ← ears (row 0)
( o.o )    ← round face with eyes (row 1)
/(   )\    ← body / arms (row 2)
 _"_"_     ← paws (row 3)

Width budget: 9 chars wide on every row (lipgloss pads to the longest line anyway, but staying balanced keeps the block from drifting under the spinner column). Block height: always exactly 4 rows.

const (
	MascotStand     MascotBaseState = iota // default — ears up, eyes open
	MascotBlink                            // eyes closed (-.-)
	MascotLookLeft                         // eyes look left (◐.◐)
	MascotLookRight                        // eyes look right (◑.◑)
	MascotHappy                            // eyes smile up (^_^)
	MascotSurprised                        // eyes wide open (O.O)
	MascotSleep                            // eyes droop, animated zZ next to ears
	MascotWave                             // both paws up, greeting

)

Base state constants. The iota starts at MascotStand.

type MascotFSM

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

MascotFSM drives the mascot state machine on each 50ms tick. It holds the current playback position within an event sequence plus scheduling state for the next idle event.

func NewMascotFSM

func NewMascotFSM() MascotFSM

NewMascotFSM returns an FSM ready to play. Locked at frame 0 for tests.

func (MascotFSM) Advance

func (f MascotFSM) Advance() MascotFSM

Advance moves the FSM one tick forward and returns a new FSM. Autonomous idle events (blink / look-around / nuzzle / stretch / cuddle / happy) and external triggers (pendingEvent set via SetExternalState / WithWorking) both fire from here.

func (MascotFSM) CurrentState

func (f MascotFSM) CurrentState() MascotBaseState

CurrentState returns the rendered base state for this tick.

func (MascotFSM) HasActiveSequence

func (f MascotFSM) HasActiveSequence() bool

HasActiveSequence reports whether the mascot is currently mid-animation (a sequence is queued and playback hasn't returned to the resting Stand state). Callers use this to decide whether to keep the frame loop in fast mode — a settled mascot at the idle cadence is one fewer reason to keep the View() rebuild rate at 20Hz.

func (MascotFSM) SetExternalState

func (f MascotFSM) SetExternalState(kind mascotEventKind) MascotFSM

SetExternalState queues an external event kind to play on the next Advance tick. If the FSM is already mid-sequence, the new event replaces the pending event (latest-wins semantics). This is the hook for live-data triggers from model.go.

func (MascotFSM) SleepZPhase

func (f MascotFSM) SleepZPhase() int

SleepZPhase returns the current zZ animation phase (0..3).

func (MascotFSM) WithWorking

func (f MascotFSM) WithWorking(b bool) MascotFSM

WithWorking returns an FSM with the working hint applied. The hint is consulted by scheduleNext + pickEvent to decide cadence and weights. Idempotent and value-typed so callers store the result.

type MascotPersona

type MascotPersona string

MascotPersona picks which character drives the now-panel mascot.

Two named characters ride the same FSM:

Meowl — the v23 cat. Default. /\_/\ ears, ( o.o ) face.
Bowl  — the legacy v9 rabbit. (\_/) ears, (o.o) face.

"Off" hides the mascot block entirely. The TOML serialization uses the new names ("meowl" / "bowl"); the legacy "kitten" / "bunny" values are accepted on read via Normalize() so a user upgrading from an earlier dev build doesn't see their persona reset to default.

const (
	MascotPersonaMeowl MascotPersona = "meowl"
	MascotPersonaBowl  MascotPersona = "bowl"
	MascotPersonaOff   MascotPersona = "off"
)

MascotPersona constants.

func (MascotPersona) Display

func (p MascotPersona) Display() string

Display returns a short human-readable label used in the settings chip.

func (MascotPersona) IsValid

func (p MascotPersona) IsValid() bool

IsValid reports whether p is a recognized MascotPersona — including legacy aliases, since the LoadConfig path treats those as valid input before normalizing.

func (MascotPersona) Next

func (p MascotPersona) Next() MascotPersona

Next returns the next persona in the cycle: meowl → bowl → off → meowl.

func (MascotPersona) Normalize

func (p MascotPersona) Normalize() MascotPersona

Normalize collapses legacy persona names into their new equivalents. Called from LoadConfig so files written before the v23 rename keep working with the same character.

type MockData

type MockData struct {
	// Title bar
	ProjectPath string
	ProjectName string
	Duration    string
	Tokens      string
	Cost        string

	// Explorer
	ModifiedFiles []ModifiedFile
	Tree          []TreeNode

	// Now panel
	NowMode    string // "writing · 47 t/s"
	NowOp      string // "edit auth.ts"
	NowMeta    string // "14 lines staged · line 46"
	NowTS      bool   // ts done
	NowLint    bool   // lint done
	NowCompile bool   // compile active (not done)

	// Calls panel (replaces tasks panel in v13)
	AgentGroups []AgentGroup

	// Diff panel
	DiffFile  string // "auth.ts · +14 −3"
	DiffLines []DiffLine
	IsGitRepo bool // true when cwd contains a .git directory

	// Usage panel
	Tokens200k int    // used out of 200k
	TokenPct   int    // 23
	Cost142    string // "$1.42"
	Turns      string // "38"
	Model      string // "opus 4.7"
	Errors     string
	Warnings   string
	Tests      string

	// Multi-window usage rows with per-token-type breakdown.
	// Each row shows label · in · out · cache · cost for one time window.
	UsageSession UsageWindowRow // this session
	Usage5h      UsageWindowRow // rolling last 5 hours
	UsageWeek    UsageWindowRow // rolling last 7 days

	// BurnRate is a derived display string shown below the usage rows:
	// e.g. "~3.2k tok/min · $0.18/hr". Empty when there is too little data.
	BurnRate string

	// PlanTier is the human-readable subscription plan tier
	// (e.g. "Max 5x", "Pro"). Empty when unknown OR when the user is
	// on API auth (in which case IsAPIUser is true and renderers show
	// "api" via that flag instead of trying to fake a tier here).
	PlanTier string

	// IsAPIUser is true when the plan-usage source returned
	// ErrPlanUsageUnavailable — the user has no subscription and is
	// paying per-token via API. Used to gate the cost-threshold
	// notification (subscribers' "cost" is mostly cosmetic) and to
	// display "api" as the tier label so the user can see at a glance
	// what mode they're in.
	IsAPIUser bool

	// PlanUsageOffline is true when the plan-usage API is unreachable or
	// the credentials are present but the request failed. Distinct from
	// IsAPIUser — offline means "we expected data and didn't get it",
	// API means "no subscription credentials by design".
	PlanUsageOffline bool

	// Servers panel
	ServersOn    int // 5
	ServersTotal int // 7
	MCPs         []Server
	LSPs         []Server

	// Bash audit panel (v22+, opt-in via settings)
	BashLog []BashRow

	// Cache efficiency panel (v22+, opt-in via settings)
	Cache CacheStatsView

	// Multi-session tab strip — one entry per session in the current cwd
	// (Σ aggregate first when populated). Populated from
	// livesession.View.SessionStats; empty in demo mode and when only a
	// single session is recently active.
	Sessions []SessionTab

	// SessionTabIndex is the index of the active tab in Sessions. -1 means
	// the Σ aggregate tab is active; 0..len(Sessions)-1 selects a specific
	// session. Default 0 (most-recently-active session).
	SessionTabIndex int

	// SessionLeaderboard mirrors Sessions for the usage panel: top sessions
	// by ContextPct desc, used to render per-session ctx bars when the Σ
	// tab is active. Empty when the panel should render its single-session
	// fallback bar.
	SessionLeaderboard []SessionTab
}

MockData is the complete v3 mock dataset.

func V3MockData

func V3MockData() MockData

V3MockData returns the authoritative v3 mock content matching v3-mock.html.

type Model

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

Model is the root Bubble Tea v2 model for the clyde prototype.

func NewModel

func NewModel() Model

NewModel constructs a Model with mock data and the Tokyo Night v4 theme. It runs in demo mode (demoMode=true): all data is from MockData, no live reads.

func NewModelLive

func NewModelLive(cfg Config, layoutOverride LayoutMode, p project.Project, ls *livesession.LiveSession, hs *hookserver.Server, ds DiffSource, sourceName string) Model

NewModelLive constructs a Model wired to real Claude Code data. It runs in live mode: on Init it fires a LiveSession.Snapshot and then refreshes every liveRefreshInterval. The demo data is kept as a fallback for panels not yet wired in Phase A (explorer, servers, diff, usage details).

p is the project to watch (typically os.Getwd()). ls is the LiveSession use case already constructed with real adapters. hs is the optional hook server; pass nil to disable hook integration. ds is the optional DiffSource for Phase E; pass nil to keep mock diff data. sourceName is the LLM source name for the title bar (e.g. "claude-code").

func NewModelWithConfig

func NewModelWithConfig(cfg Config, layoutOverride LayoutMode) Model

NewModelWithConfig constructs a Model with the given config and layout override. Runs in demo mode (demoMode=true).

func (Model) Init

func (m Model) Init() tea.Cmd

Init implements tea.Model. Starts the animation tick loop. In live mode, also fires the first snapshot immediately and starts the hook server watcher if a hook server was provided.

Boot splash is opt-out via Config.BootScreenEnabled and only activates after Init runs (i.e. tea.NewProgram(...).Run()) so unit tests that build a Model and call View() directly never see it.

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. Composes all panels.

func (Model) WithPlanUsageSource

func (m Model) WithPlanUsageSource(src ports.PlanUsageSource) Model

WithPlanUsageSource attaches a ports.PlanUsageSource to the Model so the usage panel can show real plan-quota percentages instead of the time-elapsed approximation. Pass nil to disable (the panel falls back to the JSONL-derived display).

Returns the updated Model — the caller MUST replace its reference, matching the pattern used by NewModelLive.

type ModifiedFile

type ModifiedFile struct {
	Mark  string // "M" or "+"
	Path  string
	Stats string // "+14 −3" etc.
}

ModifiedFile is a file changed during the session.

type NotificationStyle

type NotificationStyle string

NotificationStyle picks the visual treatment for hook prompts and the compaction-imminent warning.

const (
	NotificationFullscreen NotificationStyle = "fullscreen"
	NotificationBanner     NotificationStyle = "banner"
	NotificationOff        NotificationStyle = "off"
)

Notification style constants.

NotificationFullscreen — animated centered overlay (default). Covers
                          the panel grid until dismissed; pulses to
                          grab attention for hook prompts.
NotificationBanner     — compact 3-row banner above the status bar
                          (the V22 behavior, kept for users who find
                          fullscreen too aggressive).
NotificationOff        — no UI surfacing. Hook events still block on
                          the server side; the user must rely on the
                          claude CLI's own prompt or use y/n hotkeys.

func (NotificationStyle) Display

func (s NotificationStyle) Display() string

Display returns a short human-readable label used in the settings chip.

func (NotificationStyle) IsValid

func (s NotificationStyle) IsValid() bool

IsValid reports whether s is a recognized NotificationStyle. Used when loading config to fall back to the default if an unknown string slipped in via a hand-edited TOML file.

func (NotificationStyle) Next

Next returns the next style in the cycle order: fullscreen → banner → off → fullscreen. Drives the Enter handler on the settings overlay.

type NowStatus

type NowStatus struct {
	// Op is the concise action string shown on line 1:
	// "edit auth.ts", "read main.go", "bash 'go test'", "thinking…", etc.
	Op string

	// Meta is the secondary detail string shown on line 2:
	// elapsed time for bash, token rate for thinking, path tail for reads.
	Meta string

	// ModeText is the header badge text (top-right of panel border):
	// "writing · NN t/s" during streaming, tool name or "idle" otherwise.
	ModeText string

	// MascotTrigger is the mascot event to fire on this status update.
	// Valid trigger values are eventHappy, eventSurprised, eventSleep.
	MascotTrigger mascotEventKind

	// HasTrigger is true when MascotTrigger should be applied.
	// Separates "no trigger" from eventBlink (index 0).
	HasTrigger bool
}

NowStatus holds the derived display strings for the now panel. It is produced by deriveNowStatus and consumed by applyLiveView.

type Palette

type Palette struct {
	// Surface
	Bg         color.Color // terminal surface
	Surface    color.Color // panel bg
	SurfaceAcc color.Color // focused panel interior tint

	// Borders
	BorderDim color.Color // default panel border
	BorderAcc color.Color // active/focused panel border

	// Foreground
	Text     color.Color // primary text
	TextMid  color.Color // secondary text
	TextDim  color.Color // metadata, labels
	TextFade color.Color // gutters, disabled

	// Accents
	Purple  color.Color // clyde brand, active substate
	Pink    color.Color // mascot, claude voice ONLY
	Magenta color.Color // expanded-active panel borders
	Cyan    color.Color // paths, info
	Green   color.Color // live, done, success
	Amber   color.Color // modified, warnings
	Red     color.Color // removed, errors, current file
	Orange  color.Color // grep / find tags
}

Palette holds all semantic color roles for a theme. Every style in the UI is derived from this palette — never hardcoded hex.

func ClydeDarkPalette

func ClydeDarkPalette() Palette

ClydeDarkPalette is an alias for TokyoNightPalette kept for API compat.

func PaletteFor

func PaletteFor(t Theme) Palette

PaletteFor returns the palette for the given theme. Falls back to Tokyo Night when the theme is unknown — keeps the UI rendering even if a user hand-edits an invalid value into config.toml.

func TokyoNightPalette

func TokyoNightPalette() Palette

TokyoNightPalette returns the v4 Tokyo Night palette. Values are EXACT — do not substitute.

type PanelCollapseState

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

PanelCollapseState holds collapse state + animation spring for one panel.

func NewPanelCollapseState

func NewPanelCollapseState(startCollapsed bool, expandedH float64) PanelCollapseState

NewPanelCollapseState initializes collapse state. startCollapsed sets the initial visual state. expandedH is the full panel height in rows.

func (*PanelCollapseState) Advance

func (p *PanelCollapseState) Advance()

Advance ticks the spring one frame.

func (*PanelCollapseState) Collapse

func (p *PanelCollapseState) Collapse()

Collapse ensures the panel is collapsed.

func (*PanelCollapseState) Expand

func (p *PanelCollapseState) Expand()

Expand ensures the panel is expanded (does not toggle if already expanded).

func (*PanelCollapseState) ExpandedHeight

func (p *PanelCollapseState) ExpandedHeight() int

ExpandedHeight returns the settled expanded-height target — the height the panel reaches once the expand animation finishes. Use this (not the animated Height) when sizing things that must match the final layout, e.g. viewport scroll clamping.

func (*PanelCollapseState) Height

func (p *PanelCollapseState) Height() int

Height returns the current animated height for rendering.

func (*PanelCollapseState) IsCollapsed

func (p *PanelCollapseState) IsCollapsed() bool

IsCollapsed reports the logical collapsed state (not the animated state).

func (*PanelCollapseState) IsSettled

func (p *PanelCollapseState) IsSettled() bool

IsSettled reports whether the wrapped spring has converged. Used by the model's adaptive frame-tick gate to decide whether the View loop can drop to idleTickInterval.

func (*PanelCollapseState) SetExpandedHeight

func (p *PanelCollapseState) SetExpandedHeight(h float64)

SetExpandedHeight updates the target expanded height (e.g. on window resize).

func (*PanelCollapseState) Toggle

func (p *PanelCollapseState) Toggle()

Toggle flips collapsed state and updates the spring target.

type PanelConfig

type PanelConfig struct {
	Enabled          bool `toml:"enabled"`
	DefaultCollapsed bool `toml:"default_collapsed"`
	Position         int  `toml:"position"`
	Height           int  `toml:"height,omitempty"`
}

PanelConfig holds per-panel display settings.

Height is the user's persisted manual resize for this panel. When RememberLayout is enabled at the Config level, +/- resize and the space-toggle write back to Height + DefaultCollapsed so the next session restores the same layout. When RememberLayout is off, Height is ignored and the panel falls back to its computed default.

type PanelID

type PanelID int

PanelID identifies a focusable panel.

const (
	PanelNow PanelID = iota
	PanelCalls
	PanelDiff
	PanelUsage
	PanelExplorer
	PanelServers
	// PanelBash — session-wide ledger of every Bash command claude has run,
	// with exit/duration. Off by default; opt-in via the settings overlay
	// per project.
	PanelBash
	// PanelCache — prompt-cache efficiency dashboard (hit ratio, totals,
	// biggest cache miss, per-turn trend sparkline). Off by default.
	PanelCache

	// PanelNone is the zero-like sentinel meaning "no panel is active".
	// Defined after panelCount so it's never confused with a real panel.
	PanelNone PanelID = -1
)

Panel ID constants. Tab cycles through these in order.

type PanelLayout

type PanelLayout struct {
	Height           int  `toml:"height,omitempty"`
	DefaultCollapsed bool `toml:"default_collapsed,omitempty"`
}

PanelLayout is the per-project subset of PanelConfig that participates in RememberLayout persistence. Height==0 means "no manual override"; DefaultCollapsed defaults to false and is omitted from the TOML when it matches that zero value.

type PanelState

type PanelState int

PanelState describes which visual state a panel is rendered in.

const (
	// PanelStateNormal — unfocused, collapsed or expanded.
	PanelStateNormal PanelState = iota
	// PanelStatePassive — focused, expanded-passive (purple border).
	PanelStatePassive
	// PanelStateActive — focused, expanded-active (pink double border + mode badge).
	PanelStateActive
)

type PanelsConfig

type PanelsConfig struct {
	Now      PanelConfig `toml:"now"`
	Calls    PanelConfig `toml:"calls"` // v13: replaces tasks
	Tasks    PanelConfig `toml:"tasks"` // legacy alias — copied onto Calls in LoadConfig when [panels.calls] is absent
	Diff     PanelConfig `toml:"diff"`
	Usage    PanelConfig `toml:"usage"`
	Explorer PanelConfig `toml:"explorer"`
	Servers  PanelConfig `toml:"servers"`
	Bash     PanelConfig `toml:"bash"`  // v22+: session-wide bash command ledger
	Cache    PanelConfig `toml:"cache"` // v22+: prompt-cache efficiency dashboard
}

PanelsConfig holds all panel configs.

type ProjectOverride

type ProjectOverride struct {
	Panels       map[string]bool        `toml:"panels,omitempty"`
	PanelLayouts map[string]PanelLayout `toml:"panel_layouts,omitempty"`
}

ProjectOverride is a per-project settings layer applied on top of the global config when the user is launched in that working directory.

Panels carries per-panel visibility overrides (enabled flag). PanelLayouts carries per-panel layout overrides (manual Height and DefaultCollapsed) so RememberLayout-driven persistence is scoped to the project the user actually resized panels in.

type QuotaNotification

type QuotaNotification struct {
	Active   bool
	Headline string
	Detail   string
	Severity QuotaSeverity
}

QuotaNotification carries a derived plan-quota or cost alert.

Active is set when one of the configured thresholds has just been crossed upward; the renderers branch on Severity to pick the right chrome and copy.

resolveNotification suppresses Active while notifAck is set, so dismiss flow matches hook + compaction.

type QuotaSeverity

type QuotaSeverity int

QuotaSeverity tints the chrome and chooses the title copy.

const (
	// QuotaSeverityWarn is the 90% crossing — heads-up.
	QuotaSeverityWarn QuotaSeverity = iota
	// QuotaSeverityDanger is the 95% crossing — final warning.
	QuotaSeverityDanger
)

type Server

type Server struct {
	Status ServerStatus
	Name   string
	Count  string // tool count for MCPs; empty for LSPs
}

Server is one entry in the servers panel.

type ServerStatus

type ServerStatus int

ServerStatus is the state of an MCP/LSP server.

const (
	ServerOn   ServerStatus = iota // server is active
	ServerBusy                     // server is busy (amber)
	ServerOff                      // server is offline
)

Server status constants.

type SessionTab

type SessionTab struct {
	// ID is the session identifier; empty for the synthetic Σ aggregate
	// tab (which represents "all sessions in this cwd").
	ID session.ID

	// Label is the short text rendered inside the tab (e.g. "fix auth"
	// or a 6-char session-ID prefix). For the Σ tab this is "Σ all".
	Label string

	// Active is true for the currently-focused tab.
	Active bool

	// IsAggregate is true for the leftmost Σ tab.
	IsAggregate bool

	// ContextPct is the model-context fill percent for this session's
	// most recent assistant turn. Zero for the Σ tab.
	ContextPct int

	// Warning is true when the session is close to compaction
	// (ContextPct > sessionCtxWarnThreshold). Drives the ⚠ glyph on
	// inactive tabs so users notice the danger even when not focused on
	// that session.
	Warning bool

	// Live is true when the session was written to recently (claude code
	// still has it open). Drives the bullet glyph on the tab — orthogonal
	// to Active (which is the user's focused tab in clyde). A closed /
	// idle session that the user happens to have selected reads as
	// "[ resume ]" without a bullet; a running session that is not the
	// current tab still gets "[● label]" so the user can spot it.
	Live bool

	// Tokens is the total session token count (formatted in the panel
	// when needed). Mostly used by the Σ leaderboard view.
	Tokens int64
}

SessionTab is one tab rendered in the title-bar tab strip. Tabs cover every recently-active session in the current cwd, plus a Σ aggregate.

type SettingsPanelToggle

type SettingsPanelToggle struct {
	PanelID         PanelID
	Label           string
	Enabled         bool
	StartsCollapsed bool
	IsOverride      bool // true when the project layer differs from the global layer
}

SettingsPanelToggle holds the per-panel toggle state for the settings overlay.

Enabled mirrors the visibility flag (with project-scope override semantics). StartsCollapsed mirrors the global DefaultCollapsed flag — it is always edited at the global layer regardless of the current scope, since we don't support per-project default-collapsed overrides.

type SettingsScope

type SettingsScope int

SettingsScope chooses which layer of the config the overlay edits.

const (
	// ScopeProject — edits go to baseCfg.Projects["<cwd>"].Panels (default
	// when a cwd is known). Effective for ONLY this project.
	ScopeProject SettingsScope = iota
	// ScopeGlobal — edits go to baseCfg.Panels (the top-level [panels]).
	// Used when the user wants a setting to apply to every project that
	// does not explicitly override it.
	ScopeGlobal
)

type Styles

type Styles struct {
	// Panel chrome (normal border)
	Panel       lipgloss.Style
	PanelFocus  lipgloss.Style // Expanded-Passive: purple border
	PanelActive lipgloss.Style // Expanded-Active: pink double border

	// Panel label / metadata riding the border
	PanelLabel       lipgloss.Style
	PanelLabelFocus  lipgloss.Style // purple when focused (passive)
	PanelLabelActive lipgloss.Style // pink when in active mode
	PanelMeta        lipgloss.Style
	PanelModeBadge   lipgloss.Style // active-mode badge text (pink)

	// Collapsed panel one-liner summary
	PanelCollapsedLabel      lipgloss.Style // dim when blurred
	PanelCollapsedLabelFocus lipgloss.Style // purple when focused

	// Title bar
	TitleBar    lipgloss.Style
	TitleBrand  lipgloss.Style // "clyde" in purple
	TitlePath   lipgloss.Style // dim path
	TitleProjct lipgloss.Style // cyan project name
	TitleLive   lipgloss.Style // green "claude · live"
	TitleMeta   lipgloss.Style // dim meta labels
	TitleValue  lipgloss.Style // white values

	// Explorer
	SectionHeader lipgloss.Style // dim section label
	SectionCount  lipgloss.Style // fade count on right
	FileModMark   lipgloss.Style // M amber
	FileAddMark   lipgloss.Style // + green
	FileName      lipgloss.Style // mid text
	FileNameAct   lipgloss.Style // red — active file
	FileStats     lipgloss.Style // dim +/- counts
	TreeDir       lipgloss.Style // blue-ish dir color
	TreeIndent    lipgloss.Style // fade
	HintKey       lipgloss.Style // purple kbd hints
	HintText      lipgloss.Style // dim hint text

	// Now panel
	Mascot      lipgloss.Style // pink — bunny body
	MascotZZ    lipgloss.Style // dim — sleep zZ annotation
	NowOp       lipgloss.Style // purple operation line
	NowMeta     lipgloss.Style // dim meta
	NowCode     lipgloss.Style // cyan inline code
	NowDetail   lipgloss.Style // dim detail (e.g. · line 46)
	StatusGreen lipgloss.Style // green status dot
	StatusPurpl lipgloss.Style // purple status dot/chevron

	// Tasks panel
	TaskDoneIcon    lipgloss.Style // green ✓
	TaskDoneName    lipgloss.Style // muted strikethrough-ish
	TaskDur         lipgloss.Style // dim duration
	TaskSubtitle    lipgloss.Style // small dim subtitle
	TaskActBorder   lipgloss.Style // active task outer box
	TaskActName     lipgloss.Style // text active task name
	TaskActDur      lipgloss.Style // purple duration
	TaskActOp       lipgloss.Style // spinner+code line
	TaskActCode     lipgloss.Style // cyan code ref
	TaskSubDoneIcon lipgloss.Style
	TaskSubDoneName lipgloss.Style
	TaskSubActIcon  lipgloss.Style // purple
	TaskSubActName  lipgloss.Style
	TaskSubPenIcon  lipgloss.Style // fade
	TaskSubPenName  lipgloss.Style // dim
	TaskPenIcon     lipgloss.Style // fade □
	TaskPenName     lipgloss.Style // dim
	ProgPct         lipgloss.Style // purple %
	ProgLeft        lipgloss.Style // dim "N left"
	ProgBarBg       lipgloss.Style // dark bar bg

	// Diff panel
	DiffHunk    lipgloss.Style // dim italic @@ line
	DiffLineNum lipgloss.Style // fade line number
	DiffCtx     lipgloss.Style // mid context text
	DiffAdd     lipgloss.Style // green added text
	DiffRem     lipgloss.Style // red removed text

	// Usage panel
	UsageLabel   lipgloss.Style
	UsageValue   lipgloss.Style
	UsageModel   lipgloss.Style // purple model name
	UsageDivider lipgloss.Style
	IssueDot     lipgloss.Style // base
	IssueName    lipgloss.Style
	IssueValue   lipgloss.Style

	// Servers panel
	SrvSubHeader lipgloss.Style
	SrvName      lipgloss.Style
	SrvCount     lipgloss.Style
	SrvOff       lipgloss.Style // dim name when off

	// Semantic color shortcuts (for inline dot/accent usage)
	TextFade lipgloss.Style // #3b4261 gutters/disabled
	Amber    lipgloss.Style // #e0af68 warnings

	// Notification banner
	NotifIcon lipgloss.Style // purple ◆
	NotifWho  lipgloss.Style // pink "claude" — ONLY place pink is used on text
	NotifText lipgloss.Style
	NotifCmd  lipgloss.Style // amber code
	NotifPath lipgloss.Style // cyan path
	KbdChip   lipgloss.Style // keyboard chip [y]

	// Status bar
	StatusBar   lipgloss.Style
	StatusKey   lipgloss.Style // purple keybind
	StatusSep   lipgloss.Style // fade ·
	StatusVer   lipgloss.Style // dim version
	StatusReady lipgloss.Style // green "ready"

	// Tab strip (Mode B)
	TabActive   lipgloss.Style // active tab — purple text
	TabInactive lipgloss.Style // inactive tab — dim
	TabBorder   lipgloss.Style // tab separator
}

Styles holds all per-component lipgloss styles derived from a Palette. Never create lipgloss styles inline in rendering code — go through this struct so the theme stays consistent.

func NewStyles

func NewStyles(p Palette) Styles

NewStyles builds a Styles struct from a Palette.

func (Styles) WithFadedFocus

func (s Styles) WithFadedFocus(p Palette, alpha float64) Styles

WithFadedFocus returns a copy of s with the focus-chrome foregrounds interpolated toward their dim equivalents based on alpha.

alpha=1 → unchanged (full purple/BorderAcc on focused panels)
alpha=0 → focus colors collapse to dim/TextDim equivalents
in between → blendColor across BorderDim↔BorderAcc and TextDim↔Purple

Only the chrome that distinguishes Passive (focused) from Normal flips — PanelStateActive / PanelLabelActive / PanelMeta etc are left alone so active mode and meta text don't get caught up in the fade.

Used by render_stack/2col/multicol when the focus-decay handoff is in flight: the previously-focused panel's border tints purple → gray over 3s and the now-panel's border tints gray → purple over the next 3s.

type SubStep

type SubStep struct {
	Done   bool
	Active bool
	Name   string
	Dur    string // "0:08"
}

SubStep is a step within the active task.

type Task

type Task struct {
	Status   TaskStatus
	Icon     string // "✓", "▸", "□"
	Name     string
	Duration string
	Subtitle string    // shown for done tasks
	SubSteps []SubStep // only for active task
}

Task is a single entry in the tasks panel.

type TaskStatus

type TaskStatus int

TaskStatus describes a task's completion state.

const (
	TaskDone    TaskStatus = iota // completed task
	TaskActive                    // currently running task
	TaskPending                   // not yet started task
)

Task status constants.

type TerminalGraphics

type TerminalGraphics int

TerminalGraphics describes the image rendering capability of the current terminal.

const (
	// GraphicsNone — no inline image support; use ASCII fallback.
	GraphicsNone TerminalGraphics = iota
	// GraphicsKitty — Kitty graphics protocol (APC _G sequences).
	GraphicsKitty
	// GraphicsGhostty — Ghostty terminal (also supports Kitty graphics).
	GraphicsGhostty
)

func DetectTerminalGraphics

func DetectTerminalGraphics() TerminalGraphics

DetectTerminalGraphics checks the $TERM and $TERM_PROGRAM environment variables to determine whether Kitty graphics protocol is available.

Supported values:

TERM=xterm-kitty            → Kitty
TERM=ghostty                → Ghostty (Kitty-compatible)
TERM=xterm-ghostty          → Ghostty
TERM_PROGRAM=ghostty        → Ghostty

type Theme

type Theme string

Theme identifies a palette in the registry. The string value is what's persisted in config.toml so users can edit it by hand.

const (
	ThemeTokyoNight Theme = "tokyo-night"
	ThemeCatppuccin Theme = "catppuccin"
	ThemeDracula    Theme = "dracula"
	ThemeGruvbox    Theme = "gruvbox"
	ThemeNord       Theme = "nord"
	ThemeRosePine   Theme = "rose-pine"
	ThemeKanagawa   Theme = "kanagawa"
)

Theme constants. Keep ThemeTokyoNight first — it's the historical default.

func (Theme) Display

func (t Theme) Display() string

Display returns a short human-readable label used in the settings chip.

func (Theme) IsValid

func (t Theme) IsValid() bool

IsValid reports whether t is a registered theme.

func (Theme) Next

func (t Theme) Next() Theme

Next returns the next theme in the registry's cycle order. Wraps from the last theme back to the first. Used by the settings overlay's Enter handler.

type ToolCall

type ToolCall struct {
	AgentID    string
	AgentName  string
	IsSubagent bool
	Tool       string    // "Read", "Edit", "Bash", "Grep", etc.
	KeyArg     string    // first/main argument (path, command, pattern)
	Duration   string    // "12s", "1.4m"
	State      CallState // CallDone, CallActive, CallFailed
}

ToolCall is a single tool invocation within an agent session.

type TreeNode

type TreeNode struct {
	Indent   string // indentation prefix using │ characters
	Mark     string // "M", "+", " "
	IsDir    bool   // renders with dir color
	IsAct    bool   // currently active file (red highlight)
	Name     string
	FullPath string // optional full relative path (used by viewer for mock lookup)
}

TreeNode is a single line in the file tree view.

type UsageWindowRow

type UsageWindowRow struct {
	// Label is the window name shown in the left column: "session ctx", "5h usage", "weekly usage".
	Label string

	// Percent is the 0-100 fill ratio for this row's progress bar.
	//   - session row: % of model context window used (compaction risk).
	//   - 5h / weekly rows: % of plan quota consumed (when IsPlanQuota=true)
	//     OR % of time elapsed in window (when IsPlanQuota=false fallback).
	Percent int

	// IsPlanQuota is true when Percent comes from the Anthropic plan-usage
	// API (real plan-quota %), false when it's a time-elapsed approximation
	// derived from JSONL timestamps. Used by the panel to decide whether
	// to show the "(plan offline)" badge.
	IsPlanQuota bool

	// Input is the formatted input-token count, e.g. "12k".
	Input string

	// Output is the formatted output-token count, e.g. "5k".
	Output string

	// Cache is the formatted cache-read token count, e.g. "30k".
	Cache string

	// Cost is the formatted USD cost, e.g. "$0.42".
	Cost string

	// TotalUsed is the formatted total token count for the window, e.g. "3.4M".
	// Used in the two-line per-window display as the "total used" line.
	TotalUsed string

	// CurrentCtx is the "Nk / 1M (N%)" string for the current context window.
	// Only populated for the session row (LatestUsage context, not TotalUsage).
	// Empty for 5h and 7d rows.
	CurrentCtx string

	// ResetsIn is the human-readable countdown until the window resets,
	// e.g. "3h 57m" for the 5h window, "4d 14h" for the 7d window.
	// Empty when the reset time is unknown or has already passed.
	ResetsIn string

	// ResetAt is the wall-clock time when this window resets. Zero when
	// unknown (e.g. hand-authored demo rows). Used to pick the SOONEST
	// reset for the synthetic "next reset" row.
	ResetAt time.Time

	// ResetElapsedPct is the 0-100 percentage of TIME elapsed in this
	// window (0 = just reset, 100 = about to reset). This — not Percent,
	// which may hold the plan-quota % — feeds the "next reset" bar so its
	// fill always matches the countdown headline.
	ResetElapsedPct int

	// Empty is true when all token counts are zero (nothing to display).
	Empty bool

	// SessionCount is the number of sessions that contributed to this row.
	// Zero means "not available" (single-project fallback or this session row).
	// Positive values are formatted as "(N sessions)" in the display.
	SessionCount int
}

UsageWindowRow holds the pre-formatted display strings for one usage time window (session / 5h / weekly). All fields are strings to keep rendering trivial.

type ViewerMode

type ViewerMode int

ViewerMode selects the viewer's interaction model.

Today only ViewerView (read-only) is implemented. ViewerEdit is reserved for a future bubbles/textarea-backed edit experience: 'i' would enter insert from view mode, ':w' would save, ':q' would quit. The field exists now so the dispatcher and the hint bar can branch on it without requiring a refactor when edit lands.

const (
	// ViewerView is read-only navigation with vim bindings.
	ViewerView ViewerMode = iota
	// ViewerEdit is text editing — placeholder, not wired yet.
	ViewerEdit
)

type ViewerViewport

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

ViewerViewport wraps the bubbles/v2/viewport model for the file viewer. xOffset is the horizontal scroll position in characters (0 = leftmost column). The bubbles viewport does not natively support horizontal scroll, so the rendering layer applies it manually before styling each line.

func NewViewerViewport

func NewViewerViewport() ViewerViewport

NewViewerViewport constructs an initial ViewerViewport. SoftWrap is OFF: code reads better truncated than wrapped at narrow widths, and predictable one-source-line-per-row scrolling keeps the scrollbar honest.

func (*ViewerViewport) LoadFile

func (v *ViewerViewport) LoadFile(path, cwd string, demoMode bool)

LoadFile loads content for the given file path. In demo mode it uses the mock content table; in live mode it reads from disk. cwd is used to resolve relative paths in live mode.

Jump to

Keyboard shortcuts

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