Documentation
¶
Overview ¶
Package ui provides the terminal user interface.
Package ui provides the terminal user interface.
Index ¶
- Constants
- Variables
- func ArrowPairLabel() string
- func ClipboardImage() (string, bool)
- func CloseLogger()
- func FgStyle(c lipgloss.Color) lipgloss.Style
- func FlushBell()
- func GetDefaultProjectColor(index int) string
- func GetThemeBorderColors() (normal, highlighted lipgloss.Color)
- func GetThemeCardColors() (bg, fg lipgloss.Color)
- func Hyperlink(target, label string) string
- func Icon(unicodeIcon, asciiIcon string) string
- func IconArrowDown() string
- func IconArrowLeft() string
- func IconArrowRight() string
- func IconArrowUp() string
- func IconBacklog() string
- func IconBlocked() string
- func IconDefault() string
- func IconDone() string
- func IconInProgress() string
- func IconPin() string
- func IconProcessing() string
- func IconShiftDown() string
- func IconShiftUp() string
- func ImageLine(path string, width int) string
- func IsGlobalDangerousMode() bool
- func ListThemes() []string
- func LoadProjectColors(database *db.DB)
- func LoadThemeFromDB(getSetting func(string) (string, error))
- func LogPath() string
- func PRDiffStats(pr *github.PRInfo) string
- func PRDiffStatsBright(pr *github.PRInfo) string
- func PRDiffStatsPlain(pr *github.PRInfo) string
- func PRStatusBadge(pr *github.PRInfo) string
- func PRStatusDescription(pr *github.PRInfo) string
- func PRStatusIcon(pr *github.PRInfo) string
- func PrettyPermission(mode string) string
- func ProjectColor(project string) lipgloss.Color
- func RenderConversationForTest(logs []*db.TaskLog, width int, focused bool) string
- func RenderConversationWithMarkdownForTest(logs []*db.TaskLog, width int, focused bool, ...) string
- func RenderInlineImage(path string, cellWidth int) string
- func RingBell()
- func SetProjectColor(project, color string)
- func SetProjectColors(colors map[string]string)
- func SetTheme(name string) error
- func SetThemeFromJSON(data string) error
- func ShortProjectName(project string) string
- func StatusColor(status string) lipgloss.Color
- func StatusIcon(status string) string
- func StatusStyle(status string) lipgloss.Style
- func StyleGeneration() uint64
- func SupportsUnicode() bool
- func ThemeToJSON(theme Theme) (string, error)
- type ActionPickerModel
- func (m *ActionPickerModel) Init() tea.Cmd
- func (m *ActionPickerModel) IsCancelled() bool
- func (m *ActionPickerModel) Selected() *PluginActionItem
- func (m *ActionPickerModel) SetSize(width, height int)
- func (m *ActionPickerModel) Update(msg tea.Msg) (*ActionPickerModel, tea.Cmd)
- func (m *ActionPickerModel) View() string
- type AppModel
- func (m *AppModel) ClearTerminalTask()
- func (m *AppModel) DebugDetailLogCount() int
- func (m *AppModel) DebugReloadDetail() tea.Cmd
- func (m *AppModel) DumpDebugStateToFile(path string) error
- func (m *AppModel) EnableReload(token string)
- func (m *AppModel) EnableTerminalTaskReport()
- func (m *AppModel) FocusTaskOnLoad(taskID int64)
- func (m *AppModel) GenerateDebugState() DebugState
- func (m *AppModel) Init() tea.Cmd
- func (m *AppModel) OpenPaletteOnLoad(query string)
- func (m *AppModel) OpenTaskOnLoad(taskID int64)
- func (m *AppModel) ReloadState() (ReloadState, bool)
- func (m *AppModel) RestoreReloadState(state ReloadState)
- func (m *AppModel) SetDebugStatePath(path string)
- func (m *AppModel) SetMouseCaptured(captured bool)
- func (m *AppModel) SetTasks(tasks []*db.Task)
- func (m *AppModel) ShowStartupNotice(text string, d time.Duration)
- func (m *AppModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)
- func (m *AppModel) View() string
- type ApprovalChoice
- type ApprovalsModel
- func (m *ApprovalsModel) Answered() (*PendingApproval, *ApprovalChoice)
- func (m *ApprovalsModel) ClearAnswer(message string)
- func (m *ApprovalsModel) Init() tea.Cmd
- func (m *ApprovalsModel) IsCancelled() bool
- func (m *ApprovalsModel) SetSize(width, height int)
- func (m *ApprovalsModel) Update(msg tea.Msg) (*ApprovalsModel, tea.Cmd)
- func (m *ApprovalsModel) View() string
- type Attachment
- type AttachmentsModel
- type CommandPaletteModel
- func (m *CommandPaletteModel) Init() tea.Cmd
- func (m *CommandPaletteModel) IsAICommandRequest() bool
- func (m *CommandPaletteModel) IsCancelled() bool
- func (m *CommandPaletteModel) Projects() []*db.Project
- func (m *CommandPaletteModel) RawInput() string
- func (m *CommandPaletteModel) SelectedAction() *PluginActionItem
- func (m *CommandPaletteModel) SelectedTask() *db.Task
- func (m *CommandPaletteModel) SetQuery(query string)
- func (m *CommandPaletteModel) SetSize(width, height int)
- func (m *CommandPaletteModel) Update(msg tea.Msg) (*CommandPaletteModel, tea.Cmd)
- func (m *CommandPaletteModel) View() string
- type ComposerContext
- type DebugColumn
- type DebugDashboard
- type DebugDetail
- type DebugFilter
- type DebugForm
- type DebugModals
- type DebugNotification
- type DebugSize
- type DebugState
- type DebugTask
- type DetailModel
- func (m *DetailModel) AcceptComposerMenu() bool
- func (m *DetailModel) AttachFile(path string)
- func (m *DetailModel) AwayFromBottom() bool
- func (m *DetailModel) BlurComposer()
- func (m *DetailModel) ClaudePaneID() string
- func (m *DetailModel) Cleanup()
- func (m *DetailModel) CleanupWithoutSaving()
- func (m *DetailModel) ClearComposerAttachments()
- func (m *DetailModel) ClearPaneHalt()
- func (m *DetailModel) ClearPaneState()
- func (m *DetailModel) CloseComposerMenu()
- func (m *DetailModel) ComposerFocused() bool
- func (m *DetailModel) ComposerHeight() int
- func (m *DetailModel) ComposerMenuOpen() bool
- func (m *DetailModel) ComposerPickerOpen() bool
- func (m *DetailModel) ComposerSelection() (string, string)
- func (m *DetailModel) FocusComposer()
- func (m *DetailModel) GetServerURL() string
- func (m *DetailModel) HandleLogsLoaded(msg logsLoadedMsg)
- func (m *DetailModel) HasRunningShellProcess() bool
- func (m *DetailModel) InFeedbackMode() bool
- func (m *DetailModel) IsFocused() bool
- func (m *DetailModel) IsShellPaneHidden() bool
- func (m *DetailModel) JumpMessage(direction int)
- func (m *DetailModel) MessageAtViewport() (*db.TaskLog, bool)
- func (m *DetailModel) MoveComposerMenu(delta int)
- func (m *DetailModel) OpenComposerPicker(kind MenuKind, items []SlashItem, current string)
- func (m *DetailModel) PaneSetupHalted() string
- func (m *DetailModel) QuoteIntoComposer(log *db.TaskLog)
- func (m *DetailModel) Refresh() tea.Cmd
- func (m *DetailModel) RefreshPanesCmd() tea.Cmd
- func (m *DetailModel) ResumeSession() tea.Cmd
- func (m *DetailModel) ScrollLines(delta int)
- func (m *DetailModel) ScrollToBottom()
- func (m *DetailModel) SessionClosed() bool
- func (m *DetailModel) SetComposerError(message string)
- func (m *DetailModel) SetComposerOverride(model, permission string)
- func (m *DetailModel) SetPRInfo(prInfo *github.PRInfo)
- func (m *DetailModel) SetPosition(position, total int)
- func (m *DetailModel) SetSize(width, height int)
- func (m *DetailModel) StartRelatedTasksLoad() tea.Cmd
- func (m *DetailModel) StartTmuxTicker() tea.Cmd
- func (m *DetailModel) TakeComposerAttachments() []Attachment
- func (m *DetailModel) TakeComposerMessage() string
- func (m *DetailModel) TakeComposerPick() (MenuKind, string, bool)
- func (m *DetailModel) Task() *db.Task
- func (m *DetailModel) ToggleHelpExpanded()
- func (m *DetailModel) ToggleShellPane() tea.Cmd
- func (m *DetailModel) Update(msg tea.Msg) (*DetailModel, tea.Cmd)
- func (m *DetailModel) UpdateComposer(msg tea.Msg) tea.Cmd
- func (m *DetailModel) UpdateTask(t *db.Task) tea.Cmd
- func (m *DetailModel) View() string
- func (m *DetailModel) WorktreeMissing() bool
- type FileBrowserModel
- func (m *FileBrowserModel) CurrentDir() string
- func (m *FileBrowserModel) Init() tea.Cmd
- func (m *FileBrowserModel) OnCancel(fn func())
- func (m *FileBrowserModel) OnSelect(fn func(path string))
- func (m *FileBrowserModel) SetSize(width, height int)
- func (m *FileBrowserModel) Update(msg tea.Msg) (*FileBrowserModel, tea.Cmd)
- func (m *FileBrowserModel) View() string
- type FilterAutocompleteModel
- func (m *FilterAutocompleteModel) HasResults() bool
- func (m *FilterAutocompleteModel) IsHostMode() bool
- func (m *FilterAutocompleteModel) MoveDown()
- func (m *FilterAutocompleteModel) MoveUp()
- func (m *FilterAutocompleteModel) Reset()
- func (m *FilterAutocompleteModel) Select() string
- func (m *FilterAutocompleteModel) SetHostQuery(query string)
- func (m *FilterAutocompleteModel) SetQuery(query string)
- func (m *FilterAutocompleteModel) View() string
- type FolderPickerModel
- type FormField
- type FormModel
- func (m *FormModel) ApplyTo(task *db.Task)
- func (m *FormModel) GetAttachment() string
- func (m *FormModel) GetAttachments() []string
- func (m *FormModel) GetDBTask() *db.Task
- func (m *FormModel) Init() tea.Cmd
- func (m *FormModel) OriginalProject() string
- func (m *FormModel) Pipeline() string
- func (m *FormModel) PlacementChoice() (target, workDir string)
- func (m *FormModel) ProjectChanged() bool
- func (m *FormModel) SetQueue(queue bool)
- func (m *FormModel) SetSize(width, height int)
- func (m *FormModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)
- func (m *FormModel) View() string
- type KanbanBoard
- func (k *KanbanBoard) ClearOriginColumn()
- func (k *KanbanBoard) ColumnCount() int
- func (k *KanbanBoard) FocusColumn(colIdx int)
- func (k *KanbanBoard) GetOpenBlockerCount(taskID int64) int
- func (k *KanbanBoard) GetPRInfo(taskID int64) *github.PRInfo
- func (k *KanbanBoard) GetTaskPosition() (int, int)
- func (k *KanbanBoard) HandleClick(x, y int) *db.Task
- func (k *KanbanBoard) HasNextTask() bool
- func (k *KanbanBoard) HasOriginColumn() bool
- func (k *KanbanBoard) HasPrevTask() bool
- func (k *KanbanBoard) HasRunningProcess(taskID int64) bool
- func (k *KanbanBoard) IsBlockedByDeps(taskID int64) bool
- func (k *KanbanBoard) IsColumnCollapsed(colIdx int) bool
- func (k *KanbanBoard) IsEmpty() bool
- func (k *KanbanBoard) IsListMode() bool
- func (k *KanbanBoard) IsMobileMode() bool
- func (k *KanbanBoard) JumpToPinned()
- func (k *KanbanBoard) JumpToUnpinned()
- func (k *KanbanBoard) ListOptions() ListOptions
- func (k *KanbanBoard) ListTasks() []*db.Task
- func (k *KanbanBoard) MoveDown()
- func (k *KanbanBoard) MoveLeft()
- func (k *KanbanBoard) MoveRight()
- func (k *KanbanBoard) MoveUp()
- func (k *KanbanBoard) NeedsInput(taskID int64) bool
- func (k *KanbanBoard) NeedsInputCount() int
- func (k *KanbanBoard) RefreshTheme()
- func (k *KanbanBoard) RunningTaskCount() int
- func (k *KanbanBoard) SelectTask(id int64) bool
- func (k *KanbanBoard) SelectedTask() *db.Task
- func (k *KanbanBoard) SetBlockedByDeps(blockedByDeps map[int64]int)
- func (k *KanbanBoard) SetHiddenDoneCount(count int)
- func (k *KanbanBoard) SetLatestActivity(activity map[int64]*db.TaskLog)
- func (k *KanbanBoard) SetListMode(on bool)
- func (k *KanbanBoard) SetListOptions(opts ListOptions)
- func (k *KanbanBoard) SetListTitle(title string)
- func (k *KanbanBoard) SetOriginColumn()
- func (k *KanbanBoard) SetPRInfo(taskID int64, info *github.PRInfo)
- func (k *KanbanBoard) SetRunningProcesses(running map[int64]bool)
- func (k *KanbanBoard) SetSize(width, height int)
- func (k *KanbanBoard) SetTasks(tasks []*db.Task)
- func (k *KanbanBoard) SetTasksNeedingInput(needsInput map[int64]bool)
- func (k *KanbanBoard) SetWorkflowGroups(groups map[int64]*pipeline.Group)
- func (k *KanbanBoard) ToggleColumnCollapse(colIdx int)
- func (k *KanbanBoard) TotalTaskCount() int
- func (k *KanbanBoard) View() string
- type KanbanColumn
- type KeyMap
- type ListGroupBy
- type ListOptions
- type ListOptionsModel
- func (m *ListOptionsModel) Init() tea.Cmd
- func (m *ListOptionsModel) IsCancelled() bool
- func (m *ListOptionsModel) IsDone() bool
- func (m *ListOptionsModel) Options() ListOptions
- func (m *ListOptionsModel) SetSize(width, height int)
- func (m *ListOptionsModel) Update(msg tea.Msg) (*ListOptionsModel, tea.Cmd)
- func (m *ListOptionsModel) View() string
- type ListSort
- type MenuKind
- type MessageAnchor
- type PendingApproval
- type PluginActionItem
- type PluginBrowserModel
- func (m *PluginBrowserModel) Done() bool
- func (m *PluginBrowserModel) Init() tea.Cmd
- func (m *PluginBrowserModel) Rows() []PluginRow
- func (m *PluginBrowserModel) Selected() (PluginRow, bool)
- func (m *PluginBrowserModel) SetQuery(q string)
- func (m *PluginBrowserModel) SetSize(width, height int)
- func (m *PluginBrowserModel) Update(msg tea.Msg) (*PluginBrowserModel, tea.Cmd)
- func (m *PluginBrowserModel) View() string
- type PluginRow
- type PluginScope
- type ReloadState
- type RepoCloneModel
- type RetryModel
- func (m *RetryModel) GetAttachment() string
- func (m *RetryModel) GetAttachments() []string
- func (m *RetryModel) GetFeedback() string
- func (m *RetryModel) Init() tea.Cmd
- func (m *RetryModel) IsDangerous() bool
- func (m *RetryModel) SetSize(width, height int)
- func (m *RetryModel) Update(msg tea.Msg) (*RetryModel, tea.Cmd)
- func (m *RetryModel) View() string
- type RoutinesModel
- type SettingsModel
- type SlashItem
- type StatusChoice
- type TaskRefAutocompleteModel
- func (m *TaskRefAutocompleteModel) Dismiss()
- func (m *TaskRefAutocompleteModel) GetCursorPos() int
- func (m *TaskRefAutocompleteModel) GetQueryLength() int
- func (m *TaskRefAutocompleteModel) GetReference() string
- func (m *TaskRefAutocompleteModel) HasResults() bool
- func (m *TaskRefAutocompleteModel) IsDismissed() bool
- func (m *TaskRefAutocompleteModel) MoveDown()
- func (m *TaskRefAutocompleteModel) MoveUp()
- func (m *TaskRefAutocompleteModel) Reset()
- func (m *TaskRefAutocompleteModel) Select()
- func (m *TaskRefAutocompleteModel) SelectedTask() *db.Task
- func (m *TaskRefAutocompleteModel) SetQuery(query string, cursorPos int)
- func (m *TaskRefAutocompleteModel) View() string
- type TerminalImageSupport
- type Theme
- type UILogger
- type View
- type ViewPickerModel
- func (m *ViewPickerModel) Applied() *db.SavedView
- func (m *ViewPickerModel) ClearRequested() bool
- func (m *ViewPickerModel) Init() tea.Cmd
- func (m *ViewPickerModel) IsCancelled() bool
- func (m *ViewPickerModel) SetSize(width, height int)
- func (m *ViewPickerModel) Update(msg tea.Msg) (*ViewPickerModel, tea.Cmd)
- func (m *ViewPickerModel) View() string
- type WelcomeModel
Constants ¶
const ( // Unicode icons IconBacklogUnicode = "◦" IconInProgressUnicode = "▶" IconBlockedUnicode = "⚠" IconDoneUnicode = "✓" IconPinUnicode = "📌" IconWarningUnicode = "⚠" IconArrowUpUnicode = "↑" IconArrowDownUnicode = "↓" IconArrowLeftUnicode = "←" IconArrowRightUnicode = "→" IconShiftUpUnicode = "⇧↑" IconShiftDownUnicode = "⇧↓" // ASCII fallbacks IconBacklogASCII = "o" IconInProgressASCII = ">" IconProcessingASCII = "~" IconBlockedASCII = "!" IconDoneASCII = "*" IconPinASCII = "P" IconWarningASCII = "!" IconArrowUpASCII = "^" IconArrowDownASCII = "v" IconArrowLeftASCII = "<" IconArrowRightASCII = ">" IconShiftUpASCII = "^^" IconShiftDownASCII = "vv" IconProcessingUnicode = "⋯" IconDefaultUnicode = "·" IconDefaultASCII = "." )
Icon constants - Unicode and ASCII versions
const BellDebounce = 500 * time.Millisecond
BellDebounce is the quiet period used to coalesce terminal bells.
Task transitions frequently arrive in bursts: a single poll cycle can pick up several status changes at once, and the executor's event channel delivers back-to-back events when a batch of tasks finishes. Ringing once per event turns that into a machine-gun of beeps. Instead each bell restarts this window and only the last event in the burst actually rings.
The window is deliberately shorter than the board's 1s poll interval, so genuinely separate events still get their own bell — only same-burst events collapse.
const MobileWidthThreshold = 80
MobileWidthThreshold is the minimum width for showing all columns. Below this, only the selected column is shown.
Variables ¶
var ( PinHook func(taskID int64, pinned bool) error ArchiveHook func(taskID int64) error DeleteHook func(taskID int64) error StatusHook func(taskID int64, status string) error )
var ( // Primary colors ColorPrimary = lipgloss.Color("#61AFEF") // Soft blue (OneDark default) ColorSecondary = lipgloss.Color("#56B6C2") // Cyan ColorSuccess = lipgloss.Color("#98C379") // Green ColorWarning = lipgloss.Color("#E5C07B") // Yellow ColorError = lipgloss.Color("#E06C75") // Red ColorDangerous = lipgloss.Color("#E06C75") // Red (same as error, for dangerous mode indicator) ColorMuted = lipgloss.Color("#5C6370") // Gray // Status colors ColorInProgress = lipgloss.Color("#D19A66") // Orange ColorDone = lipgloss.Color("#98C379") // Green ColorBlocked = lipgloss.Color("#E06C75") // Red // Default project colors palette - used when no color is set // These are distinct, visually pleasing colors for project labels DefaultProjectColors = []string{ "#C678DD", "#61AFEF", "#56B6C2", "#98C379", "#E5C07B", "#E06C75", "#D19A66", "#ABB2BF", } // Type colors (fixed, not theme-dependent) ColorCode = lipgloss.Color("#C678DD") // Purple ColorWriting = lipgloss.Color("#E5C07B") // Yellow ColorThinking = lipgloss.Color("#56B6C2") // Cyan )
Colors - these are updated by refreshStyles() when theme changes
var ( // Text styles Bold = lipgloss.NewStyle().Bold(true) Dim = lipgloss.NewStyle().Foreground(ColorMuted) Title = lipgloss.NewStyle().Bold(true).Foreground(ColorPrimary) Subtitle = lipgloss.NewStyle().Foreground(ColorSecondary) Success = lipgloss.NewStyle().Foreground(ColorSuccess) Warning = lipgloss.NewStyle().Foreground(ColorWarning) Error = lipgloss.NewStyle().Foreground(ColorError) AttachmentChip = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorMuted). Foreground(ColorMuted). Padding(0, 1) AttachmentChipSelected = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorPrimary). Foreground(ColorPrimary). Bold(true). Padding(0, 1) // Status badges StatusInProgress = lipgloss.NewStyle(). Foreground(ColorInProgress). SetString("⋯") StatusDone = lipgloss.NewStyle(). Foreground(ColorDone). SetString("✓") StatusBlocked = lipgloss.NewStyle(). Foreground(ColorBlocked). SetString("!") // Box styles Box = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorMuted). Padding(0, 1) FocusedBox = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorPrimary). Padding(0, 1) // List item styles ListItem = lipgloss.NewStyle(). PaddingLeft(2) SelectedListItem = lipgloss.NewStyle(). PaddingLeft(2). Foreground(ColorPrimary). Bold(true) // Help bar HelpBar = lipgloss.NewStyle(). Foreground(ColorMuted). Padding(1, 0) HelpKey = lipgloss.NewStyle(). Foreground(ColorSecondary). Bold(true) HelpDesc = lipgloss.NewStyle(). Foreground(ColorMuted) // Header Header = lipgloss.NewStyle(). Bold(true). Foreground(ColorPrimary). Padding(0, 1). MarginBottom(1) // Tag styles ProjectTag = lipgloss.NewStyle(). Foreground(ColorSecondary). Bold(true) TypeTag = lipgloss.NewStyle(). Foreground(ColorMuted). Italic(true) )
Base styles - these are updated by refreshStyles() when theme changes
var ( ColorPROpen = lipgloss.Color("#98C379") // Green - ready to merge ColorPRDraft = lipgloss.Color("#5C6370") // Gray - draft ColorPRMerged = lipgloss.Color("#C678DD") // Purple - merged ColorPRClosed = lipgloss.Color("#E06C75") // Red - closed ColorPRPending = lipgloss.Color("#E5C07B") // Yellow - checks running ColorPRFailing = lipgloss.Color("#E06C75") // Red - checks failing )
PR status colors
var ( ColorDiffAdd = lipgloss.Color("#98C379") // Green for additions (bright) ColorDiffDel = lipgloss.Color("#E06C75") // Red for deletions (bright) ColorDiffAddDim = lipgloss.Color("#5C7A4A") // Dim green for unselected ColorDiffDelDim = lipgloss.Color("#8B4C52") // Dim red for unselected )
Diff stat colors (git-style green/red)
var ActivityHook func(taskID int64) (bool, string)
ActivityHook reports whether the thread is mid-turn and what it is doing, so the view can say so instead of looking idle while bb works.
var AnswerApprovalHook func(taskID int64, approval PendingApproval, choice ApprovalChoice) error
AnswerApprovalHook delivers the chosen answer.
var ApprovalsHook func(taskID int64) ([]PendingApproval, error)
ApprovalsHook returns whatever the task's thread is waiting on.
var BuiltinThemes = map[string]Theme{ "onedark": OneDarkTheme, "default": DefaultTheme, "nord": NordTheme, "gruvbox": GruvboxTheme, "catppuccin": CatppuccinTheme, "dracula": DraculaTheme, }
BuiltinThemes contains all built-in themes.
var CatppuccinTheme = Theme{
Name: "catppuccin",
Primary: "#CBA6F7",
Secondary: "#89DCEB",
Muted: "#6C7086",
Success: "#A6E3A1",
Warning: "#F9E2AF",
Error: "#F38BA8",
InProgress: "#FAB387",
Done: "#A6E3A1",
Blocked: "#F38BA8",
Backlog: "#6C7086",
CardBg: "#313244",
CardFg: "#CDD6F4",
CardBorder: "#45475A",
CardBorderHi: "#CBA6F7",
ColumnBorder: "#45475A",
ColumnBorderHi: "#CBA6F7",
}
CatppuccinTheme is inspired by Catppuccin Mocha - soothing pastel colors.
var ColumnTitles = [4]string{"Backlog", "In Progress", "Blocked", "Done"}
ColumnTitles labels the four board columns. They are variables because the column a status lands in is a product decision: TaskYou tracks work it queues itself, while bb threads start on creation and come back to you for answers, so bb-tui names them after what bb actually does.
var ComposerEnabled = false
ComposerEnabled turns on the message box at the bottom of the detail view. TaskYou has no composer because a task is driven by typing into its tmux pane; a bb thread is driven by sending it a message, so the detail view needs somewhere to write one.
var ContextHook func(taskID int64) ComposerContext
ContextHook supplies those chips. Without it the composer shows only the input, which is what it did before and is still correct for TaskYou.
var ConversationView = false
ConversationView renders a thread the way the app does — as a conversation rather than TaskYou's timestamped execution log. A bb thread is a conversation; a log of tool calls buries the part a person came to read.
var CreateTaskHook func(task *db.Task) (int64, error)
Write hooks. When bb-tui owns the data, a local write would be reverted by the next sync, so each of these forwards the action to bb instead. Returning an error surfaces it to the user rather than silently falling back. CreateTaskHook, when set, replaces creating a local task. bb-tui spawns a real thread instead, then reports the task id the mirror gave it so the board can select it.
var DefaultTheme = Theme{
Name: "default",
Primary: "#7C3AED",
Secondary: "#06B6D4",
Muted: "#6B7280",
Success: "#10B981",
Warning: "#F59E0B",
Error: "#EF4444",
InProgress: "#9A9463",
Done: "#10B981",
Blocked: "#EF4444",
Backlog: "#6B7280",
CardBg: "#333333",
CardFg: "#FFFFFF",
CardBorder: "#6B7280",
CardBorderHi: "#7C3AED",
ColumnBorder: "#6B7280",
ColumnBorderHi: "#7C3AED",
}
DefaultTheme is the original purple-heavy theme.
var DisableUpstreamVersionCheck = false
DisableUpstreamVersionCheck stops the TUI polling TaskYou's GitHub releases. bb-tui ships on bb's release cadence, so an upstream "ty upgrade" banner would be pointing at the wrong product.
var DraculaTheme = Theme{
Name: "dracula",
Primary: "#BD93F9",
Secondary: "#8BE9FD",
Muted: "#6272A4",
Success: "#50FA7B",
Warning: "#F1FA8C",
Error: "#FF5555",
InProgress: "#FFB86C",
Done: "#50FA7B",
Blocked: "#FF5555",
Backlog: "#6272A4",
CardBg: "#44475A",
CardFg: "#F8F8F2",
CardBorder: "#6272A4",
CardBorderHi: "#BD93F9",
ColumnBorder: "#6272A4",
ColumnBorderHi: "#BD93F9",
}
DraculaTheme is inspired by the Dracula color scheme - vibrant purples and pinks.
var GruvboxTheme = Theme{
Name: "gruvbox",
Primary: "#83A598",
Secondary: "#B8BB26",
Muted: "#665C54",
Success: "#B8BB26",
Warning: "#FABD2F",
Error: "#FB4934",
InProgress: "#FE8019",
Done: "#B8BB26",
Blocked: "#FB4934",
Backlog: "#665C54",
CardBg: "#3C3836",
CardFg: "#EBDBB2",
CardBorder: "#504945",
CardBorderHi: "#83A598",
ColumnBorder: "#504945",
ColumnBorderHi: "#83A598",
}
GruvboxTheme is inspired by the Gruvbox color scheme - retro, earthy tones.
var MenuItemsHook func(taskID int64, kind MenuKind, query string) []SlashItem
MenuItemsHook supplies completions for a trigger. The query is passed so a large project can be filtered by the server rather than shipped whole.
var NordTheme = Theme{
Name: "nord",
Primary: "#88C0D0",
Secondary: "#81A1C1",
Muted: "#4C566A",
Success: "#A3BE8C",
Warning: "#EBCB8B",
Error: "#BF616A",
InProgress: "#D08770",
Done: "#A3BE8C",
Blocked: "#BF616A",
Backlog: "#4C566A",
CardBg: "#3B4252",
CardFg: "#ECEFF4",
CardBorder: "#4C566A",
CardBorderHi: "#88C0D0",
ColumnBorder: "#4C566A",
ColumnBorderHi: "#88C0D0",
}
NordTheme is inspired by the Nord color palette - arctic, bluish tones.
var OneDarkTheme = Theme{
Name: "onedark",
Primary: "#61AFEF",
Secondary: "#56B6C2",
Muted: "#5C6370",
Success: "#98C379",
Warning: "#E5C07B",
Error: "#E06C75",
InProgress: "#D19A66",
Done: "#98C379",
Blocked: "#E06C75",
Backlog: "#5C6370",
CardBg: "#3E4451",
CardFg: "#ABB2BF",
CardBorder: "#3E4451",
CardBorderHi: "#61AFEF",
ColumnBorder: "#3E4451",
ColumnBorderHi: "#61AFEF",
}
OneDarkTheme is inspired by Atom's One Dark theme - subtle and easy on the eyes.
var RetryTurnHook func(taskID int64) error
RetryTurnHook, when set, replaces TaskYou's retry-with-feedback. bb's retry re-submits the turn whose failure put the thread in error; it takes no message, because the message it will re-send is the one that already failed.
var SendPromptHook func(taskID int64, text string, attachments []Attachment, model, permission string) error
SendPromptHook, when set, replaces the tmux/executor delivery path used to get text in front of a running agent. bb has no pane to type into: a thread is driven over the server API, so bb-tui sets this to post the message instead.
var StatusChoices func() []StatusChoice
var StopTaskHook func(taskID int64) error
StopTaskHook, when set, replaces cancelling a run. bb stops a thread through the server rather than by signalling a local process.
Functions ¶
func ArrowPairLabel ¶
func ArrowPairLabel() string
ArrowPairLabel is arrowPairLabel for callers outside this file.
func ClipboardImage ¶
ClipboardImage writes any image on the system clipboard to a temporary file and returns its path. Terminals deliver a paste as text, so an image on the clipboard never reaches the program as keystrokes — it has to be asked for.
func FgStyle ¶
FgStyle returns a cached foreground-only style for the given colour. Use this instead of lipgloss.NewStyle().Foreground(c) in per-frame render paths.
func FlushBell ¶
func FlushBell()
FlushBell delivers a pending debounced bell right away. Call it before the process exits so a bell scheduled just before quitting still reaches the terminal.
func GetDefaultProjectColor ¶
GetDefaultProjectColor returns a default color for a project based on its index. Used when a project doesn't have a color set.
func GetThemeBorderColors ¶
GetThemeBorderColors returns border colors from the current theme.
func GetThemeCardColors ¶
GetThemeCardColors returns card-specific colors from the current theme.
func Hyperlink ¶
Hyperlink wraps text in an OSC 8 escape so a terminal that supports them makes it clickable, and one that does not simply prints the text.
func IconInProgress ¶
func IconInProgress() string
IconInProgress returns the in-progress status icon.
func IconProcessing ¶
func IconProcessing() string
IconProcessing returns the processing status icon.
func ImageLine ¶
ImageLine is what a thread shows for an image: the picture itself where the terminal can draw one, and a labelled, clickable chip everywhere else.
func IsGlobalDangerousMode ¶
func IsGlobalDangerousMode() bool
IsGlobalDangerousMode returns true if the system is running in global dangerous mode. This is set by the WORKTREE_DANGEROUS_MODE=1 environment variable.
func ListThemes ¶
func ListThemes() []string
ListThemes returns the names of all built-in themes (sorted).
func LoadProjectColors ¶
LoadProjectColors loads all project colors from the database into the cache.
func LoadThemeFromDB ¶
LoadThemeFromDB loads and applies the saved theme from the database. If no theme is saved or the theme is invalid, OneDark is used as the default.
func LogPath ¶
func LogPath() string
LogPath returns the path to the log file: ui.log beside the database, so an instance pointed at another database logs beside that one instead of into the live instance's file.
func PRDiffStats ¶
PRDiffStats returns a formatted diff stats string like "+123 -45" with dim colors. Use this for unselected items. Returns empty string if no changes or PR is nil.
func PRDiffStatsBright ¶
PRDiffStatsBright returns diff stats with bright colors (for selected items).
func PRDiffStatsPlain ¶
PRDiffStatsPlain returns diff stats without color (for selected items with inverted bg).
func PRStatusBadge ¶
PRStatusBadge returns a styled badge for a PR status.
func PRStatusDescription ¶
PRStatusDescription returns a human-readable description of PR status.
func PRStatusIcon ¶
PRStatusIcon returns just the icon character for a PR status (without styling). This is useful for creating consistently-sized badges with different color schemes.
func PrettyPermission ¶
prettyPermission uses bb's own wording for its permission modes. PrettyPermission exposes bb's own wording for a permission mode.
func ProjectColor ¶
ProjectColor returns the color for a project. It uses the cached color from the database, or falls back to a default color.
func RenderConversationForTest ¶
RenderConversationForTest exposes the renderer so its output can be inspected without standing up a terminal.
func RenderConversationWithMarkdownForTest ¶
func RenderConversationWithMarkdownForTest( logs []*db.TaskLog, width int, focused bool, markdown func(string) (string, error), ) string
RenderConversationWithMarkdownForTest renders with a supplied markdown renderer so the formatting can be inspected without a terminal.
func RenderInlineImage ¶
RenderInlineImage returns the escape sequence that draws an image, or "" when the terminal cannot draw one.
func RingBell ¶
func RingBell()
RingBell asks the terminal for an audible bell, debounced by BellDebounce so a burst of task events makes one sound rather than one per event.
func SetProjectColor ¶
func SetProjectColor(project, color string)
SetProjectColor sets the color for a single project.
func SetProjectColors ¶
SetProjectColors updates the project color cache with colors from the database. This should be called when projects are loaded.
func SetThemeFromJSON ¶
SetThemeFromJSON sets a custom theme from JSON.
func ShortProjectName ¶
ShortProjectName abbreviates a project name for a card or a list row.
A hyphenated or spaced name becomes its initials ("my-side-project" → "msp"); a single long word is truncated ("a-very-long-name" → truncated). Names within the limit are returned unchanged, so most projects read normally.
func StatusColor ¶
StatusColor returns the background color for a status.
func StatusIcon ¶
StatusIcon returns the icon for a given status.
func StatusStyle ¶
StatusStyle returns the style for a given status.
func StyleGeneration ¶
func StyleGeneration() uint64
StyleGeneration returns the current style generation counter. Callers that cache rendered output should include this in their cache key.
func SupportsUnicode ¶
func SupportsUnicode() bool
SupportsUnicode returns true if the terminal likely supports Unicode characters. It checks LANG, LC_ALL, and LC_CTYPE environment variables for UTF-8 indicators.
func ThemeToJSON ¶
ThemeToJSON serializes a theme to JSON.
Types ¶
type ActionPickerModel ¶
type ActionPickerModel struct {
// contains filtered or unexported fields
}
ActionPickerModel is a modal list of plugin actions for the current task. It mirrors CommandPaletteModel: a self-contained sub-model with its own View, switched to via a dedicated View constant, so it never touches the huh form-router path.
func NewActionPickerModel ¶
func NewActionPickerModel(taskTitle string, items []PluginActionItem, width, height int) *ActionPickerModel
NewActionPickerModel creates an action picker for the given items.
func (*ActionPickerModel) Init ¶
func (m *ActionPickerModel) Init() tea.Cmd
Init implements tea.Model.
func (*ActionPickerModel) IsCancelled ¶
func (m *ActionPickerModel) IsCancelled() bool
IsCancelled reports whether the user dismissed the picker.
func (*ActionPickerModel) Selected ¶
func (m *ActionPickerModel) Selected() *PluginActionItem
Selected returns the chosen item, or nil if none was chosen yet.
func (*ActionPickerModel) SetSize ¶
func (m *ActionPickerModel) SetSize(width, height int)
SetSize updates dimensions.
func (*ActionPickerModel) Update ¶
func (m *ActionPickerModel) Update(msg tea.Msg) (*ActionPickerModel, tea.Cmd)
Update handles key input. It returns the model and never a command; the parent reads Selected()/IsCancelled() after each update.
type AppModel ¶
type AppModel struct {
// contains filtered or unexported fields
}
AppModel is the main application model.
func NewAppModel ¶
func NewAppModel(database *db.DB, exec *executor.Executor, workingDir string, version ...string) *AppModel
NewAppModel creates a new application model.
func (*AppModel) ClearTerminalTask ¶
func (m *AppModel) ClearTerminalTask()
ClearTerminalTask blanks the published variables so a tab title does not keep naming a task after ty exits.
func (*AppModel) DebugDetailLogCount ¶
DebugDetailLogCount reports how much of the conversation has arrived.
func (*AppModel) DebugReloadDetail ¶
DebugReloadDetail re-reads the open thread. The harness needs it because a conversation is fetched in the background: the first paint after opening a thread legitimately has no logs, so a check that scrolls would be scrolling nothing.
func (*AppModel) DumpDebugStateToFile ¶
DumpDebugStateToFile writes the current debug state to a file.
func (*AppModel) EnableReload ¶
EnableReload is called only by the local process that can re-exec itself.
func (*AppModel) EnableTerminalTaskReport ¶
func (m *AppModel) EnableTerminalTaskReport()
EnableTerminalTaskReport makes the TUI publish its focused task as iTerm2 user variables. Local TUI only: over SSH, /dev/tty is the server's terminal, not the viewer's.
func (*AppModel) FocusTaskOnLoad ¶
FocusTaskOnLoad selects taskID once the board finishes loading.
Used by `ty --task <id>` so a session can be reopened where it left off, rather than on whatever the board happens to sort first.
func (*AppModel) GenerateDebugState ¶
func (m *AppModel) GenerateDebugState() DebugState
GenerateDebugState creates a snapshot of the current application state.
func (*AppModel) OpenPaletteOnLoad ¶
OpenPaletteOnLoad opens the go-to-task palette with query typed in once the board has loaded (`ty open <search>`), leaving the pick to the user.
func (*AppModel) OpenTaskOnLoad ¶
OpenTaskOnLoad opens a task's detail view once the board has loaded (`ty open`). It seeds the restore path a reloaded TUI uses. When this process is itself a reload, the RestoreReloadState that follows replaces it, so a restart returns to where the user was, not to the task on the original command line.
func (*AppModel) ReloadState ¶
func (m *AppModel) ReloadState() (ReloadState, bool)
func (*AppModel) RestoreReloadState ¶
func (m *AppModel) RestoreReloadState(state ReloadState)
func (*AppModel) SetDebugStatePath ¶
SetDebugStatePath sets the path for dumping debug state.
func (*AppModel) SetMouseCaptured ¶
SetMouseCaptured records whether the program started with mouse reporting on, so the toggle knows which way to go first.
func (*AppModel) ShowStartupNotice ¶
ShowStartupNotice seeds the board's notification banner before the program starts, for something the user needs to see that happened before the TUI did — chiefly a daemon running a different build (see internal/handshake). stderr is not an option there: the alt screen wipes it on the first frame.
type ApprovalChoice ¶
type ApprovalChoice struct {
Label string
Description string
// Decision is set for tool approvals: allow_once, allow_for_session, deny.
Decision string
// QuestionID and OptionValue are set for user questions.
QuestionID string
OptionValue string
}
ApprovalChoice is one answer the user can give to a pending interaction.
type ApprovalsModel ¶
type ApprovalsModel struct {
// contains filtered or unexported fields
}
ApprovalsModel is a modal that answers whatever a thread is blocked on. It follows ActionPickerModel: a self-contained sub-model with its own View, reached through a dedicated View constant so it never touches the huh router.
func NewApprovalsModel ¶
func NewApprovalsModel(taskTitle string, approvals []PendingApproval, width, height int) *ApprovalsModel
func (*ApprovalsModel) Answered ¶
func (m *ApprovalsModel) Answered() (*PendingApproval, *ApprovalChoice)
func (*ApprovalsModel) ClearAnswer ¶
func (m *ApprovalsModel) ClearAnswer(message string)
func (*ApprovalsModel) Init ¶
func (m *ApprovalsModel) Init() tea.Cmd
func (*ApprovalsModel) IsCancelled ¶
func (m *ApprovalsModel) IsCancelled() bool
func (*ApprovalsModel) SetSize ¶
func (m *ApprovalsModel) SetSize(width, height int)
func (*ApprovalsModel) Update ¶
func (m *ApprovalsModel) Update(msg tea.Msg) (*ApprovalsModel, tea.Cmd)
func (*ApprovalsModel) View ¶
func (m *ApprovalsModel) View() string
type Attachment ¶
Attachment is a file the composer will send alongside the message.
func ExtractAttachments ¶
func ExtractAttachments(text string, startIndex int) (string, []Attachment)
ExtractAttachments pulls dropped file paths out of a message and returns the text with each one replaced by a short placeholder. A path is only taken when it names a file that exists, so prose containing a slash is left alone.
type AttachmentsModel ¶
type AttachmentsModel struct {
// contains filtered or unexported fields
}
AttachmentsModel displays and manages task attachments.
func NewAttachmentsModel ¶
NewAttachmentsModel creates a new attachments view.
func (*AttachmentsModel) AddFile ¶
func (m *AttachmentsModel) AddFile(path string) tea.Cmd
AddFile adds a file to the task's attachments.
func (*AttachmentsModel) Init ¶
func (m *AttachmentsModel) Init() tea.Cmd
Init initializes the model.
func (*AttachmentsModel) Update ¶
func (m *AttachmentsModel) Update(msg tea.Msg) (*AttachmentsModel, tea.Cmd)
Update handles messages.
func (*AttachmentsModel) View ¶
func (m *AttachmentsModel) View() string
View renders the attachments view.
type CommandPaletteModel ¶
type CommandPaletteModel struct {
// contains filtered or unexported fields
}
CommandPaletteModel represents the Command+P task switcher and AI command input.
func NewCommandPaletteModel ¶
func NewCommandPaletteModel(database *db.DB, tasks []*db.Task, width, height int) *CommandPaletteModel
NewCommandPaletteModel creates a new command palette model.
func (*CommandPaletteModel) Init ¶
func (m *CommandPaletteModel) Init() tea.Cmd
Init initializes the command palette.
func (*CommandPaletteModel) IsAICommandRequest ¶
func (m *CommandPaletteModel) IsAICommandRequest() bool
IsAICommandRequest returns true if the user pressed Enter with text that should go to AI.
func (*CommandPaletteModel) IsCancelled ¶
func (m *CommandPaletteModel) IsCancelled() bool
IsCancelled returns true if the user cancelled the palette.
func (*CommandPaletteModel) Projects ¶
func (m *CommandPaletteModel) Projects() []*db.Project
Projects returns the available projects for AI context.
func (*CommandPaletteModel) RawInput ¶
func (m *CommandPaletteModel) RawInput() string
RawInput returns the raw input text for AI command processing.
func (*CommandPaletteModel) SelectedAction ¶
func (m *CommandPaletteModel) SelectedAction() *PluginActionItem
SelectedAction returns the chosen plugin action (action mode), or nil.
func (*CommandPaletteModel) SelectedTask ¶
func (m *CommandPaletteModel) SelectedTask() *db.Task
SelectedTask returns the selected task, or nil if cancelled.
func (*CommandPaletteModel) SetQuery ¶
func (m *CommandPaletteModel) SetQuery(query string)
SetQuery types query into the search box, as if the user had.
func (*CommandPaletteModel) SetSize ¶
func (m *CommandPaletteModel) SetSize(width, height int)
SetSize updates the command palette dimensions.
func (*CommandPaletteModel) Update ¶
func (m *CommandPaletteModel) Update(msg tea.Msg) (*CommandPaletteModel, tea.Cmd)
Update handles messages.
func (*CommandPaletteModel) View ¶
func (m *CommandPaletteModel) View() string
View renders the command palette.
type ComposerContext ¶
type ComposerContext struct {
Project string
Environment string
Provider string
Model string
Reasoning string
Permission string
}
ComposerContext mirrors the row of chips the app shows around its composer.
type DebugColumn ¶
type DebugDashboard ¶
type DebugDashboard struct {
Columns []DebugColumn `json:"columns"`
FocusedColumn int `json:"focused_column"`
SelectedTask int64 `json:"selected_task_id,omitempty"`
}
type DebugDetail ¶
type DebugDetail struct {
TaskID int64 `json:"task_id"`
Title string `json:"title"`
Status string `json:"status"`
Project string `json:"project"`
Focused bool `json:"is_focused"`
Logs int `json:"log_count"`
HasPanes bool `json:"has_panes"`
// Scroll state, so thread navigation can be checked without a terminal.
ScrollOffset int `json:"scroll_offset"`
ScrollTotal int `json:"scroll_total"`
AtBottom bool `json:"at_bottom"`
StickyBottom bool `json:"sticky_bottom"`
MessageAnchors int `json:"message_anchors"`
ComposerFocus bool `json:"composer_focused"`
// PaneError is the user-visible pane message (empty when panes are healthy).
PaneError string `json:"pane_error,omitempty"`
}
type DebugFilter ¶
type DebugModals ¶
type DebugModals struct {
ActiveConfirm string `json:"active_confirm,omitempty"` // "delete", "close", "archive", "quit", etc.
}
type DebugNotification ¶
type DebugState ¶
type DebugState struct {
View string `json:"view"`
Size DebugSize `json:"size"`
Dashboard *DebugDashboard `json:"dashboard,omitempty"`
Detail *DebugDetail `json:"detail,omitempty"`
Form *DebugForm `json:"form,omitempty"`
Modals *DebugModals `json:"modals,omitempty"`
Filter *DebugFilter `json:"filter,omitempty"`
Notification *DebugNotification `json:"notification,omitempty"`
Timestamp time.Time `json:"timestamp"`
}
DebugState represents the application state as a "Text DOM" for AI agents.
type DetailModel ¶
type DetailModel struct {
// contains filtered or unexported fields
}
DetailModel represents the task detail view.
func NewDetailModel ¶
func NewDetailModel(t *db.Task, database *db.DB, exec *executor.Executor, width, height int, focusExecutor bool) (*DetailModel, tea.Cmd)
NewDetailModel creates a new detail model. Returns the model and an optional command for async pane setup. If focusExecutor is true, the executor pane will be focused after panes are joined.
func (*DetailModel) AcceptComposerMenu ¶
func (m *DetailModel) AcceptComposerMenu() bool
AcceptComposerMenu inserts the highlighted completion.
func (*DetailModel) AttachFile ¶
func (m *DetailModel) AttachFile(path string)
AttachFile stages a file that did not come from the message text, such as an image pasted from the clipboard.
func (*DetailModel) AwayFromBottom ¶
func (m *DetailModel) AwayFromBottom() bool
AwayFromBottom reports whether the reader has scrolled back, which is when the jump-to-latest affordance is worth showing.
func (*DetailModel) BlurComposer ¶
func (m *DetailModel) BlurComposer()
BlurComposer returns to single-key browsing.
func (*DetailModel) ClaudePaneID ¶
func (m *DetailModel) ClaudePaneID() string
ClaudePaneID returns the tmux pane ID where Claude is running.
func (*DetailModel) Cleanup ¶
func (m *DetailModel) Cleanup()
Cleanup should be called when leaving detail view. It saves the layout as the new default and closes the view; the task's panes stay in the daemon.
func (*DetailModel) CleanupWithoutSaving ¶
func (m *DetailModel) CleanupWithoutSaving()
CleanupWithoutSaving cleans up panes without saving the height. Use this during task transitions (prev/next) to avoid rounding errors that accumulate with each transition and cause the pane to shrink.
func (*DetailModel) ClearComposerAttachments ¶
func (m *DetailModel) ClearComposerAttachments()
ClearComposerAttachments drops staged files without sending them.
func (*DetailModel) ClearPaneHalt ¶
func (m *DetailModel) ClearPaneHalt()
ClearPaneHalt lifts a halt after the user has fixed what caused it, and lets the task spawn again. Only user recovery calls this: nothing in the automatic paths may clear a halt, or the loop comes back.
func (*DetailModel) ClearPaneState ¶
func (m *DetailModel) ClearPaneState()
ClearPaneState clears the cached pane state without breaking panes. Use this when the tmux window has been recreated externally (e.g., dangerous mode toggle).
func (*DetailModel) CloseComposerMenu ¶
func (m *DetailModel) CloseComposerMenu()
CloseComposerMenu dismisses the completions without inserting one.
func (*DetailModel) ComposerFocused ¶
func (m *DetailModel) ComposerFocused() bool
ComposerFocused reports whether keystrokes belong to the message box rather than to the board's single-key actions.
func (*DetailModel) ComposerHeight ¶
func (m *DetailModel) ComposerHeight() int
ComposerHeight is what the composer currently occupies, so the view above it can be sized and a click can be attributed to it.
func (*DetailModel) ComposerMenuOpen ¶
func (m *DetailModel) ComposerMenuOpen() bool
ComposerMenuOpen reports whether the slash menu is showing, which changes what enter and the arrow keys mean.
func (*DetailModel) ComposerPickerOpen ¶
func (m *DetailModel) ComposerPickerOpen() bool
ComposerPickerOpen reports whether the open menu changes the composer rather than the message.
func (*DetailModel) ComposerSelection ¶
func (m *DetailModel) ComposerSelection() (string, string)
ComposerSelection reports the overrides chosen for the next message.
func (*DetailModel) FocusComposer ¶
func (m *DetailModel) FocusComposer()
FocusComposer puts the cursor in the message box.
func (*DetailModel) GetServerURL ¶
func (m *DetailModel) GetServerURL() string
GetServerURL returns the server URL if a server is listening on the task's port.
func (*DetailModel) HandleLogsLoaded ¶
func (m *DetailModel) HandleLogsLoaded(msg logsLoadedMsg)
HandleLogsLoaded processes the result of async log loading.
func (*DetailModel) HasRunningShellProcess ¶
func (m *DetailModel) HasRunningShellProcess() bool
HasRunningShellProcess returns true if the shell pane has a running process. This shells out to tmux, so it must NOT be called from the per-frame render path. Refresh() polls it on a throttle and caches the result in hasRunningShellProc, which renderHeader() reads instead.
func (*DetailModel) InFeedbackMode ¶
func (m *DetailModel) InFeedbackMode() bool
InFeedbackMode returns false - use tmux pane for interaction.
func (*DetailModel) IsFocused ¶
func (m *DetailModel) IsFocused() bool
IsFocused returns true if the detail pane is the active tmux pane.
func (*DetailModel) IsShellPaneHidden ¶
func (m *DetailModel) IsShellPaneHidden() bool
IsShellPaneHidden returns true if the shell pane is currently hidden.
func (*DetailModel) JumpMessage ¶
func (m *DetailModel) JumpMessage(direction int)
JumpMessage moves the viewport to the previous or next message boundary, which is what a reader means by "back one" in a conversation — a line at a time is the wrong unit when a single reply can be forty lines.
func (*DetailModel) MessageAtViewport ¶
func (m *DetailModel) MessageAtViewport() (*db.TaskLog, bool)
MessageAtViewport is the message the reader is looking at: the last one that starts at or above the top of the pane.
func (*DetailModel) MoveComposerMenu ¶
func (m *DetailModel) MoveComposerMenu(delta int)
MoveComposerMenu changes the highlighted completion.
func (*DetailModel) OpenComposerPicker ¶
func (m *DetailModel) OpenComposerPicker(kind MenuKind, items []SlashItem, current string)
OpenComposerPicker shows the model or permission chooser.
func (*DetailModel) PaneSetupHalted ¶
func (m *DetailModel) PaneSetupHalted() string
PaneSetupHalted returns the reason pane setup stopped and is not retrying, or "" when the view is operating normally.
func (*DetailModel) QuoteIntoComposer ¶
func (m *DetailModel) QuoteIntoComposer(log *db.TaskLog)
QuoteIntoComposer puts a message into the message box as a quote, which is what the app's "add to chat" does.
func (*DetailModel) Refresh ¶
func (m *DetailModel) Refresh() tea.Cmd
Refresh reloads task and logs from database. Returns a tea.Cmd if async work (like log loading) needs to happen.
func (*DetailModel) RefreshPanesCmd ¶
func (m *DetailModel) RefreshPanesCmd() tea.Cmd
RefreshPanesCmd returns a command to refresh the tmux panes. Use this after ClearPaneState() to rejoin panes to a recreated window.
func (*DetailModel) ResumeSession ¶
func (m *DetailModel) ResumeSession() tea.Cmd
ResumeSession relaunches the closed session (with --resume) and joins its panes, as reopening the task would. The caller restarts the idle clock so the sweep does not suspend it again a minute later.
func (*DetailModel) ScrollLines ¶
func (m *DetailModel) ScrollLines(delta int)
ScrollLines moves the conversation by lines and updates whether it is still following new messages.
func (*DetailModel) ScrollToBottom ¶
func (m *DetailModel) ScrollToBottom()
ScrollToBottom jumps to the newest message and re-arms following it.
func (*DetailModel) SessionClosed ¶
func (m *DetailModel) SessionClosed() bool
SessionClosed reports whether a blocked task's session closed while this view was open — usually the idle sweep — which is when enter resumes it.
func (*DetailModel) SetComposerError ¶
func (m *DetailModel) SetComposerError(message string)
SetComposerError shows why a send failed, in place of the hint.
func (*DetailModel) SetComposerOverride ¶
func (m *DetailModel) SetComposerOverride(model, permission string)
SetComposerOverride records what the next message will use, and shows it on the chip so the choice is visible before it is sent.
func (*DetailModel) SetPRInfo ¶
func (m *DetailModel) SetPRInfo(prInfo *github.PRInfo)
SetPRInfo sets the PR info for this task. Every task reload hands the stored state back in, so an unchanged value is a no-op rather than a re-render.
func (*DetailModel) SetPosition ¶
func (m *DetailModel) SetPosition(position, total int)
SetPosition updates the task's position in its column.
func (*DetailModel) SetSize ¶
func (m *DetailModel) SetSize(width, height int)
SetSize updates the viewport size.
func (*DetailModel) StartRelatedTasksLoad ¶
func (m *DetailModel) StartRelatedTasksLoad() tea.Cmd
StartRelatedTasksLoad starts loading related tasks from QMD if available. Returns a tea.Cmd that can be batched with other commands.
func (*DetailModel) StartTmuxTicker ¶
func (m *DetailModel) StartTmuxTicker() tea.Cmd
StartTmuxTicker is no longer needed - real tmux pane handles display.
func (*DetailModel) TakeComposerAttachments ¶
func (m *DetailModel) TakeComposerAttachments() []Attachment
TakeComposerAttachments returns and clears the files staged for sending.
func (*DetailModel) TakeComposerMessage ¶
func (m *DetailModel) TakeComposerMessage() string
TakeComposerMessage returns and clears whatever has been typed.
func (*DetailModel) TakeComposerPick ¶
func (m *DetailModel) TakeComposerPick() (MenuKind, string, bool)
TakeComposerPick returns what the picker chose.
func (*DetailModel) ToggleHelpExpanded ¶
func (m *DetailModel) ToggleHelpExpanded()
ToggleHelpExpanded flips the footer help row between the collapsed (primary actions + '?') and expanded (all actions) states.
func (*DetailModel) ToggleShellPane ¶
func (m *DetailModel) ToggleShellPane() tea.Cmd
ToggleShellPane toggles the visibility of the shell pane. When hidden, the shell pane is moved to the daemon window and Claude expands to full width. When shown, the shell pane is rejoined from the daemon or a new one is created.
func (*DetailModel) Update ¶
func (m *DetailModel) Update(msg tea.Msg) (*DetailModel, tea.Cmd)
Update handles messages.
func (*DetailModel) UpdateComposer ¶
func (m *DetailModel) UpdateComposer(msg tea.Msg) tea.Cmd
UpdateComposer feeds a keystroke to the message box.
func (*DetailModel) UpdateTask ¶
func (m *DetailModel) UpdateTask(t *db.Task) tea.Cmd
UpdateTask updates the task and refreshes the view. Returns a tea.Cmd if the executor changed and a switch is needed.
func (*DetailModel) View ¶
func (m *DetailModel) View() string
View renders the detail view.
View runs on every Bubble Tea update while the detail view is open. The header and help are cheap (~12µs) and have many volatile inputs (spinner frame, PR info, badges, theme colours), so we render them every frame and fold their output into the cache signature — that captures every input they read without having to enumerate each one. The expensive parts — viewport.View() and the bordered box.Render(), together ~2ms and ~2.7MB per call — are skipped entirely when the signature is unchanged.
func (*DetailModel) WorktreeMissing ¶
func (m *DetailModel) WorktreeMissing() bool
WorktreeMissing reports whether this view is halted because the task's recorded worktree is gone — the one halt with a one-key recovery.
type FileBrowserModel ¶
type FileBrowserModel struct {
// contains filtered or unexported fields
}
FileBrowserModel is a directory browser for selecting paths.
func NewFileBrowserModel ¶
func NewFileBrowserModel(startPath string, width, height int) *FileBrowserModel
NewFileBrowserModel creates a new file browser starting at the given path.
func (*FileBrowserModel) CurrentDir ¶
func (m *FileBrowserModel) CurrentDir() string
CurrentDir returns the currently displayed directory.
func (*FileBrowserModel) Init ¶
func (m *FileBrowserModel) Init() tea.Cmd
Init initializes the model.
func (*FileBrowserModel) OnCancel ¶
func (m *FileBrowserModel) OnCancel(fn func())
OnCancel sets the callback for when browsing is cancelled.
func (*FileBrowserModel) OnSelect ¶
func (m *FileBrowserModel) OnSelect(fn func(path string))
OnSelect sets the callback for when a path is selected.
func (*FileBrowserModel) SetSize ¶
func (m *FileBrowserModel) SetSize(width, height int)
SetSize updates the browser size.
func (*FileBrowserModel) Update ¶
func (m *FileBrowserModel) Update(msg tea.Msg) (*FileBrowserModel, tea.Cmd)
Update handles messages.
func (*FileBrowserModel) View ¶
func (m *FileBrowserModel) View() string
View renders the file browser.
type FilterAutocompleteModel ¶
type FilterAutocompleteModel struct {
// contains filtered or unexported fields
}
FilterAutocompleteModel provides project name and host name autocomplete for the filter.
func NewFilterAutocompleteModel ¶
func NewFilterAutocompleteModel(database *db.DB) *FilterAutocompleteModel
NewFilterAutocompleteModel creates a new filter autocomplete model.
func (*FilterAutocompleteModel) HasResults ¶
func (m *FilterAutocompleteModel) HasResults() bool
func (*FilterAutocompleteModel) IsHostMode ¶
func (m *FilterAutocompleteModel) IsHostMode() bool
IsHostMode reports whether the dropdown is completing a host rather than a project, so the caller knows which chip syntax to write back.
func (*FilterAutocompleteModel) MoveDown ¶
func (m *FilterAutocompleteModel) MoveDown()
func (*FilterAutocompleteModel) MoveUp ¶
func (m *FilterAutocompleteModel) MoveUp()
func (*FilterAutocompleteModel) Reset ¶
func (m *FilterAutocompleteModel) Reset()
func (*FilterAutocompleteModel) Select ¶
func (m *FilterAutocompleteModel) Select() string
Select returns the selected project or host name.
func (*FilterAutocompleteModel) SetHostQuery ¶
func (m *FilterAutocompleteModel) SetHostQuery(query string)
SetHostQuery filters host names for the "@host" chip and resets selection.
The fleet has no central registry — a placement hook names whatever machines it likes — so the candidates are the hosts tasks have actually run on, plus "local" for the ones that ran here.
func (*FilterAutocompleteModel) SetQuery ¶
func (m *FilterAutocompleteModel) SetQuery(query string)
SetQuery filters projects based on query and resets selection.
func (*FilterAutocompleteModel) View ¶
func (m *FilterAutocompleteModel) View() string
View renders the dropdown.
type FolderPickerModel ¶
type FolderPickerModel struct {
// contains filtered or unexported fields
}
FolderPickerModel is a fuzzy, type-to-search folder picker built on bubbles/list. It seeds the list with likely project roots; typing drives the list's built-in fuzzy filter (matched runes are underlined), → descends into the selected folder, and enter picks it.
func NewFolderPickerModel ¶
func NewFolderPickerModel(width, height int) *FolderPickerModel
NewFolderPickerModel seeds the picker from common project roots.
func (*FolderPickerModel) Init ¶
func (m *FolderPickerModel) Init() tea.Cmd
func (*FolderPickerModel) SetSize ¶
func (m *FolderPickerModel) SetSize(w, h int)
func (*FolderPickerModel) Update ¶
func (m *FolderPickerModel) Update(msg tea.Msg) (*FolderPickerModel, tea.Cmd)
func (*FolderPickerModel) View ¶
func (m *FolderPickerModel) View() string
type FormField ¶
type FormField int
FormField represents the currently focused field.
const ( FieldProject FormField = iota // Project first for immediate context FieldTitle FieldBody FieldAttachments // Moved after body for proximity - drag works from any field FieldType FieldExecutor FieldHost // Only shown when a placement plugin offers a choice of machines FieldEffort FieldModel FieldPermission FieldCount )
type FormModel ¶
type FormModel struct {
// contains filtered or unexported fields
}
FormModel represents the new task form.
func NewEditFormModel ¶
func NewEditFormModel(database *db.DB, task *db.Task, width, height int, availableExecutors []string) *FormModel
NewEditFormModel creates a form model pre-populated with an existing task's data for editing.
func NewFormModel ¶
func NewFormModel(database *db.DB, width, height int, workingDir string, availableExecutors []string) *FormModel
NewFormModel creates a new form model.
func (*FormModel) ApplyTo ¶
ApplyTo overlays the form's editable fields onto task, leaving every other persisted column untouched. Editing a task in place uses this so that saving never resets fields the form doesn't expose (session IDs, pin state, tags, source branch, PR info, ...). See issue #560. Permission mode IS exposed by the form, so it is applied here (and kept in sync with DangerousMode).
func (*FormModel) GetAttachment ¶
GetAttachment returns the first attachment for backwards compatibility.
func (*FormModel) GetAttachments ¶
GetAttachments returns the parsed attachment file paths.
func (*FormModel) OriginalProject ¶
OriginalProject returns the original project when editing.
func (*FormModel) Pipeline ¶
Pipeline returns the selected kind's name when it is a workflow (has steps), or "" when the selected kind is a plain type — i.e. an ordinary single task. When non-empty, the caller builds a workflow (see internal/pipeline) using the form's task as the goal carrier rather than creating a single task.
func (*FormModel) PlacementChoice ¶
PlacementChoice returns the host the user picked for a new task: the target to record ("" = leave it to the resolver, "local" = this machine) and that project's directory on it.
func (*FormModel) ProjectChanged ¶
ProjectChanged returns true if the project was changed during editing. Only relevant when isEdit is true.
type KanbanBoard ¶
type KanbanBoard struct {
// contains filtered or unexported fields
}
KanbanBoard manages the kanban board state.
func NewKanbanBoard ¶
func NewKanbanBoard(width, height int) *KanbanBoard
NewKanbanBoard creates a new kanban board.
func (*KanbanBoard) ClearOriginColumn ¶
func (k *KanbanBoard) ClearOriginColumn()
ClearOriginColumn clears the origin column, returning to normal navigation.
func (*KanbanBoard) ColumnCount ¶
func (k *KanbanBoard) ColumnCount() int
ColumnCount returns the number of columns.
func (*KanbanBoard) FocusColumn ¶
func (k *KanbanBoard) FocusColumn(colIdx int)
FocusColumn moves selection to a specific column by index. If the column is collapsed, it will be uncollapsed first.
func (*KanbanBoard) GetOpenBlockerCount ¶
func (k *KanbanBoard) GetOpenBlockerCount(taskID int64) int
GetOpenBlockerCount returns the number of open blockers for a task. Returns 0 if the task has no blockers.
func (*KanbanBoard) GetPRInfo ¶
func (k *KanbanBoard) GetPRInfo(taskID int64) *github.PRInfo
GetPRInfo returns the PR info for a task.
func (*KanbanBoard) GetTaskPosition ¶
func (k *KanbanBoard) GetTaskPosition() (int, int)
GetTaskPosition returns the position of the currently selected task in its column. Returns (position, total) where position is 1-indexed, or (0, 0) if no task is selected.
func (*KanbanBoard) HandleClick ¶
func (k *KanbanBoard) HandleClick(x, y int) *db.Task
HandleClick handles a mouse click at the given coordinates. Returns the clicked task if a task card was clicked, nil otherwise. Also updates the selection to the clicked task.
func (*KanbanBoard) HasNextTask ¶
func (k *KanbanBoard) HasNextTask() bool
HasNextTask returns true if there is a next task in the current column. Returns false if already at the last task or no tasks exist.
func (*KanbanBoard) HasOriginColumn ¶
func (k *KanbanBoard) HasOriginColumn() bool
HasOriginColumn returns true if an origin column is set.
func (*KanbanBoard) HasPrevTask ¶
func (k *KanbanBoard) HasPrevTask() bool
HasPrevTask returns true if there is a previous task in the current column. Returns false if already at the first task or no tasks exist.
func (*KanbanBoard) HasRunningProcess ¶
func (k *KanbanBoard) HasRunningProcess(taskID int64) bool
HasRunningProcess returns true if the task has a running shell process.
func (*KanbanBoard) IsBlockedByDeps ¶
func (k *KanbanBoard) IsBlockedByDeps(taskID int64) bool
IsBlockedByDeps returns true if the task is blocked by dependencies.
func (*KanbanBoard) IsColumnCollapsed ¶
func (k *KanbanBoard) IsColumnCollapsed(colIdx int) bool
IsColumnCollapsed returns true if the column at the given index is collapsed.
func (*KanbanBoard) IsEmpty ¶
func (k *KanbanBoard) IsEmpty() bool
IsEmpty returns true if all columns have no tasks.
func (*KanbanBoard) IsListMode ¶
func (k *KanbanBoard) IsListMode() bool
IsListMode reports whether the board renders as a flat list.
func (*KanbanBoard) IsMobileMode ¶
func (k *KanbanBoard) IsMobileMode() bool
IsMobileMode returns true if the board should show single-column mode.
func (*KanbanBoard) JumpToPinned ¶
func (k *KanbanBoard) JumpToPinned()
JumpToPinned moves selection to the first pinned task in the current column. If there are no pinned tasks, moves to the top of the column.
func (*KanbanBoard) JumpToUnpinned ¶
func (k *KanbanBoard) JumpToUnpinned()
JumpToUnpinned moves selection to the first unpinned task in the current column. If all tasks are pinned or there are no tasks, stays at current position.
func (*KanbanBoard) ListOptions ¶
func (k *KanbanBoard) ListOptions() ListOptions
ListOptions returns the current arrangement.
func (*KanbanBoard) ListTasks ¶
func (k *KanbanBoard) ListTasks() []*db.Task
ListTasks returns the tasks in list order. Exported for tests and for the header counts.
func (*KanbanBoard) MoveDown ¶
func (k *KanbanBoard) MoveDown()
MoveDown moves selection down within the current column. If at the bottom, wraps around to the top.
func (*KanbanBoard) MoveLeft ¶
func (k *KanbanBoard) MoveLeft()
MoveLeft moves selection to the left column, skipping collapsed columns.
func (*KanbanBoard) MoveRight ¶
func (k *KanbanBoard) MoveRight()
MoveRight moves selection to the right column, skipping collapsed columns.
func (*KanbanBoard) MoveUp ¶
func (k *KanbanBoard) MoveUp()
MoveUp moves selection up within the current column. If at the top, wraps around to the bottom.
func (*KanbanBoard) NeedsInput ¶
func (k *KanbanBoard) NeedsInput(taskID int64) bool
NeedsInput returns true if the task has an active input notification.
func (*KanbanBoard) NeedsInputCount ¶
func (k *KanbanBoard) NeedsInputCount() int
NeedsInputCount returns how many tasks are waiting on the user.
func (*KanbanBoard) RefreshTheme ¶
func (k *KanbanBoard) RefreshTheme()
RefreshTheme updates column colors after a theme change.
func (*KanbanBoard) RunningTaskCount ¶
func (k *KanbanBoard) RunningTaskCount() int
RunningTaskCount returns how many tasks are currently processing.
func (*KanbanBoard) SelectTask ¶
func (k *KanbanBoard) SelectTask(id int64) bool
SelectTask selects a task by ID.
func (*KanbanBoard) SelectedTask ¶
func (k *KanbanBoard) SelectedTask() *db.Task
SelectedTask returns the currently selected task.
func (*KanbanBoard) SetBlockedByDeps ¶
func (k *KanbanBoard) SetBlockedByDeps(blockedByDeps map[int64]int)
func (*KanbanBoard) SetHiddenDoneCount ¶
func (k *KanbanBoard) SetHiddenDoneCount(count int)
SetHiddenDoneCount sets the number of done tasks not shown in the kanban.
func (*KanbanBoard) SetLatestActivity ¶
func (k *KanbanBoard) SetLatestActivity(activity map[int64]*db.TaskLog)
SetLatestActivity updates the most-recent-log-per-task map used to render activity lines.
func (*KanbanBoard) SetListMode ¶
func (k *KanbanBoard) SetListMode(on bool)
SetListMode switches between the kanban and the flat list, keeping the selected task selected across the switch.
func (*KanbanBoard) SetListOptions ¶
func (k *KanbanBoard) SetListOptions(opts ListOptions)
SetListOptions changes how the list is arranged and re-sorts it, keeping the selected task selected across the rearrangement.
func (*KanbanBoard) SetListTitle ¶
func (k *KanbanBoard) SetListTitle(title string)
SetListTitle sets the label shown in the list header — the active saved view or filter, so the list always says what it is a list of.
func (*KanbanBoard) SetOriginColumn ¶
func (k *KanbanBoard) SetOriginColumn()
SetOriginColumn sets the origin column for detail view navigation. This preserves the column context even if the task moves to a different column.
func (*KanbanBoard) SetPRInfo ¶
func (k *KanbanBoard) SetPRInfo(taskID int64, info *github.PRInfo)
SetPRInfo updates the PR info for a task.
func (*KanbanBoard) SetRunningProcesses ¶
func (k *KanbanBoard) SetRunningProcesses(running map[int64]bool)
SetRunningProcesses updates the map of tasks with running shell processes.
func (*KanbanBoard) SetSize ¶
func (k *KanbanBoard) SetSize(width, height int)
SetSize updates the board dimensions.
func (*KanbanBoard) SetTasks ¶
func (k *KanbanBoard) SetTasks(tasks []*db.Task)
SetTasks updates the tasks in the kanban board.
func (*KanbanBoard) SetTasksNeedingInput ¶
func (k *KanbanBoard) SetTasksNeedingInput(needsInput map[int64]bool)
SetTasksNeedingInput updates the map of tasks waiting for user input.
func (*KanbanBoard) SetWorkflowGroups ¶
func (k *KanbanBoard) SetWorkflowGroups(groups map[int64]*pipeline.Group)
SetBlockedByDeps updates the map of tasks blocked by dependencies. The map contains task ID -> number of open blockers. SetWorkflowGroups records the collapsed workflow groups, keyed by the lead task whose card represents the whole workflow on the board.
func (*KanbanBoard) ToggleColumnCollapse ¶
func (k *KanbanBoard) ToggleColumnCollapse(colIdx int)
ToggleColumnCollapse toggles the collapsed state of a column. Cannot collapse the currently selected column.
func (*KanbanBoard) TotalTaskCount ¶
func (k *KanbanBoard) TotalTaskCount() int
TotalTaskCount returns the total number of tasks across all columns.
func (*KanbanBoard) View ¶
func (k *KanbanBoard) View() string
View renders the kanban board.
View is called on every Bubble Tea Update — including periodic ticks, mouse motion, and task events that don't change the board — so it is the single hottest path in the TUI. We compute a signature over every input that affects the rendered output and return the previously rendered string when nothing has changed, turning idle re-renders into a cheap hash instead of a full lipgloss layout pass.
type KanbanColumn ¶
type KanbanColumn struct {
Title string
Status string // The status this column represents
Tasks []*db.Task
Color lipgloss.Color
Icon string // Visual icon for the column
}
KanbanColumn represents a column in the kanban board.
type KeyMap ¶
type KeyMap struct {
Left key.Binding
Right key.Binding
Up key.Binding
Down key.Binding
Enter key.Binding
Back key.Binding
New key.Binding
Edit key.Binding
Queue key.Binding
Retry key.Binding
Close key.Binding
Archive key.Binding
Delete key.Binding
Refresh key.Binding
Settings key.Binding
Routines key.Binding
Plugins key.Binding
Help key.Binding
Quit key.Binding
ChangeStatus key.Binding
PlaceTask key.Binding
CommandPalette key.Binding
ToggleDangerous key.Binding
QueueDangerous key.Binding
TogglePin key.Binding
Filter key.Binding
OpenWorktree key.Binding
ToggleShellPane key.Binding
JumpToNotification key.Binding
Actions key.Binding
// Column focus shortcuts
FocusBacklog key.Binding
FocusInProgress key.Binding
FocusBlocked key.Binding
FocusDone key.Binding
// Jump to pinned/unpinned tasks
JumpToPinned key.Binding
JumpToUnpinned key.Binding
// Column collapse
CollapseBacklog key.Binding
CollapseDone key.Binding
// Open browser
OpenBrowser key.Binding
// Open PR
OpenPR key.Binding
// Rebuild a task's missing worktree (detail view recovery)
RecreateWorktree key.Binding
ResumeSession key.Binding
// Board display: kanban columns vs a flat list, and the saved-view picker
ToggleListView key.Binding
SavedViews key.Binding
// How the list is arranged: group by, sort
ListOptions key.Binding
// Answer whatever the selected task is waiting on
Approvals key.Binding
// Put the cursor in the thread's message box
FocusComposer key.Binding
}
KeyMap defines key bindings.
func ApplyKeybindingsConfig ¶
func ApplyKeybindingsConfig(km KeyMap, cfg *config.KeybindingsConfig) KeyMap
ApplyKeybindingsConfig applies custom keybindings from a config to the KeyMap. Only non-nil config values override the defaults.
func LoadKeyMap ¶
func LoadKeyMap() KeyMap
LoadKeyMap loads the KeyMap from the config file, falling back to defaults.
type ListGroupBy ¶
type ListGroupBy string
ListGroupBy is the field the list breaks into sections on.
const ( GroupByStatus ListGroupBy = "status" GroupByProject ListGroupBy = "project" GroupByNone ListGroupBy = "none" )
type ListOptions ¶
type ListOptions struct {
GroupBy ListGroupBy
Sort ListSort
}
ListOptions is the arrangement of the list view.
func DefaultListOptions ¶
func DefaultListOptions() ListOptions
DefaultListOptions is what a board that has never been configured uses.
func LoadListOptions ¶
func LoadListOptions(get func(string) (string, error)) ListOptions
LoadListOptions reads the arrangement from settings.
func (ListOptions) Arrange ¶
func (o ListOptions) Arrange(tasks []*db.Task) []*db.Task
Arrange returns the tasks in display order for these options.
func (ListOptions) Normalize ¶
func (o ListOptions) Normalize() ListOptions
Normalize replaces unknown values with the defaults, so a hand-edited setting can never leave the list in a state with no renderer.
func (ListOptions) Save ¶
func (o ListOptions) Save(set func(string, string) error)
Save writes the arrangement back. Errors are ignored: a lost settings write costs a preference next launch, never the current session.
func (ListOptions) Summary ¶
func (o ListOptions) Summary() string
Summary is the one-line widget shown under the list header.
type ListOptionsModel ¶
type ListOptionsModel struct {
// contains filtered or unexported fields
}
ListOptionsModel is the modal behind `O`: three rows, left/right cycles the value under the cursor, and the list re-renders live so the effect of a choice is visible before it is committed.
func NewListOptionsModel ¶
func NewListOptionsModel(opts ListOptions, width, height int) *ListOptionsModel
NewListOptionsModel opens the widget on the current arrangement.
func (*ListOptionsModel) Init ¶
func (m *ListOptionsModel) Init() tea.Cmd
Init implements tea.Model.
func (*ListOptionsModel) IsCancelled ¶
func (m *ListOptionsModel) IsCancelled() bool
IsCancelled reports whether the user backed out.
func (*ListOptionsModel) IsDone ¶
func (m *ListOptionsModel) IsDone() bool
IsDone reports whether the user committed.
func (*ListOptionsModel) Options ¶
func (m *ListOptionsModel) Options() ListOptions
Options returns the arrangement as it currently stands, including while the user is still cycling — the caller renders the list behind the modal with it.
func (*ListOptionsModel) SetSize ¶
func (m *ListOptionsModel) SetSize(width, height int)
SetSize updates dimensions.
func (*ListOptionsModel) Update ¶
func (m *ListOptionsModel) Update(msg tea.Msg) (*ListOptionsModel, tea.Cmd)
Update handles key input.
type MenuKind ¶
type MenuKind string
MenuKind is which trigger opened the completion menu.
const ( // MenuSkills is "/", which bb declares in each provider's composerActions. MenuSkills MenuKind = "skills" // MenuMentions is "@", which the app offers for files, folders, sections // and threads. MenuMentions MenuKind = "mentions" // MenuModels picks which model answers the next message. MenuModels MenuKind = "models" // MenuPermissions picks how much that model is allowed to do. MenuPermissions MenuKind = "permissions" )
type MessageAnchor ¶
renderConversation turns the thread's rows into something readable: what was said is given room and colour, what was done is collapsed to one dim line each, so a long run of tool calls never hides the reply after it. MessageAnchor is where a message starts in the rendered conversation, and which log row it came from, so a key can act on the message a reader is looking at rather than on a line number.
type PendingApproval ¶
type PendingApproval struct {
ID string
Kind string
Summary string
Prompt string
Choices []ApprovalChoice
}
PendingApproval is one thing a thread is waiting on, flattened for display.
type PluginActionItem ¶
PluginActionItem pairs a plugin with one of its actions for display/execution.
type PluginBrowserModel ¶
type PluginBrowserModel struct {
// contains filtered or unexported fields
}
PluginBrowserModel is the searchable plugin catalog view.
func NewPluginBrowserModel ¶
func NewPluginBrowserModel(width, height int) *PluginBrowserModel
NewPluginBrowserModel creates the browser and starts the catalog load.
func (*PluginBrowserModel) Done ¶
func (m *PluginBrowserModel) Done() bool
Done reports whether the view asked to close.
func (*PluginBrowserModel) Init ¶
func (m *PluginBrowserModel) Init() tea.Cmd
Init starts the first catalog load.
func (*PluginBrowserModel) Rows ¶
func (m *PluginBrowserModel) Rows() []PluginRow
Rows returns the currently filtered rows (exported for tests).
func (*PluginBrowserModel) Selected ¶
func (m *PluginBrowserModel) Selected() (PluginRow, bool)
Selected returns the highlighted row.
func (*PluginBrowserModel) SetQuery ¶
func (m *PluginBrowserModel) SetQuery(q string)
SetQuery sets the search text and refilters — used when the view is opened with a query already in hand (e.g. from the command palette).
func (*PluginBrowserModel) SetSize ¶
func (m *PluginBrowserModel) SetSize(width, height int)
SetSize updates the view dimensions.
func (*PluginBrowserModel) Update ¶
func (m *PluginBrowserModel) Update(msg tea.Msg) (*PluginBrowserModel, tea.Cmd)
Update handles input and the async load/install/remove results.
func (*PluginBrowserModel) View ¶
func (m *PluginBrowserModel) View() string
View renders the browser: search box, scope tabs, the result list, a detail panel for the highlighted row, and the key hints.
type PluginRow ¶
type PluginRow struct {
Entry *registry.Entry
Installed *hooks.Plugin
// Dir is the installed directory's base name, when installed.
Dir string
}
PluginRow is one line in the browser: a catalog entry, an installed plugin, or both when an installed plugin is also catalogued.
func (PluginRow) Category ¶
Category is the catalog category, or "installed" for an uncatalogued plugin.
func (PluginRow) Description ¶
Description prefers the catalog copy (written for a reader deciding whether to install) over the manifest's, which is often a note to the plugin's own author.
func (PluginRow) ID ¶
ID is the row's stable handle: the catalog ID when there is one, else the installed plugin's manifest name.
func (PluginRow) IsInstalled ¶
IsInstalled reports whether the row is on disk.
type PluginScope ¶
type PluginScope int
PluginScope is which slice of the list is shown.
const ( // ScopeAll shows the catalog plus anything installed from outside it. ScopeAll PluginScope = iota // ScopeInstalled shows only what is on disk. ScopeInstalled // ScopeAvailable shows only catalog entries that are not installed yet. ScopeAvailable )
func (PluginScope) String ¶
func (s PluginScope) String() string
type ReloadState ¶
type ReloadState struct {
TaskID int64 `json:"task_id"`
Detail bool `json:"detail"`
Filter string `json:"filter"`
}
ReloadState contains navigation state only. Forms are never interrupted.
type RepoCloneModel ¶
type RepoCloneModel struct {
// contains filtered or unexported fields
}
RepoCloneModel confirms where a pasted repo URL should land, clones it, and reports the local path. It never creates a project itself — that stays with the folder-picked path.
func NewRepoCloneModel ¶
func NewRepoCloneModel(ref github.RepoRef, width, height int) *RepoCloneModel
NewRepoCloneModel prepares a clone of ref into the default clone root.
func (*RepoCloneModel) Cancel ¶
func (m *RepoCloneModel) Cancel() bool
Cancel stops an in-flight clone. It reports whether there was one — the app uses that to decide whether esc means "stop cloning" or "go back".
func (*RepoCloneModel) Init ¶
func (m *RepoCloneModel) Init() tea.Cmd
func (*RepoCloneModel) SetSize ¶
func (m *RepoCloneModel) SetSize(w, h int)
func (*RepoCloneModel) Update ¶
func (m *RepoCloneModel) Update(msg tea.Msg) (*RepoCloneModel, tea.Cmd)
func (*RepoCloneModel) View ¶
func (m *RepoCloneModel) View() string
type RetryModel ¶
type RetryModel struct {
// contains filtered or unexported fields
}
RetryModel handles retrying a blocked/failed task with optional feedback.
func NewRetryModel ¶
NewRetryModel creates a new retry model.
func (*RetryModel) GetAttachment ¶
func (m *RetryModel) GetAttachment() string
GetAttachment returns the first attachment path.
func (*RetryModel) GetAttachments ¶
func (m *RetryModel) GetAttachments() []string
GetAttachments returns all attachment paths.
func (*RetryModel) GetFeedback ¶
func (m *RetryModel) GetFeedback() string
GetFeedback returns the feedback text.
func (*RetryModel) IsDangerous ¶
func (m *RetryModel) IsDangerous() bool
IsDangerous returns whether the retry should execute in dangerous mode.
func (*RetryModel) SetSize ¶
func (m *RetryModel) SetSize(width, height int)
SetSize updates the size.
func (*RetryModel) Update ¶
func (m *RetryModel) Update(msg tea.Msg) (*RetryModel, tea.Cmd)
Update handles messages.
type RoutinesModel ¶
type RoutinesModel struct {
// contains filtered or unexported fields
}
RoutinesModel is a read-only fleet-health view over all routines: name, target project, model, enabled state, and last run outcome. Routines are a global namespace (a routine *targets* a project, it doesn't belong to one), so this view is global too — a dead routine must be visible even when its project hasn't been opened in weeks.
func NewRoutinesModel ¶
func NewRoutinesModel(database *db.DB, width, height int) *RoutinesModel
NewRoutinesModel loads routine definitions and their latest runs.
func (*RoutinesModel) SetSize ¶
func (m *RoutinesModel) SetSize(width, height int)
SetSize updates the view dimensions.
func (*RoutinesModel) Update ¶
func (m *RoutinesModel) Update(msg tea.Msg) (*RoutinesModel, tea.Cmd)
Update implements tea.Model.
type SettingsModel ¶
type SettingsModel struct {
// contains filtered or unexported fields
}
SettingsModel represents the settings view.
func NewSettingsModel ¶
func NewSettingsModel(database *db.DB, width, height int) *SettingsModel
NewSettingsModel creates a new settings model.
func (*SettingsModel) SetSize ¶
func (m *SettingsModel) SetSize(width, height int)
SetSize updates the view size.
func (*SettingsModel) Update ¶
func (m *SettingsModel) Update(msg tea.Msg) (*SettingsModel, tea.Cmd)
Update handles messages.
type StatusChoice ¶
StatusChoices, when set, replaces the list offered by the status picker. The four TaskYou statuses are states a queue moves work through; a bb thread moves itself, so the only ones a person can pick are the two that are really actions: stop it, or archive it.
type TaskRefAutocompleteModel ¶
type TaskRefAutocompleteModel struct {
// contains filtered or unexported fields
}
TaskRefAutocompleteModel provides inline autocomplete for task references (#123). It appears as a floating dropdown when the user types '#' in a text field.
func NewTaskRefAutocompleteModel ¶
func NewTaskRefAutocompleteModel(database *db.DB, width int) *TaskRefAutocompleteModel
NewTaskRefAutocompleteModel creates a new task reference autocomplete model.
func (*TaskRefAutocompleteModel) Dismiss ¶
func (m *TaskRefAutocompleteModel) Dismiss()
Dismiss closes the autocomplete without selecting.
func (*TaskRefAutocompleteModel) GetCursorPos ¶
func (m *TaskRefAutocompleteModel) GetCursorPos() int
GetCursorPos returns the position where '#' was typed.
func (*TaskRefAutocompleteModel) GetQueryLength ¶
func (m *TaskRefAutocompleteModel) GetQueryLength() int
GetQueryLength returns the length of the query (including #).
func (*TaskRefAutocompleteModel) GetReference ¶
func (m *TaskRefAutocompleteModel) GetReference() string
GetReference returns the task reference string (e.g., "#123 (Task Title)") for the selected task.
func (*TaskRefAutocompleteModel) HasResults ¶
func (m *TaskRefAutocompleteModel) HasResults() bool
HasResults returns true if there are matching tasks.
func (*TaskRefAutocompleteModel) IsDismissed ¶
func (m *TaskRefAutocompleteModel) IsDismissed() bool
IsDismissed returns true if the autocomplete was dismissed.
func (*TaskRefAutocompleteModel) MoveDown ¶
func (m *TaskRefAutocompleteModel) MoveDown()
MoveDown moves selection down.
func (*TaskRefAutocompleteModel) MoveUp ¶
func (m *TaskRefAutocompleteModel) MoveUp()
MoveUp moves selection up.
func (*TaskRefAutocompleteModel) Reset ¶
func (m *TaskRefAutocompleteModel) Reset()
Reset resets the autocomplete state.
func (*TaskRefAutocompleteModel) Select ¶
func (m *TaskRefAutocompleteModel) Select()
Select confirms the current selection.
func (*TaskRefAutocompleteModel) SelectedTask ¶
func (m *TaskRefAutocompleteModel) SelectedTask() *db.Task
SelectedTask returns the selected task, or nil if none selected.
func (*TaskRefAutocompleteModel) SetQuery ¶
func (m *TaskRefAutocompleteModel) SetQuery(query string, cursorPos int)
SetQuery sets the search query and filters tasks.
func (*TaskRefAutocompleteModel) View ¶
func (m *TaskRefAutocompleteModel) View() string
View renders the autocomplete dropdown.
type TerminalImageSupport ¶
type TerminalImageSupport int
TerminalImageSupport is which inline-image protocol this terminal speaks, if any. Terminals disagree completely here, so it is detected rather than assumed: iTerm2 has its own OSC 1337, Kitty and Ghostty share a different one, and everything else can only be given a link.
const ( ImagesUnsupported TerminalImageSupport = iota ImagesITerm2 ImagesKitty )
func DetectImageSupport ¶
func DetectImageSupport() TerminalImageSupport
DetectImageSupport reports what the current terminal can draw. tmux blocks the passthrough these protocols need unless it is configured for it, so a tmux session is treated as unsupported rather than drawing garbage.
type Theme ¶
type Theme struct {
Name string `json:"name"`
// Core colors
Primary string `json:"primary"` // Main accent color (selections, highlights)
Secondary string `json:"secondary"` // Secondary accent (links, tags)
Muted string `json:"muted"` // Dimmed text, borders
// Semantic colors
Success string `json:"success"` // Success states, done
Warning string `json:"warning"` // Warning states
Error string `json:"error"` // Error states, blocked
// Status colors
InProgress string `json:"in_progress"` // Queued/processing tasks
Done string `json:"done"` // Completed tasks
Blocked string `json:"blocked"` // Blocked tasks
Backlog string `json:"backlog"` // Backlog tasks
// Card colors
CardBg string `json:"card_bg"` // Selected card background
CardFg string `json:"card_fg"` // Selected card foreground
CardBorder string `json:"card_border"` // Card border
CardBorderHi string `json:"card_border_hi"` // Highlighted card border
ColumnBorder string `json:"column_border"` // Column border
ColumnBorderHi string `json:"column_border_hi"` // Selected column border
}
Theme defines all colors used in the UI.
type UILogger ¶
type UILogger struct {
// contains filtered or unexported fields
}
UILogger provides file-based logging for the UI package. The TUI draws on stdout, so diagnostics cannot go there; they go to ui.log beside the database.
Three properties this file depends on, each learned the hard way:
- It sits next to the database, so an isolated instance (a QA harness with its own WORKTREE_DB_PATH) writes to its own log. Sharing one file made a QA run's errors look like they came from the live TUI.
- Every line carries the pid. Several ty processes append concurrently — TUIs, the daemon — and without it there is no way to tell who wrote what.
- It rotates. Unrotated, this file reached 205MB and eight months of history.
func GetLogger ¶
func GetLogger() *UILogger
GetLogger returns the singleton UI logger instance. Call CloseLogger() when the application exits.
type View ¶
type View int
View represents the current view.
const ( ViewDashboard View = iota ViewDetail ViewNewTask ViewNewTaskConfirm ViewEditTask ViewProjectChangeConfirm // Confirmation when changing a task's project ViewDeleteConfirm ViewCloseConfirm ViewArchiveConfirm ViewQuitConfirm ViewSettings ViewRetry ViewAttachments ViewChangeStatus ViewPlacement ViewCommandPalette ViewProjectDetectConfirm // Offer to create a project for the current git repo ViewWelcome // first-run fork: set up a project vs start a task ViewFolderPicker // fuzzy folder picker for "set up a project" ViewRoutines // global routines fleet-health view ViewActionPicker // modal list of plugin actions for the current task ViewApprovals // modal answering what a thread is waiting on ViewPluginBrowser // searchable plugin catalog: browse, install, remove ViewRepoClone // clone a pasted repo URL, then continue as a folder ViewSavedViews // modal list of saved filter views ViewListOptions // modal for the list's grouping and sort )
type ViewPickerModel ¶
type ViewPickerModel struct {
// contains filtered or unexported fields
}
ViewPickerModel is the modal behind `V`: the saved views, plus the two operations that keep them useful — save the filter you are looking at, and delete one you no longer want.
A saved view is only ever its query string (see db.SavedView), so applying one is exactly "type this into the filter bar". That is why the picker hands a query back to the app instead of filtering anything itself.
func NewViewPickerModel ¶
func NewViewPickerModel(database *db.DB, currentFilter string, width, height int) *ViewPickerModel
NewViewPickerModel builds the picker over the views stored in the database.
func (*ViewPickerModel) Applied ¶
func (m *ViewPickerModel) Applied() *db.SavedView
Applied returns the view the user chose, or nil.
func (*ViewPickerModel) ClearRequested ¶
func (m *ViewPickerModel) ClearRequested() bool
ClearRequested reports whether the user asked to drop the filter entirely.
func (*ViewPickerModel) IsCancelled ¶
func (m *ViewPickerModel) IsCancelled() bool
IsCancelled reports whether the user dismissed the picker.
func (*ViewPickerModel) SetSize ¶
func (m *ViewPickerModel) SetSize(width, height int)
SetSize updates dimensions.
func (*ViewPickerModel) Update ¶
func (m *ViewPickerModel) Update(msg tea.Msg) (*ViewPickerModel, tea.Cmd)
Update handles key input. The parent reads Applied()/ClearRequested()/ IsCancelled() after each update, matching ActionPickerModel.
type WelcomeModel ¶
type WelcomeModel struct {
// contains filtered or unexported fields
}
WelcomeModel is the first-run fork shown when there's no project to suggest: "Set up a project" vs "Just start a task" (in the personal project). It also surfaces a machine-readiness status: detected agent CLIs and any missing prerequisites (non-blocking — detection does the work, nothing gates the fork).
func NewWelcomeModel ¶
func NewWelcomeModel(width, height int, detectedAgents []string, tmuxFound bool) *WelcomeModel
func (*WelcomeModel) Choice ¶
func (m *WelcomeModel) Choice() welcomeChoice
func (*WelcomeModel) MoveLeft ¶
func (m *WelcomeModel) MoveLeft()
MoveLeft/MoveRight/Choice drive selection; key handling lives in app.go so it composes with the global update loop (mirrors viewProjectDetectConfirm).
func (*WelcomeModel) MoveRight ¶
func (m *WelcomeModel) MoveRight()
func (*WelcomeModel) SetSize ¶
func (m *WelcomeModel) SetSize(w, h int)
func (*WelcomeModel) View ¶
func (m *WelcomeModel) View() string
Source Files
¶
- action_picker.go
- agent_send.go
- app.go
- approvals.go
- attachments.go
- attachpaths.go
- bell.go
- board_view.go
- clipboard.go
- command_palette.go
- composer.go
- conversation.go
- debug.go
- detail.go
- detail_remote_shell.go
- detail_view.go
- executor_spawn_guard.go
- filebrowser.go
- filter_autocomplete.go
- filter_host.go
- folderpicker.go
- form.go
- hyperlink.go
- images.go
- kanban.go
- key_csi.go
- list.go
- list_render.go
- listopts.go
- liveboard.go
- logger.go
- placement.go
- plugin_browser.go
- plugin_nudge.go
- project_detect.go
- projectname.go
- reload.go
- repoclone.go
- retry.go
- routines.go
- settings.go
- slashmenu.go
- styles.go
- task_ref_autocomplete.go
- terminal_task.go
- theme.go
- views.go
- welcome.go