Documentation
¶
Overview ¶
Package console provides layout composition helpers for creating styled CLI output with Lipgloss.
Layout Composition Helpers ¶
The layout package provides reusable helper functions for common Lipgloss layout patterns. These helpers automatically respect TTY detection and provide both styled (TTY) and plain text (non-TTY) output modes.
Usage Example ¶
Here's a complete example showing how to compose a styled CLI output:
import (
"fmt"
"os"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/styles"
)
// Create layout elements
title := console.LayoutTitleBox("Trial Execution Plan", 60)
info1 := console.LayoutInfoSection("Workflow", "test-workflow")
info2 := console.LayoutInfoSection("Status", "Ready")
warning := console.LayoutEmphasisBox("⚠️ WARNING: Large workflow file", styles.ColorWarning)
// Compose sections vertically with spacing
output := console.LayoutJoinVertical(title, "", info1, info2, "", warning)
fmt.Fprintln(os.Stderr, output)
TTY Detection ¶
All layout helpers automatically detect whether output is going to a terminal (TTY) or being piped/redirected. In TTY mode, they use Lipgloss styling with colors and borders. In non-TTY mode, they output plain text suitable for parsing or logging.
Available Helpers ¶
- LayoutTitleBox: Centered title with double border
- LayoutInfoSection: Info section with left border emphasis
- LayoutEmphasisBox: Thick-bordered box with custom color
- LayoutJoinVertical: Composes sections with automatic spacing
Comparison with Existing Functions ¶
These helpers complement the existing RenderTitleBox, RenderInfoSection, and RenderComposedSections functions in console.go. The key differences:
- Layout helpers return strings instead of []string for simpler composition
- LayoutInfoSection takes separate label and value parameters
- LayoutEmphasisBox provides custom color support with thick borders
- Layout helpers are designed for inline composition and chaining
Package console provides terminal UI components including spinners for long-running operations.
Spinner Component ¶
The spinner provides visual feedback during long-running operations with a minimal dot animation (⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷). It automatically adapts to the environment:
- TTY Detection: Spinners only animate in terminal environments (disabled in pipes/redirects)
- Accessibility: Respects ACCESSIBLE environment variable to disable animations
- Color Adaptation: Uses lipgloss adaptive colors for light/dark terminal themes
Implementation ¶
This spinner uses idiomatic Bubble Tea patterns with tea.NewProgram() for proper message handling and rendering pipeline integration. It includes thread-safe lifecycle management:
- Thread-safe start/stop tracking with mutex protection
- Safe to call Stop/StopWithMessage before Start (no-op or message-only)
- Prevents multiple concurrent Start calls
- No deadlock when stopping before goroutine initializes
- Leverages Bubble Tea's message passing for updates
Usage Example ¶
spinner := console.NewSpinner("Loading...")
spinner.Start()
// Long-running operation
spinner.Stop()
Accessibility ¶
Spinners respect the ACCESSIBLE environment variable. When ACCESSIBLE is set to any value, spinner animations are disabled to support screen readers and accessibility tools.
export ACCESSIBLE=1 gh aw compile workflow.md # Spinners will be disabled
Example ¶
Example demonstrates how to compose a styled CLI output using the layout helper functions.
// Create layout elements
title := LayoutTitleBox("Trial Execution Plan", 60)
info1 := LayoutInfoSection("Workflow", "test-workflow")
info2 := LayoutInfoSection("Status", "Ready")
warning := LayoutEmphasisBox("⚠️ WARNING: Large workflow file", styles.ColorWarning)
// Compose sections vertically with spacing
output := LayoutJoinVertical(title, "", info1, info2, "", warning)
// In a real application, you would output to stderr:
// fmt.Fprintln(os.Stderr, output)
// For test purposes, just verify the output contains expected content
if !strings.Contains(output, "Trial Execution Plan") {
panic("missing title")
}
if !strings.Contains(output, "test-workflow") {
panic("missing workflow name")
}
if !strings.Contains(output, "WARNING") {
panic("missing warning")
}
Index ¶
- Variables
- func ClearLine()
- func ClearScreen()
- func ConfirmAction(title, affirmative, negative string) (bool, error)
- func FormatBanner() string
- func FormatCommandMessage(command string) string
- func FormatCountMessage(message string) string
- func FormatError(err CompilerError) string
- func FormatErrorMessage(message string) string
- func FormatErrorWithSuggestions(message string, suggestions []string) string
- func FormatFileSize(size int64) string
- func FormatInfoMessage(message string) string
- func FormatListHeader(header string) string
- func FormatListItem(item string) string
- func FormatLocationMessage(message string) string
- func FormatNumber(n int) string
- func FormatProgressMessage(message string) string
- func FormatPromptMessage(message string) string
- func FormatSectionHeader(header string) string
- func FormatSuccessMessage(message string) string
- func FormatVerboseMessage(message string) string
- func FormatWarningMessage(message string) string
- func IsAccessibleMode() bool
- func LayoutEmphasisBox(content string, color lipgloss.AdaptiveColor) string
- func LayoutInfoSection(label, value string) string
- func LayoutJoinVertical(sections ...string) string
- func LayoutTitleBox(title string, width int) string
- func LogVerbose(verbose bool, message string)
- func MoveCursorDown(n int)
- func MoveCursorUp(n int)
- func PrintBanner()
- func PromptInput(title, description, placeholder string) (string, error)
- func PromptInputWithValidation(title, description, placeholder string, validate func(string) error) (string, error)
- func PromptMultiSelect(title, description string, options []SelectOption, limit int) ([]string, error)
- func PromptSecretInput(title, description string) (string, error)
- func PromptSelect(title, description string, options []SelectOption) (string, error)
- func RenderComposedSections(sections []string)
- func RenderErrorBox(title string) []string
- func RenderInfoSection(content string) []string
- func RenderStruct(v any) string
- func RenderTable(config TableConfig) string
- func RenderTableAsJSON(config TableConfig) (string, error)
- func RenderTitleBox(title string, width int) []string
- func RenderTree(root TreeNode) string
- func RunForm(fields []FormField) error
- func ShowInteractiveList(title string, items []ListItem) (string, error)
- func ToRelativePath(path string) string
- type CompilerError
- type ErrorPosition
- type FormField
- type ListItem
- type ProgressBar
- type SelectOption
- type SpinnerWrapper
- type TableConfig
- type TreeNode
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var BannerStyle = lipgloss.NewStyle(). Bold(true). Foreground(styles.ColorPurple)
BannerStyle defines the style for the ASCII banner Uses GitHub's purple color theme
Functions ¶
func ClearLine ¶
func ClearLine()
ClearLine clears the current line in the terminal if stderr is a TTY Uses ANSI escape codes: \r moves cursor to start, \033[K clears to end of line
func ClearScreen ¶
func ClearScreen()
ClearScreen clears the terminal screen if stderr is a TTY Uses ANSI escape codes for cross-platform compatibility
func ConfirmAction ¶
ConfirmAction shows an interactive confirmation dialog using Bubble Tea (huh) Returns true if the user confirms, false if they cancel or an error occurs
func FormatBanner ¶
func FormatBanner() string
FormatBanner returns the ASCII logo formatted with purple GitHub color theme. It applies the purple color styling when running in a terminal (TTY).
func FormatCommandMessage ¶
FormatCommandMessage formats a command execution message
func FormatCountMessage ¶
FormatCountMessage formats a count/numeric status message
func FormatError ¶
func FormatError(err CompilerError) string
FormatError formats a CompilerError with Rust-like rendering
func FormatErrorMessage ¶
FormatErrorMessage formats a simple error message (for stderr output)
func FormatErrorWithSuggestions ¶
FormatErrorWithSuggestions formats an error message with actionable suggestions
func FormatFileSize ¶
FormatFileSize formats file sizes in a human-readable way (e.g., "1.2 KB", "3.4 MB")
func FormatInfoMessage ¶
FormatInfoMessage formats an informational message
func FormatListHeader ¶
FormatListHeader formats a section header for lists
func FormatListItem ¶
FormatListItem formats an item in a list
func FormatLocationMessage ¶
FormatLocationMessage formats a file/directory location message
func FormatNumber ¶
FormatNumber formats large numbers in a human-readable way (e.g., "1k", "1.2k", "1.12M")
func FormatProgressMessage ¶
FormatProgressMessage formats a progress/activity message
func FormatPromptMessage ¶
FormatPromptMessage formats a user prompt message
func FormatSectionHeader ¶
FormatSectionHeader formats a section header with proper styling This is used for major sections in CLI output (e.g., "Overview", "Metrics")
func FormatSuccessMessage ¶
FormatSuccessMessage formats a success message with styling
func FormatVerboseMessage ¶
FormatVerboseMessage formats verbose debugging output
func FormatWarningMessage ¶
FormatWarningMessage formats a warning message
func IsAccessibleMode ¶
func IsAccessibleMode() bool
IsAccessibleMode detects if accessibility mode should be enabled based on environment variables. Accessibility mode is enabled when: - ACCESSIBLE environment variable is set to any value - TERM environment variable is set to "dumb" - NO_COLOR environment variable is set to any value
This function should be used by UI components to determine whether to: - Disable animations and spinners - Simplify interactive elements - Use plain text instead of fancy formatting
func LayoutEmphasisBox ¶
func LayoutEmphasisBox(content string, color lipgloss.AdaptiveColor) string
LayoutEmphasisBox renders content in a rounded-bordered box with custom color. In TTY mode, uses Lipgloss styled box with rounded border for a polished appearance. In non-TTY mode, renders content with surrounding marker lines.
Example:
warning := console.LayoutEmphasisBox("⚠️ WARNING: Large workflow", styles.ColorWarning)
fmt.Fprintln(os.Stderr, warning)
func LayoutInfoSection ¶
LayoutInfoSection renders an info section with left border emphasis as a single string. In TTY mode, uses Lipgloss styled section with left border and padding. In non-TTY mode, adds manual indentation. This is a simpler alternative to RenderInfoSection that returns a string and takes label/value.
Example:
info := console.LayoutInfoSection("Workflow", "test-workflow")
fmt.Fprintln(os.Stderr, info)
func LayoutJoinVertical ¶
LayoutJoinVertical composes sections vertically with automatic spacing. In TTY mode, uses lipgloss.JoinVertical for proper composition. In non-TTY mode, joins sections with newlines.
Example:
title := console.LayoutTitleBox("Plan", 60)
info := console.LayoutInfoSection("Status", "Ready")
output := console.LayoutJoinVertical(title, info)
fmt.Fprintln(os.Stderr, output)
func LayoutTitleBox ¶
LayoutTitleBox renders a title with a double border box as a single string. In TTY mode, uses Lipgloss styled box centered with the Info color scheme. In non-TTY mode, renders plain text with separator lines. This is a simpler alternative to RenderTitleBox that returns a string instead of []string.
Example:
title := console.LayoutTitleBox("Trial Execution Plan", 60)
fmt.Fprintln(os.Stderr, title)
func LogVerbose ¶
LogVerbose outputs a verbose message to stderr only when verbose mode is enabled. This is a convenience helper to avoid repetitive if-verbose checks throughout the codebase.
func MoveCursorDown ¶ added in v0.42.14
func MoveCursorDown(n int)
MoveCursorDown moves cursor down n lines if stderr is a TTY. Uses ANSI escape code: \033[nB where n is the number of lines.
func MoveCursorUp ¶ added in v0.42.14
func MoveCursorUp(n int)
MoveCursorUp moves cursor up n lines if stderr is a TTY. Uses ANSI escape code: \033[nA where n is the number of lines.
func PrintBanner ¶
func PrintBanner()
PrintBanner prints the ASCII logo to stderr with purple GitHub color theme. This is used by the --banner flag to display the logo at the start of command execution.
func PromptInput ¶ added in v0.42.14
PromptInput shows an interactive text input prompt using Bubble Tea (huh) Returns the entered text or an error
func PromptInputWithValidation ¶ added in v0.42.14
func PromptInputWithValidation(title, description, placeholder string, validate func(string) error) (string, error)
PromptInputWithValidation shows an interactive text input with custom validation Returns the entered text or an error
func PromptMultiSelect ¶ added in v0.42.14
func PromptMultiSelect(title, description string, options []SelectOption, limit int) ([]string, error)
PromptMultiSelect shows an interactive multi-select menu Returns the selected values or an error
func PromptSecretInput ¶ added in v0.42.14
PromptSecretInput shows an interactive password input prompt with masking The input is masked for security and includes validation Returns the entered secret value or an error
func PromptSelect ¶ added in v0.42.14
func PromptSelect(title, description string, options []SelectOption) (string, error)
PromptSelect shows an interactive single-select menu Returns the selected value or an error
func RenderComposedSections ¶
func RenderComposedSections(sections []string)
RenderComposedSections composes and outputs a slice of sections to stderr. In TTY mode, uses lipgloss.JoinVertical for proper composition. In non-TTY mode, outputs each section as a separate line. Adds blank lines before and after the output.
func RenderErrorBox ¶
RenderErrorBox renders an error/warning message with a rounded border box in TTY mode, or plain text in non-TTY mode. The box will be styled with the Error color scheme for critical messages. Returns a slice of strings ready to be added to sections or printed directly.
func RenderInfoSection ¶
RenderInfoSection renders an info section with left border emphasis in TTY mode, or plain text with manual indentation in non-TTY mode. Returns a slice of strings ready to be added to sections.
func RenderStruct ¶
RenderStruct renders a Go struct to console output using reflection and struct tags. It supports: - Rendering structs as markdown-style headers with key-value pairs - Rendering slices as tables using the console table renderer - Rendering maps as markdown headers
Struct tags: - `console:"title:My Title"` - Sets the title for a section - `console:"header:Column Name"` - Sets the column header name for table columns - `console:"omitempty"` - Skips zero values - `console:"-"` - Skips the field entirely
func RenderTable ¶
func RenderTable(config TableConfig) string
RenderTable renders a formatted table using lipgloss/table package
func RenderTableAsJSON ¶
func RenderTableAsJSON(config TableConfig) (string, error)
RenderTableAsJSON renders a table configuration as JSON This converts the table structure to a JSON array of objects
func RenderTitleBox ¶
RenderTitleBox renders a title with a double border box in TTY mode, or plain text with separator lines in non-TTY mode. The box will be centered and styled with the Info color scheme. Returns a slice of strings ready to be added to sections.
func RenderTree ¶
RenderTree renders a hierarchical tree structure using lipgloss/tree package Returns styled tree output for TTY, or simple indented text for non-TTY
func RunForm ¶ added in v0.42.14
RunForm executes a multi-field form with validation This is a higher-level helper that creates a form with multiple fields
func ShowInteractiveList ¶
ShowInteractiveList displays an interactive list with arrow key navigation Returns the selected item's value, or an error if cancelled or failed
func ToRelativePath ¶
ToRelativePath converts an absolute path to a relative path from the current working directory
Types ¶
type CompilerError ¶
type CompilerError struct {
Position ErrorPosition
Type string // "error", "warning", "info"
Message string
Context []string // Source code lines for context
Hint string // Optional hint for fixing the error
}
CompilerError represents a structured compiler error with position information
type ErrorPosition ¶
ErrorPosition represents a position in a source file
type FormField ¶ added in v0.42.14
type FormField struct {
Type string // "input", "password", "confirm", "select"
Title string
Description string
Placeholder string
Value any // Pointer to the value to store the result
Options []SelectOption // For select fields
Validate func(string) error // For input/password fields
}
FormField represents a generic form field configuration
type ListItem ¶
type ListItem struct {
// contains filtered or unexported fields
}
ListItem represents an item in an interactive list
func NewListItem ¶
NewListItem creates a new list item with title, description, and value
func (ListItem) Description ¶
Description returns the item's description
func (ListItem) FilterValue ¶
FilterValue returns the value used for filtering
type ProgressBar ¶
type ProgressBar struct {
// contains filtered or unexported fields
}
ProgressBar provides a reusable progress bar component with TTY detection and graceful fallback to text-based progress for non-TTY environments.
Modes:
- Determinate: When total size is known (shows percentage and progress)
- Indeterminate: When total size is unknown (shows activity indicator)
Visual Features:
- Scaled gradient effect from purple (#BD93F9) to cyan (#8BE9FD)
- Smooth color transitions using bubbles v0.21.0+ gradient capabilities
- Gradient scales with filled portion for enhanced visual feedback
- Works well in both light and dark terminal themes
The gradient provides visual appeal without affecting functionality:
- TTY mode: Visual progress bar with smooth gradient transitions
- Non-TTY mode: Text-based percentage with human-readable byte sizes
func NewIndeterminateProgressBar ¶
func NewIndeterminateProgressBar() *ProgressBar
NewIndeterminateProgressBar creates a progress bar for when the total size is unknown This mode shows activity without a specific completion percentage, useful for:
- Streaming downloads with unknown size
- Processing unknown number of items
- Operations where duration cannot be predicted
The progress bar automatically adapts to TTY/non-TTY environments
func NewProgressBar ¶
func NewProgressBar(total int64) *ProgressBar
NewProgressBar creates a new progress bar with the specified total size (determinate mode) The progress bar automatically adapts to TTY/non-TTY environments
func (*ProgressBar) Update ¶
func (p *ProgressBar) Update(current int64) string
Update updates the current progress and returns a formatted string In determinate mode:
- TTY: Returns a visual progress bar with gradient and percentage
- Non-TTY: Returns text percentage with human-readable sizes
In indeterminate mode:
- TTY: Returns a pulsing progress indicator
- Non-TTY: Returns processing indicator with current value
type SelectOption ¶ added in v0.42.14
SelectOption represents a selectable option with a label and value
type SpinnerWrapper ¶
type SpinnerWrapper struct {
// contains filtered or unexported fields
}
SpinnerWrapper wraps the spinner functionality with TTY detection and Bubble Tea program
func NewSpinner ¶
func NewSpinner(message string) *SpinnerWrapper
NewSpinner creates a new spinner with the given message using MiniDot style. Automatically disabled when not running in a TTY or when ACCESSIBLE env var is set.
func (*SpinnerWrapper) IsEnabled ¶
func (s *SpinnerWrapper) IsEnabled() bool
func (*SpinnerWrapper) Start ¶
func (s *SpinnerWrapper) Start()
func (*SpinnerWrapper) Stop ¶
func (s *SpinnerWrapper) Stop()
func (*SpinnerWrapper) StopWithMessage ¶
func (s *SpinnerWrapper) StopWithMessage(msg string)
func (*SpinnerWrapper) UpdateMessage ¶
func (s *SpinnerWrapper) UpdateMessage(message string)