Documentation
¶
Overview ¶
ABOUTME: Event adapter — converts raw engine events into typed TUI messages. ABOUTME: The ONLY file in the TUI package that imports pipeline, agent, and llm engine types.
ABOUTME: AgentLog component — append-only streaming log with per-node streams. ABOUTME: Each node gets its own line buffer. Parallel branches interleave with labeled separators.
ABOUTME: Root Bubbletea App model — "Signal Cabin" dashboard orchestrator. ABOUTME: Owns layout, message routing to state store and child components, and keyboard handling.
ABOUTME: AutopilotTUIInterviewer bridges autopilot decisions to the TUI modal. ABOUTME: Gets the LLM decision, shows it briefly in the gate modal, then auto-closes.
ABOUTME: TUI package for the tracker pipeline dashboard. ABOUTME: Clean-room rewrite in progress — components being added incrementally.
ABOUTME: Header component — displays pipeline name, run ID, elapsed time, and token/cost readout. ABOUTME: Self-contained Bubbletea model with its own Update/View. Reads token data from StateStore.
ABOUTME: Help overlay — modal showing all keyboard shortcuts in a styled table. ABOUTME: Implements ModalContent so it integrates with the existing modal system.
ABOUTME: HistoryTrail bubbletea component — reverse-chronological node visit log. ABOUTME: Shows the execution path through the pipeline as a compact scrollable trail.
ABOUTME: Hybrid gate content — radio selection of known labels with optional freeform "other" input. ABOUTME: Replaces pure freeform when a human gate has labeled outgoing edges.
ABOUTME: Fullscreen multi-field interview form modal for interview-mode human gates. ABOUTME: Renders questions as radio selects, yes/no toggles, or textareas with pagination.
ABOUTME: BubbleteaInterviewer bridges pipeline gate handlers to the TUI. ABOUTME: Mode 1 runs inline tea.Programs per gate; Mode 2 delegates via SendFunc to a running TUI.
ABOUTME: All typed Bubbletea message constants for the TUI. ABOUTME: Components communicate exclusively through these messages — no string comparisons.
ABOUTME: Modal overlay with pluggable content (choice selection, freeform input). ABOUTME: Renders a centered bordered box over background content using lipgloss.
ABOUTME: NodeList component — signal lamp panel showing pipeline nodes with status and thinking animation. ABOUTME: Renders colored indicator lamps per node and shows spinner frames for thinking nodes.
ABOUTME: Desktop notification utility — fires OS-native notifications on pipeline completion. ABOUTME: Uses osascript on macOS, notify-send on Linux. Respects TRACKER_NO_NOTIFY env var.
ABOUTME: ProgressTracker — renders an ASCII progress bar with ETA from rolling average. ABOUTME: Uses simple block characters styled to match the amber/dim palette.
ABOUTME: Split-pane review content for long human gate prompts. ABOUTME: Glamour-rendered scrollable viewport on top, textarea on bottom. Used for plan approval.
ABOUTME: ReviewHybridContent — scrollable context viewport with radio selection + freeform below. ABOUTME: Used when a labeled human gate has substantial context (agent output, errors).
ABOUTME: Reusable scroll container with auto-scroll and manual override. ABOUTME: Tracks visible range within a list of lines, re-enabling auto-scroll on ScrollToBottom.
ABOUTME: SearchBar for the agent log — uses bubbles/textinput for inline search. ABOUTME: Highlights matching lines in yellow; n/N jump between matches.
ABOUTME: Central state container for the TUI with Apply(msg) pattern. ABOUTME: Holds node entries, statuses, thinking state, and pipeline completion state.
ABOUTME: StatusBar component — bottom bar with track diagram, progress, and keybinding hints. ABOUTME: Renders colored lamp glyphs per node and a compact progress summary.
ABOUTME: Style registry for the TUI — "Signal Cabin" aesthetic with train control panel colors. ABOUTME: Lamp indicators, thinking animation frames, and lipgloss styles for all components.
ABOUTME: Per-node LLM thinking state tracker with animation frames and elapsed time. ABOUTME: Manages a global tick counter that advances all active nodes through spinner frames.
Index ¶
- Constants
- Variables
- func AdaptAgentEvent(evt agent.Event, nodeID string) tea.Msg
- func AdaptLLMTraceEvent(evt llm.TraceEvent, nodeID string, verbose bool) []tea.Msg
- func AdaptPipelineEvent(evt pipeline.PipelineEvent) tea.Msg
- func CompletionRow(status pipeline.TerminalStatus, override *pipeline.OverrideDetail, ...) string
- func DecisionString(decision string) string
- func HighlightLine(line, term string) string
- func IsSubgraphNode(id string) bool
- func SendNotification(title, body string)
- func StatusLamp(status NodeState) (string, lipgloss.Style)
- func SubgraphChildLabel(id string) string
- func SubgraphDepth(id string) int
- type AgentLog
- func (al *AgentLog) CycleVerbosity()
- func (al *AgentLog) Init() tea.Cmd
- func (al *AgentLog) Search() *SearchBar
- func (al *AgentLog) SetFocusNodeID(nodeID string)
- func (al *AgentLog) SetFocusedNode(nodeID string)
- func (al *AgentLog) SetSize(w, h int)
- func (al *AgentLog) SetVerboseTrace(v bool)
- func (al *AgentLog) Update(msg tea.Msg) tea.Cmd
- func (al *AgentLog) Verbosity() Verbosity
- func (al *AgentLog) View() string
- func (al *AgentLog) VisibleText() string
- type AppModel
- func (a *AppModel) ActiveNode() string
- func (a *AppModel) Header() *Header
- func (a AppModel) Init() tea.Cmd
- func (a *AppModel) SetInitialNodes(entries []NodeEntry)
- func (a *AppModel) SetVerboseTrace(v bool)
- func (a AppModel) String() string
- func (a AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)
- func (a AppModel) View() string
- type AutopilotContent
- type AutopilotTUIInterviewer
- func (a *AutopilotTUIInterviewer) Actor() pipeline.Actor
- func (a *AutopilotTUIInterviewer) Ask(prompt string, choices []string, defaultChoice string) (string, error)
- func (a *AutopilotTUIInterviewer) AskFreeform(prompt string) (string, error)
- func (a *AutopilotTUIInterviewer) AskFreeformWithLabels(prompt string, labels []string, defaultLabel string) (string, error)
- func (a *AutopilotTUIInterviewer) AskInterview(questions []handlers.Question, prev *handlers.InterviewResult) (*handlers.InterviewResult, error)
- type BubbleteaInterviewer
- func (b *BubbleteaInterviewer) Actor() pipeline.Actor
- func (b *BubbleteaInterviewer) Ask(prompt string, choices []string, defaultChoice string) (string, error)
- func (b *BubbleteaInterviewer) AskFreeform(prompt string) (string, error)
- func (b *BubbleteaInterviewer) AskFreeformWithLabels(prompt string, labels []string, defaultLabel string) (string, error)
- func (b *BubbleteaInterviewer) AskInterview(questions []handlers.Question, prev *handlers.InterviewResult) (*handlers.InterviewResult, error)
- type Cancellable
- type ChoiceContent
- type FreeformContent
- type FullscreenContent
- type Header
- type HelpContent
- type HistoryTrail
- type HybridContent
- type InterviewContent
- type LineKind
- type Modal
- type ModalContent
- type MsgAgentError
- type MsgClearFocus
- type MsgCycleVerbosity
- type MsgFocusNode
- type MsgGateAutopilot
- type MsgGateChoice
- type MsgGateFreeform
- type MsgGateInterview
- type MsgHeaderTick
- type MsgLLMFinish
- type MsgLLMProviderRaw
- type MsgLLMRequestPreparing
- type MsgLLMRequestStart
- type MsgModalDismiss
- type MsgNodeCompleted
- type MsgNodeFailed
- type MsgNodeRetrying
- type MsgNodeStarted
- type MsgPipelineCompleted
- type MsgPipelineDone
- type MsgPipelineFailed
- type MsgReasoningChunk
- type MsgSearchActivate
- type MsgSearchDeactivate
- type MsgSearchUpdate
- type MsgStatusFlash
- type MsgStatusFlashClear
- type MsgTextChunk
- type MsgThinkingStarted
- type MsgThinkingStopped
- type MsgThinkingTick
- type MsgToggleExpand
- type MsgToolCallEnd
- type MsgToolCallStart
- type MsgValidationOverridden
- type MsgVerifyStatus
- type NodeEntry
- type NodeFlags
- type NodeList
- type NodePhase
- type NodeState
- type PipelineAdapter
- type ProgressTracker
- type ReviewContent
- type ReviewHybridContent
- type ScrollView
- func (sv *ScrollView) Append(line string)
- func (sv *ScrollView) AutoScroll() bool
- func (sv *ScrollView) Len() int
- func (sv *ScrollView) Lines() []string
- func (sv *ScrollView) ScrollDown(n int)
- func (sv *ScrollView) ScrollToBottom()
- func (sv *ScrollView) ScrollUp(n int)
- func (sv *ScrollView) SetHeight(h int)
- func (sv *ScrollView) UpdateLast(line string)
- func (sv *ScrollView) VisibleRange() (start, end int)
- type SearchBar
- func (s *SearchBar) Activate()
- func (s *SearchBar) Active() bool
- func (s *SearchBar) Confirm()
- func (s *SearchBar) CurrentMatchLine() int
- func (s *SearchBar) Deactivate()
- func (s *SearchBar) MatchCount() int
- func (s *SearchBar) NextMatch()
- func (s *SearchBar) PrevMatch()
- func (s *SearchBar) Term() string
- func (s *SearchBar) Update(msg tea.Msg) (tea.Cmd, bool)
- func (s *SearchBar) UpdateMatches(lines []styledLine)
- func (s *SearchBar) UpdateMatchesFiltered(lines []styledLine, filteredIndices []int)
- func (s *SearchBar) View() string
- type SendFunc
- type StateStore
- func (s *StateStore) Apply(msg interface{})
- func (s *StateStore) GetPhase(id string) NodePhase
- func (s *StateStore) HeadlineOverride() *pipeline.OverrideDetail
- func (s *StateStore) IsOnCurrentPath(nodeID string) bool
- func (s *StateStore) IsThinking(id string) bool
- func (s *StateStore) IsWaiting(id string) bool
- func (s *StateStore) NodeCost(id string) float64
- func (s *StateStore) NodeDuration(id string) time.Duration
- func (s *StateStore) NodeError(id string) string
- func (s *StateStore) NodeRetryMessage(id string) string
- func (s *StateStore) NodeStatus(id string) NodeState
- func (s *StateStore) NodeTokens(id string) int
- func (s *StateStore) Nodes() []NodeEntry
- func (s *StateStore) PhaseElapsed(id string) time.Duration
- func (s *StateStore) PipelineDone() bool
- func (s *StateStore) PipelineError() string
- func (s *StateStore) PipelineStatus() pipeline.TerminalStatus
- func (s *StateStore) Progress() (done, total int)
- func (s *StateStore) SetNodes(entries []NodeEntry)
- func (s *StateStore) ValidationOverrides() []pipeline.OverrideDetail
- func (s *StateStore) VisitPath() []string
- type StatusBar
- type StyleRegistry
- type ThinkingTracker
- func (tr *ThinkingTracker) Elapsed(nodeID string) time.Duration
- func (tr *ThinkingTracker) Frame(nodeID string) string
- func (tr *ThinkingTracker) IsThinking(nodeID string) bool
- func (tr *ThinkingTracker) IsToolRunning(nodeID string) bool
- func (tr *ThinkingTracker) Start(nodeID string)
- func (tr *ThinkingTracker) StartAt(nodeID string, t time.Time)
- func (tr *ThinkingTracker) StartTool(nodeID, toolName string)
- func (tr *ThinkingTracker) Stop(nodeID string)
- func (tr *ThinkingTracker) StopTool(nodeID string)
- func (tr *ThinkingTracker) Tick()
- func (tr *ThinkingTracker) ToolName(nodeID string) string
- type Verbosity
Constants ¶
const ( LampRunning = "◉" // active/pulsing signal LampDone = "●" // lit signal lamp LampPending = "○" // off/pending signal LampFailed = "✖" // fault indicator )
Signal lamp indicators — control panel style status lights.
Variables ¶
var ( ColorPanel = lipgloss.Color("234") // #1c1c1c — instrument panel dark ColorBrightText = lipgloss.Color("255") // white — high-visibility primary ColorAmber = lipgloss.Color("214") // #ffaf00 — amber signal lamp (running) ColorGreen = lipgloss.Color("34") // #00af00 — green clear signal (done) ColorRed = lipgloss.Color("196") // #ff0000 — red alarm (failed) ColorBezel = lipgloss.Color("24") // #005f87 — instrument bezel blue ColorReadout = lipgloss.Color("117") // #87d7ff — digital readout blue ColorLabel = lipgloss.Color("244") // #808080 — panel label grey ColorOff = lipgloss.Color("239") // #4e4e4e — off indicator (pending) ColorDim = lipgloss.Color("240") // dim grey for secondary text // Tool palette — distinct colors per tool category. ColorBash = lipgloss.Color("178") // #d7af00 — gold terminal ColorFile = lipgloss.Color("75") // #5fafff — blue file ops ColorGrep = lipgloss.Color("114") // #87d787 — green search ColorAgent = lipgloss.Color("213") // #ff87ff — magenta spawn ColorPatch = lipgloss.Color("180") // #d7af87 — tan patch/apply // ColorOverride is Tailwind amber-600 — the chosen color for the // validation_overridden terminal status (Gap 5.2 spec D18). Canonical // source for the override amber across both the TUI and CLI surfaces; // cmd/tracker/branding.go's colorOverride aliases this value so future // updates only need to land here. Distinct from ColorAmber (#ffaf00, // used for running/warning states) so the override treatment is visually // unique within the TUI palette. ColorOverride = lipgloss.Color("#D97706") )
"Platform Edge" color palette — derived from train driving control panels.
var ( ColorRunning = ColorAmber ColorDone = ColorGreen ColorFailed = ColorRed ColorPending = ColorOff )
Color aliases for semantic node status colors.
var Styles = StyleRegistry{ NodeName: lipgloss.NewStyle().Foreground(ColorBrightText).Bold(true), DimText: lipgloss.NewStyle().Foreground(ColorDim), PrimaryText: lipgloss.NewStyle().Foreground(ColorBrightText), ZoneLabel: lipgloss.NewStyle().Foreground(ColorLabel).Bold(true), Readout: lipgloss.NewStyle().Foreground(ColorReadout), PanelBorder: lipgloss.DoubleBorder(), Header: lipgloss.NewStyle().Foreground(ColorBrightText).Bold(true), Muted: lipgloss.NewStyle().Foreground(ColorDim), StatusBar: lipgloss.NewStyle().Background(ColorPanel).Padding(0, 1), ToolName: lipgloss.NewStyle().Foreground(ColorAmber).Bold(true), Error: lipgloss.NewStyle().Foreground(ColorRed).Bold(true), Warn: lipgloss.NewStyle().Foreground(ColorAmber).Bold(true), Thinking: lipgloss.NewStyle().Foreground(ColorAmber), SearchMatch: lipgloss.NewStyle().Background(lipgloss.Color("226")).Foreground(lipgloss.Color("0")), CostBadge: lipgloss.NewStyle().Foreground(ColorAmber), VerbosityBadge: lipgloss.NewStyle().Foreground(ColorReadout).Bold(true), }
Styles is the global style registry instance.
var ThinkingFrames = [4]string{"◐", "◓", "◑", "◒"}
ThinkingFrames are the animation frames for the thinking spinner.
Functions ¶
func AdaptAgentEvent ¶
AdaptAgentEvent maps an agent session event to a typed TUI message. Returns nil for event types that have no TUI representation.
func AdaptLLMTraceEvent ¶
AdaptLLMTraceEvent maps an LLM trace event to one or more typed TUI messages. Some trace events produce multiple messages (e.g. TraceRequestStart emits both MsgLLMRequestStart and MsgThinkingStarted). Returns nil for filtered events.
func AdaptPipelineEvent ¶
func AdaptPipelineEvent(evt pipeline.PipelineEvent) tea.Msg
AdaptPipelineEvent maps a pipeline lifecycle event to a typed TUI message. Returns nil for event types that have no TUI representation.
This is the stateless free-function form: the returned MsgPipelineCompleted always has zero-valued Status and nil Override, because the function doesn't observe override events across the run. Callers that need the completion message to carry Status + Override should use the stateful PipelineAdapter (NewPipelineAdapter) instead — it accumulates EventValidationOverridden across the run and synthesizes the headline at completion. Tests retain the free-function form for one-shot conversions.
func CompletionRow ¶
func CompletionRow(status pipeline.TerminalStatus, override *pipeline.OverrideDetail, pipelineErr string) string
CompletionRow renders the terminal-status banner shown in the status bar once the pipeline has reached a final state. The bullet glyph and textual status string carry the same semantic signal as the color, so NO_COLOR / monochrome terminals still distinguish the four states (Gap 5.2 spec D17 + D18). Override is non-nil only for validation_overridden runs; pipelineErr supplies the failed-at-node detail when present.
Status branches:
- OutcomeSuccess: green ● Completed
- OutcomeValidationOverridden: amber ● Completed — validation override at <gate> (label "<label>" by <actor>)
- OutcomeBudgetExceeded: red ✗ Budget exceeded
- OutcomeFail: red ✗ Failed[: <error>]
- unknown/zero status: dim ● Completed (defensive default)
func DecisionString ¶
DecisionString formats the autopilot decision for display.
func HighlightLine ¶
HighlightLine highlights all occurrences of the search term in a styled line. Uses rune-aware operations for correct multi-byte character handling.
func IsSubgraphNode ¶
IsSubgraphNode returns true if the node ID contains a "/" separator, indicating it belongs to a child subgraph pipeline.
func SendNotification ¶
func SendNotification(title, body string)
SendNotification sends a desktop notification. Fire-and-forget: errors are silently ignored. Respects TRACKER_NO_NOTIFY=1 env var to disable.
func StatusLamp ¶
StatusLamp returns the indicator character and style for a node status.
func SubgraphChildLabel ¶
SubgraphChildLabel extracts the last segment of a namespaced node ID for display (e.g., "Parent/Child" → "Child").
func SubgraphDepth ¶
SubgraphDepth returns the nesting depth of a node (0 for top-level nodes).
Types ¶
type AgentLog ¶
type AgentLog struct {
// contains filtered or unexported fields
}
AgentLog renders a streaming activity log. Each pipeline node gets its own line accumulation buffer. Lines from concurrent nodes interleave in the unified log with separators when the source node changes. Lines are styled once on newline and never re-rendered.
func NewAgentLog ¶
func NewAgentLog(store *StateStore, thinking *ThinkingTracker, height int) *AgentLog
NewAgentLog creates an AgentLog with the given state, thinking tracker, and viewport height.
func (*AgentLog) CycleVerbosity ¶
func (al *AgentLog) CycleVerbosity()
CycleVerbosity advances to the next verbosity level.
func (*AgentLog) SetFocusNodeID ¶
SetFocusNodeID sets the node ID to filter by (empty = show all).
func (*AgentLog) SetFocusedNode ¶
SetFocusedNode is a no-op kept for interface compatibility. The activity log no longer tracks a single focused node — it shows all active nodes with separators.
func (*AgentLog) SetVerboseTrace ¶
SetVerboseTrace enables or disables verbose trace output.
func (*AgentLog) View ¶
View renders the agent log viewport. The indicator is always rendered at the bottom — content fills upward from the remaining space. This guarantees the indicator is never pushed off-screen regardless of content size or wrapping.
func (*AgentLog) VisibleText ¶
VisibleText returns the plain text of the visible log (for clipboard copy).
type AppModel ¶
type AppModel struct {
// contains filtered or unexported fields
}
AppModel is the root Bubbletea model composing all TUI components. All fields are pointers so mutations through value receivers propagate correctly (required by tea.Model's value-receiver interface).
func NewAppModel ¶
func NewAppModel(store *StateStore, pipelineName, runID string) *AppModel
NewAppModel creates a fully-wired App with all child components.
func (*AppModel) ActiveNode ¶
ActiveNode returns the ID of the first running node, for focusing the log.
func (*AppModel) SetInitialNodes ¶
SetInitialNodes configures the ordered node list via the state store.
func (*AppModel) SetVerboseTrace ¶
SetVerboseTrace enables or disables verbose LLM trace output in the agent log.
type AutopilotContent ¶
type AutopilotContent struct {
// contains filtered or unexported fields
}
AutopilotContent shows the autopilot's gate decision briefly before auto-closing. Read-only — the user can press Enter to dismiss early.
func NewAutopilotContent ¶
func NewAutopilotContent(prompt, decision string, replyCh chan<- string) *AutopilotContent
NewAutopilotContent creates a display-only modal showing the autopilot decision.
func (*AutopilotContent) Cancel ¶
func (a *AutopilotContent) Cancel()
func (*AutopilotContent) View ¶
func (a *AutopilotContent) View() string
type AutopilotTUIInterviewer ¶
type AutopilotTUIInterviewer struct {
// contains filtered or unexported fields
}
AutopilotTUIInterviewer wraps an AutopilotInterviewer and routes its decisions through the TUI modal for visual feedback before auto-replying.
func NewAutopilotTUIInterviewer ¶
func NewAutopilotTUIInterviewer(autopilot handlers.LabeledFreeformInterviewer, send SendFunc) *AutopilotTUIInterviewer
NewAutopilotTUIInterviewer creates an interviewer that shows autopilot decisions in the TUI modal before auto-closing.
func (*AutopilotTUIInterviewer) Actor ¶
func (a *AutopilotTUIInterviewer) Actor() pipeline.Actor
Actor returns ActorAutopilot — autopilot persona acting through the TUI surface.
func (*AutopilotTUIInterviewer) AskFreeform ¶
func (a *AutopilotTUIInterviewer) AskFreeform(prompt string) (string, error)
func (*AutopilotTUIInterviewer) AskFreeformWithLabels ¶
func (*AutopilotTUIInterviewer) AskInterview ¶
func (a *AutopilotTUIInterviewer) AskInterview(questions []handlers.Question, prev *handlers.InterviewResult) (*handlers.InterviewResult, error)
AskInterview delegates interview questions to the inner autopilot (if it supports interviews), then flashes a brief summary in the TUI.
type BubbleteaInterviewer ¶
type BubbleteaInterviewer struct {
// contains filtered or unexported fields
}
BubbleteaInterviewer implements handlers.Interviewer and handlers.FreeformInterviewer. In Mode 1 (send == nil), each gate spins up a short-lived inline tea.Program. In Mode 2 (send != nil), gates delegate via the send function and block until a response is received through the reply channel.
func NewBubbleteaInterviewer ¶
func NewBubbleteaInterviewer(send SendFunc) *BubbleteaInterviewer
NewBubbleteaInterviewer creates a Mode 2 BubbleteaInterviewer that delegates gate prompts to a running TUI program via the provided send function.
func NewMode1Interviewer ¶
func NewMode1Interviewer() *BubbleteaInterviewer
NewMode1Interviewer creates a Mode 1 BubbleteaInterviewer that runs inline tea.Programs for each gate prompt. No running TUI program required.
func (*BubbleteaInterviewer) Actor ¶
func (b *BubbleteaInterviewer) Actor() pipeline.Actor
Actor returns ActorHuman — gate response came from a real human at the TUI.
func (*BubbleteaInterviewer) Ask ¶
func (b *BubbleteaInterviewer) Ask(prompt string, choices []string, defaultChoice string) (string, error)
Ask presents a choice prompt and returns the selected option.
func (*BubbleteaInterviewer) AskFreeform ¶
func (b *BubbleteaInterviewer) AskFreeform(prompt string) (string, error)
AskFreeform presents a freeform text prompt and returns the user's input.
func (*BubbleteaInterviewer) AskFreeformWithLabels ¶
func (b *BubbleteaInterviewer) AskFreeformWithLabels(prompt string, labels []string, defaultLabel string) (string, error)
AskFreeformWithLabels presents labeled options alongside a freeform textarea.
func (*BubbleteaInterviewer) AskInterview ¶
func (b *BubbleteaInterviewer) AskInterview(questions []handlers.Question, prev *handlers.InterviewResult) (*handlers.InterviewResult, error)
AskInterview presents a multi-field interview form and returns the structured result.
type Cancellable ¶
type Cancellable interface {
Cancel()
}
Cancellable is an optional interface for modal content that can be cancelled externally (e.g., on Ctrl+C quit). Implementations should close their reply channel to unblock the pipeline handler.
type ChoiceContent ¶
type ChoiceContent struct {
// contains filtered or unexported fields
}
ChoiceContent presents a list of choices with arrow-key navigation. Sends the selected value on replyCh when Enter is pressed.
func NewChoiceContent ¶
func NewChoiceContent(prompt string, choices []string, replyCh chan<- string) *ChoiceContent
NewChoiceContent creates a choice content model. If replyCh is nil, no reply is sent on selection (useful for rendering-only tests).
func (*ChoiceContent) Cancel ¶
func (c *ChoiceContent) Cancel()
Cancel implements Cancellable for external cancellation (e.g., Ctrl+C).
func (*ChoiceContent) Update ¶
func (c *ChoiceContent) Update(msg tea.Msg) tea.Cmd
Update handles arrow keys and Enter for choice selection.
func (*ChoiceContent) View ¶
func (c *ChoiceContent) View() string
View renders the prompt and choice list with a cursor indicator.
type FreeformContent ¶
type FreeformContent struct {
// contains filtered or unexported fields
}
FreeformContent captures free-text input using a wrapping textarea. Enter inserts newlines; Ctrl+S submits. The textarea expands vertically as the user types, wrapping at the viewport width.
func NewFreeformContent ¶
func NewFreeformContent(prompt string, replyCh chan<- string) *FreeformContent
NewFreeformContent creates a freeform input content model with a wrapping textarea. If replyCh is nil, no reply is sent on submit (useful for tests).
func (*FreeformContent) Cancel ¶
func (f *FreeformContent) Cancel()
Cancel implements Cancellable for external cancellation (e.g., Ctrl+C).
func (*FreeformContent) SetWidth ¶
func (f *FreeformContent) SetWidth(w int)
SetWidth adjusts the textarea to fit the available modal width.
func (*FreeformContent) Update ¶
func (f *FreeformContent) Update(msg tea.Msg) tea.Cmd
Update handles keyboard input. Ctrl+S submits, everything else goes to the textarea (Enter inserts newlines, arrow keys navigate, etc.).
func (*FreeformContent) View ¶
func (f *FreeformContent) View() string
View renders the prompt, wrapping textarea, and key hints.
type FullscreenContent ¶
type FullscreenContent interface {
IsFullscreen() bool
}
FullscreenContent is an optional interface for modal content that wants to fill the entire terminal instead of being centered in a bordered box.
type Header ¶
type Header struct {
// contains filtered or unexported fields
}
Header renders the top bar of the TUI dashboard.
func NewHeader ¶
func NewHeader(store *StateStore, pipelineName, runID string) *Header
NewHeader creates a Header with the given state, pipeline name, and run ID.
func (*Header) SetAutopilot ¶
SetAutopilot sets the autopilot persona tag displayed in the header.
func (*Header) SetBackend ¶
SetBackend sets the backend tag displayed in the header.
type HelpContent ¶
type HelpContent struct{}
HelpContent implements ModalContent to display the shortcut help overlay.
func NewHelpContent ¶
func NewHelpContent() *HelpContent
NewHelpContent creates the help overlay content.
func (*HelpContent) Cancel ¶
func (h *HelpContent) Cancel()
Cancel is a no-op (help has no reply channels).
func (*HelpContent) Update ¶
func (h *HelpContent) Update(msg tea.Msg) tea.Cmd
Update handles key events — Esc or ? dismisses the help.
func (*HelpContent) View ¶
func (h *HelpContent) View() string
View renders the two-column shortcut table.
type HistoryTrail ¶
type HistoryTrail struct {
// contains filtered or unexported fields
}
HistoryTrail is a bubbletea model that renders a compact reverse-chronological trail of node visits. Consecutive visits to the same node are deduplicated with a count (e.g., "TestMilestone ×3").
func NewHistoryTrail ¶
func NewHistoryTrail(store *StateStore) *HistoryTrail
NewHistoryTrail creates a trail that reads from the given state store.
func (*HistoryTrail) SetSize ¶
func (h *HistoryTrail) SetSize(w, height int)
SetSize updates the display dimensions.
type HybridContent ¶
type HybridContent struct {
// contains filtered or unexported fields
}
HybridContent presents labeled options as a radio list with a freeform textarea for custom input. Selecting a label submits it directly. Selecting "other" focuses the textarea for custom text.
func NewHybridContent ¶
func NewHybridContent(prompt string, labels []string, defaultLabel string, replyCh chan<- string) *HybridContent
NewHybridContent creates a hybrid gate with labeled options and freeform fallback.
func (*HybridContent) Cancel ¶
func (h *HybridContent) Cancel()
Cancel implements Cancellable for external cancellation (e.g., Ctrl+C).
func (*HybridContent) SetWidth ¶
func (h *HybridContent) SetWidth(w int)
SetWidth adjusts the content width for prompt wrapping and textarea.
func (*HybridContent) Update ¶
func (h *HybridContent) Update(msg tea.Msg) tea.Cmd
Update handles navigation, selection, and textarea input.
func (*HybridContent) View ¶
func (h *HybridContent) View() string
View renders the prompt, radio options, and textarea.
type InterviewContent ¶
type InterviewContent struct {
// contains filtered or unexported fields
}
InterviewContent implements ModalContent, Cancellable, and FullscreenContent for multi-field interview forms shown in the TUI modal.
func NewInterviewContent ¶
func NewInterviewContent(questions []handlers.Question, previous *handlers.InterviewResult, replyCh chan<- string, width, height int) *InterviewContent
NewInterviewContent creates a fullscreen interview form. If previous is non-nil, fields are pre-filled from matching answers by ID.
func (*InterviewContent) Cancel ¶
func (ic *InterviewContent) Cancel()
Cancel implements Cancellable. Closes the reply channel to signal cancellation, consistent with all other ModalContent types (ChoiceContent, FreeformContent, etc.).
func (*InterviewContent) IsFullscreen ¶
func (ic *InterviewContent) IsFullscreen() bool
IsFullscreen signals the modal to use the full terminal.
func (*InterviewContent) SetSize ¶
func (ic *InterviewContent) SetSize(w, h int)
SetSize updates dimensions.
func (*InterviewContent) Update ¶
func (ic *InterviewContent) Update(msg tea.Msg) tea.Cmd
Update handles keyboard input for the interview form.
func (*InterviewContent) View ¶
func (ic *InterviewContent) View() string
View renders the interview form — one question at a time with progress summary.
type Modal ¶
type Modal struct {
// contains filtered or unexported fields
}
Modal renders a bordered overlay centered over background terminal content.
func (*Modal) CancelAndHide ¶
func (m *Modal) CancelAndHide()
CancelAndHide cancels the modal content (closing reply channels) and hides it. Used on Ctrl+C to prevent pipeline goroutine hangs.
func (*Modal) Show ¶
func (m *Modal) Show(content ModalContent)
Show displays the modal with the given content.
type ModalContent ¶
ModalContent is the interface for content rendered inside the modal overlay.
type MsgAgentError ¶
type MsgClearFocus ¶
type MsgClearFocus struct{}
type MsgCycleVerbosity ¶
type MsgCycleVerbosity struct{}
Verbosity cycling for the agent log filter.
type MsgGateAutopilot ¶
type MsgGateAutopilot struct {
NodeID string
Prompt string
Decision string
Reasoning string
Labels []string
Default string
ReplyCh chan<- string
}
MsgGateAutopilot tells the TUI to show the autopilot decision in the modal for a brief moment, then auto-close by sending the reply.
type MsgGateChoice ¶
Gate (human-in-the-loop) messages.
type MsgGateFreeform ¶
type MsgGateInterview ¶
type MsgHeaderTick ¶
type MsgHeaderTick struct{}
type MsgLLMFinish ¶
type MsgLLMFinish struct{ NodeID string }
type MsgLLMProviderRaw ¶
type MsgLLMRequestPreparing ¶
LLM provider messages.
type MsgLLMRequestStart ¶
type MsgModalDismiss ¶
type MsgModalDismiss struct{}
type MsgNodeCompleted ¶
type MsgNodeFailed ¶
type MsgNodeRetrying ¶
type MsgPipelineCompleted ¶
type MsgPipelineCompleted struct {
Status pipeline.TerminalStatus
Override *pipeline.OverrideDetail // headline entry (latest) per D5a; non-nil for override runs
}
MsgPipelineCompleted is emitted when the pipeline reaches the success exit. Status carries the terminal status so the TUI's completion row can render validation_overridden in amber (per Gap 5.2 spec D17 + D18) instead of a generic green checkmark. Override is non-nil when overrides fired during the run, carrying the headline (latest) entry per spec D5a.
When the message comes from the free-function AdaptPipelineEvent (the stateless adapter used in tests / one-shot conversions), Status defaults to the zero value and Override is nil — the StateStore reconstructs Status from accumulated MsgValidationOverridden messages it has seen during the run. The stateful Adapter populates these fields at construction time so downstream consumers don't have to re-derive them.
type MsgPipelineDone ¶
type MsgPipelineDone struct{ Err error }
type MsgPipelineFailed ¶
type MsgPipelineFailed struct{ Error string }
type MsgReasoningChunk ¶
type MsgSearchDeactivate ¶
type MsgSearchDeactivate struct{}
type MsgSearchUpdate ¶
type MsgSearchUpdate struct{ Term string }
type MsgStatusFlash ¶
type MsgStatusFlash struct{ Text string }
Status bar flash messages (e.g., "Copied!").
type MsgStatusFlashClear ¶
type MsgStatusFlashClear struct{}
type MsgTextChunk ¶
type MsgThinkingStopped ¶
type MsgThinkingStopped struct{ NodeID string }
type MsgToggleExpand ¶
type MsgToggleExpand struct{}
type MsgToolCallEnd ¶
type MsgToolCallStart ¶
type MsgValidationOverridden ¶
type MsgValidationOverridden struct {
NodeID string // the gate node that produced the override
Detail pipeline.OverrideDetail // gate/label/actor/subgraph_path
}
MsgValidationOverridden carries a single override-edge traversal so the StateStore can accumulate overrides for the completion row's gate/label/actor display and so live UI surfaces (e.g. the activity log) can flag the moment. Mapped from pipeline.EventValidationOverridden by the adapter.
type MsgVerifyStatus ¶
type NodeFlags ¶
type NodeFlags struct {
IsParallelDispatcher bool // node dispatches parallel branches
IsParallelBranch bool // node is a branch of a parallel dispatch
IsFanIn bool // node joins parallel branches
}
NodeFlags carries metadata about a node's role in the pipeline.
type NodeList ¶
type NodeList struct {
// contains filtered or unexported fields
}
NodeList renders a signal lamp panel of pipeline nodes.
func NewNodeList ¶
func NewNodeList(store *StateStore, thinking *ThinkingTracker, height int) *NodeList
NewNodeList creates a NodeList that reads from the given state store and thinking tracker.
func (*NodeList) MoveDown ¶
func (nl *NodeList) MoveDown()
MoveDown moves the selection cursor down.
func (*NodeList) SelectedNodeID ¶
SelectedNodeID returns the ID of the currently selected node, or "".
type NodePhase ¶
type NodePhase int
NodePhase represents the current activity phase of a running node.
type PipelineAdapter ¶
type PipelineAdapter struct {
// contains filtered or unexported fields
}
PipelineAdapter is a stateful event-to-message adapter that accumulates override events across a run so the MsgPipelineCompleted it emits carries the terminal Status (OutcomeSuccess vs OutcomeValidationOverridden) and the headline OverrideDetail per Gap 5.2 spec D5a (latest entry wins). Use this when the TUI needs live override status on the completion event; use the stateless AdaptPipelineEvent for one-shot conversions.
Lifetime is scoped to a single pipeline run. Construct one per run via NewPipelineAdapter — sharing one across runs would mix override state.
func NewPipelineAdapter ¶
func NewPipelineAdapter() *PipelineAdapter
NewPipelineAdapter returns a freshly-initialized PipelineAdapter ready to adapt one pipeline run's events.
func (*PipelineAdapter) Adapt ¶
func (a *PipelineAdapter) Adapt(evt pipeline.PipelineEvent) tea.Msg
Adapt is the stateful equivalent of AdaptPipelineEvent: it tracks override events as they arrive and, on EventPipelineCompleted, returns a MsgPipelineCompleted with Status and Override populated from accumulated state. Other event types route through the same mapping as the free function.
type ProgressTracker ¶
type ProgressTracker struct {
// contains filtered or unexported fields
}
ProgressTracker renders a progress bar and estimates ETA from rolling average of completed node durations.
func NewProgressTracker ¶
func NewProgressTracker(store *StateStore) *ProgressTracker
NewProgressTracker creates a progress bar styled to the TUI palette.
func (*ProgressTracker) RecordNodeDuration ¶
func (p *ProgressTracker) RecordNodeDuration(d time.Duration)
RecordNodeDuration adds a completed node's duration for ETA calculation.
func (*ProgressTracker) SetWidth ¶
func (p *ProgressTracker) SetWidth(w int)
SetWidth adjusts the progress bar width.
func (*ProgressTracker) View ¶
func (p *ProgressTracker) View() string
View renders the progress bar with fraction and ETA. Uses simple block characters: filled=━ (amber), empty=─ (dim).
type ReviewContent ¶
type ReviewContent struct {
// contains filtered or unexported fields
}
ReviewContent presents a split-pane view for reviewing long content (e.g., execution plans). Top pane is a scrollable glamour-rendered viewport, bottom pane is a textarea for the user's response.
func NewReviewContent ¶
func NewReviewContent(prompt string, replyCh chan<- string, width, height int) *ReviewContent
NewReviewContent creates a split-pane review view. The markdown content is rendered via glamour in the viewport. A temp file is written so the user can open the plan in an external editor.
func (*ReviewContent) Cancel ¶
func (r *ReviewContent) Cancel()
Cancel implements Cancellable for external cancellation (e.g., Ctrl+C).
func (*ReviewContent) IsFullscreen ¶
func (r *ReviewContent) IsFullscreen() bool
IsFullscreen signals the modal wrapper to skip centering and use the full terminal.
func (*ReviewContent) SetSize ¶
func (r *ReviewContent) SetSize(w, h int)
SetSize updates dimensions for the review panes.
func (*ReviewContent) Update ¶
func (r *ReviewContent) Update(msg tea.Msg) tea.Cmd
Update routes keys: PgUp/PgDn/arrows to viewport, typing to textarea, Ctrl+S to submit.
func (*ReviewContent) View ¶
func (r *ReviewContent) View() string
View renders the split-pane: viewport, divider, textarea, hints.
type ReviewHybridContent ¶
type ReviewHybridContent struct {
// contains filtered or unexported fields
}
ReviewHybridContent shows a glamour-rendered scrollable viewport with radio label selection and an "other" freeform option below. Used when an escalation gate has both context content (what failed) and labeled options (accept/retry/abandon), plus the ability to provide custom feedback.
func NewReviewHybridContent ¶
func NewReviewHybridContent(label, context string, labels []string, defaultLabel string, replyCh chan<- string, width, height int) *ReviewHybridContent
NewReviewHybridContent creates a split view: scrollable context on top, radio options + freeform textarea on bottom.
func (*ReviewHybridContent) Cancel ¶
func (r *ReviewHybridContent) Cancel()
Cancel implements Cancellable.
func (*ReviewHybridContent) IsFullscreen ¶
func (r *ReviewHybridContent) IsFullscreen() bool
IsFullscreen signals the modal to use the full terminal.
func (*ReviewHybridContent) SetSize ¶
func (r *ReviewHybridContent) SetSize(w, h int)
SetSize updates dimensions.
func (*ReviewHybridContent) Update ¶
func (r *ReviewHybridContent) Update(msg tea.Msg) tea.Cmd
Update handles navigation and selection.
func (*ReviewHybridContent) View ¶
func (r *ReviewHybridContent) View() string
View renders viewport + divider + radio options + other + textarea.
type ScrollView ¶
type ScrollView struct {
// contains filtered or unexported fields
}
ScrollView is a line-buffered viewport that auto-scrolls to the bottom unless the user manually scrolls up.
func NewScrollView ¶
func NewScrollView(height int) *ScrollView
NewScrollView creates a ScrollView with the given viewport height.
func (*ScrollView) Append ¶
func (sv *ScrollView) Append(line string)
Append adds a line and auto-scrolls if enabled.
func (*ScrollView) AutoScroll ¶
func (sv *ScrollView) AutoScroll() bool
AutoScroll returns whether auto-scroll is enabled.
func (*ScrollView) Lines ¶
func (sv *ScrollView) Lines() []string
Lines returns the full line buffer.
func (*ScrollView) ScrollDown ¶
func (sv *ScrollView) ScrollDown(n int)
ScrollDown moves the viewport down by n lines.
func (*ScrollView) ScrollToBottom ¶
func (sv *ScrollView) ScrollToBottom()
ScrollToBottom re-enables auto-scroll and jumps to the end.
func (*ScrollView) ScrollUp ¶
func (sv *ScrollView) ScrollUp(n int)
ScrollUp moves the viewport up by n lines and disables auto-scroll.
func (*ScrollView) SetHeight ¶
func (sv *ScrollView) SetHeight(h int)
SetHeight updates the viewport height.
func (*ScrollView) UpdateLast ¶
func (sv *ScrollView) UpdateLast(line string)
UpdateLast replaces the last line in the buffer.
func (*ScrollView) VisibleRange ¶
func (sv *ScrollView) VisibleRange() (start, end int)
VisibleRange returns the start (inclusive) and end (exclusive) indices of currently visible lines.
type SearchBar ¶
type SearchBar struct {
// contains filtered or unexported fields
}
SearchBar provides incremental search within the agent log.
func NewSearchBar ¶
func NewSearchBar() *SearchBar
NewSearchBar creates a search bar styled for the TUI.
func (*SearchBar) Activate ¶
func (s *SearchBar) Activate()
Activate shows the search bar and focuses the input.
func (*SearchBar) Confirm ¶
func (s *SearchBar) Confirm()
Confirm exits input mode but keeps the term and highlights active. The search bar is hidden but n/N navigation still works.
func (*SearchBar) CurrentMatchLine ¶
CurrentMatchLine returns the line index of the current match, or -1.
func (*SearchBar) Deactivate ¶
func (s *SearchBar) Deactivate()
Deactivate hides the search bar and clears the search.
func (*SearchBar) MatchCount ¶
MatchCount returns the number of matches.
func (*SearchBar) NextMatch ¶
func (s *SearchBar) NextMatch()
NextMatch advances to the next match index.
func (*SearchBar) PrevMatch ¶
func (s *SearchBar) PrevMatch()
PrevMatch moves to the previous match index.
func (*SearchBar) Update ¶
Update handles input events when the search bar is active. Returns a command and whether the event was consumed.
func (*SearchBar) UpdateMatches ¶
func (s *SearchBar) UpdateMatches(lines []styledLine)
UpdateMatches rebuilds the match index list from the given lines. The indices stored in matches correspond to positions in the provided slice.
func (*SearchBar) UpdateMatchesFiltered ¶
UpdateMatchesFiltered rebuilds match indices against a filtered subset of lines. filteredIndices maps positions in the filtered view back to the original line array.
type SendFunc ¶
SendFunc is a function that sends a Bubbletea message to a running program. In Mode 2, this is typically tea.Program.Send.
type StateStore ¶
type StateStore struct {
Tokens *llm.TokenTracker
// contains filtered or unexported fields
}
StateStore is the central state container for the TUI.
func NewStateStore ¶
func NewStateStore(tokens *llm.TokenTracker) *StateStore
NewStateStore creates a StateStore with an optional TokenTracker.
func (*StateStore) Apply ¶
func (s *StateStore) Apply(msg interface{})
Apply updates state based on a typed message.
func (*StateStore) GetPhase ¶
func (s *StateStore) GetPhase(id string) NodePhase
GetPhase returns the current activity phase of a node.
func (*StateStore) HeadlineOverride ¶
func (s *StateStore) HeadlineOverride() *pipeline.OverrideDetail
HeadlineOverride returns the snapshot of the headline override entry as of pipeline completion (latest accumulated entry per spec D5a), or nil when no override fired. The renderer uses this for the "Completed — validation override at <gate> ..." copy.
func (*StateStore) IsOnCurrentPath ¶
func (s *StateStore) IsOnCurrentPath(nodeID string) bool
IsOnCurrentPath returns true if the node was visited in the execution so far.
func (*StateStore) IsThinking ¶
func (s *StateStore) IsThinking(id string) bool
IsThinking returns whether a node is in the thinking state.
func (*StateStore) IsWaiting ¶
func (s *StateStore) IsWaiting(id string) bool
IsWaiting returns whether the node is waiting for provider response.
func (*StateStore) NodeCost ¶
func (s *StateStore) NodeCost(id string) float64
NodeCost returns the accumulated cost for a node (0 if unknown).
func (*StateStore) NodeDuration ¶
func (s *StateStore) NodeDuration(id string) time.Duration
NodeDuration returns the elapsed time for a completed node.
func (*StateStore) NodeError ¶
func (s *StateStore) NodeError(id string) string
NodeError returns the error message for a failed node.
func (*StateStore) NodeRetryMessage ¶
func (s *StateStore) NodeRetryMessage(id string) string
NodeRetryMessage returns the retry message for a retrying node.
func (*StateStore) NodeStatus ¶
func (s *StateStore) NodeStatus(id string) NodeState
NodeStatus returns the current state of a node.
func (*StateStore) NodeTokens ¶
func (s *StateStore) NodeTokens(id string) int
NodeTokens returns the accumulated token count for a node.
func (*StateStore) Nodes ¶
func (s *StateStore) Nodes() []NodeEntry
Nodes returns the ordered node list.
func (*StateStore) PhaseElapsed ¶
func (s *StateStore) PhaseElapsed(id string) time.Duration
PhaseElapsed returns how long the node has been in its current phase.
func (*StateStore) PipelineDone ¶
func (s *StateStore) PipelineDone() bool
PipelineDone returns whether the pipeline has completed (success or failure).
func (*StateStore) PipelineError ¶
func (s *StateStore) PipelineError() string
PipelineError returns the pipeline error message, if any.
func (*StateStore) PipelineStatus ¶
func (s *StateStore) PipelineStatus() pipeline.TerminalStatus
PipelineStatus returns the terminal status assigned when the pipeline finished. Empty string while the run is in flight or for partial states that haven't yet computed a terminal classification. The completion-row renderer keys on this to pick green vs amber vs red.
func (*StateStore) Progress ¶
func (s *StateStore) Progress() (done, total int)
Progress returns the count of completed nodes and total nodes.
func (*StateStore) SetNodes ¶
func (s *StateStore) SetNodes(entries []NodeEntry)
SetNodes sets the ordered list of pipeline nodes.
func (*StateStore) ValidationOverrides ¶
func (s *StateStore) ValidationOverrides() []pipeline.OverrideDetail
ValidationOverrides returns a defensive copy of the accumulated override list for inspection (e.g. tests). The caller may mutate the returned slice without affecting StateStore.
func (*StateStore) VisitPath ¶
func (s *StateStore) VisitPath() []string
VisitPath returns the ordered list of visited node IDs (includes repeats for loops).
type StatusBar ¶
type StatusBar struct {
// contains filtered or unexported fields
}
StatusBar renders the bottom status bar of the TUI dashboard.
func NewStatusBar ¶
func NewStatusBar(store *StateStore, agentLog *AgentLog) *StatusBar
NewStatusBar creates a StatusBar reading state from the given store.
func (*StatusBar) ClearFlash ¶
func (sb *StatusBar) ClearFlash()
ClearFlash removes the flash message.
func (*StatusBar) Progress ¶
func (sb *StatusBar) Progress() *ProgressTracker
Progress returns the progress tracker for recording node durations.
type StyleRegistry ¶
type StyleRegistry struct {
NodeName lipgloss.Style
DimText lipgloss.Style
PrimaryText lipgloss.Style
ZoneLabel lipgloss.Style
Readout lipgloss.Style
PanelBorder lipgloss.Border
Header lipgloss.Style
Muted lipgloss.Style
StatusBar lipgloss.Style
ToolName lipgloss.Style
Error lipgloss.Style
Warn lipgloss.Style
Thinking lipgloss.Style
SearchMatch lipgloss.Style
CostBadge lipgloss.Style
VerbosityBadge lipgloss.Style
}
StyleRegistry holds all shared lipgloss styles for the TUI.
type ThinkingTracker ¶
type ThinkingTracker struct {
// contains filtered or unexported fields
}
ThinkingTracker manages per-node thinking and tool execution animation state.
func NewThinkingTracker ¶
func NewThinkingTracker() *ThinkingTracker
NewThinkingTracker creates a ThinkingTracker with no active nodes.
func (*ThinkingTracker) Elapsed ¶
func (tr *ThinkingTracker) Elapsed(nodeID string) time.Duration
Elapsed returns how long a node has been thinking.
func (*ThinkingTracker) Frame ¶
func (tr *ThinkingTracker) Frame(nodeID string) string
Frame returns the current animation frame character for a thinking node. Returns empty string if the node is not thinking.
func (*ThinkingTracker) IsThinking ¶
func (tr *ThinkingTracker) IsThinking(nodeID string) bool
IsThinking returns whether a node is currently in the thinking state.
func (*ThinkingTracker) IsToolRunning ¶
func (tr *ThinkingTracker) IsToolRunning(nodeID string) bool
IsToolRunning returns whether a node is currently executing a tool.
func (*ThinkingTracker) Start ¶
func (tr *ThinkingTracker) Start(nodeID string)
Start begins thinking for a node, recording the current time.
func (*ThinkingTracker) StartAt ¶
func (tr *ThinkingTracker) StartAt(nodeID string, t time.Time)
StartAt begins thinking for a node at a specific time.
func (*ThinkingTracker) StartTool ¶
func (tr *ThinkingTracker) StartTool(nodeID, toolName string)
StartTool marks a node as executing a tool (distinct from LLM thinking). Resets the phase timestamp so the elapsed display reflects tool duration rather than accumulated thinking+tool time.
func (*ThinkingTracker) Stop ¶
func (tr *ThinkingTracker) Stop(nodeID string)
Stop ends thinking for a node.
func (*ThinkingTracker) StopTool ¶
func (tr *ThinkingTracker) StopTool(nodeID string)
StopTool clears the tool-running state for a node.
func (*ThinkingTracker) Tick ¶
func (tr *ThinkingTracker) Tick()
Tick advances the global animation counter by one frame.
func (*ThinkingTracker) ToolName ¶
func (tr *ThinkingTracker) ToolName(nodeID string) string
ToolName returns the name of the tool currently running on a node.