console

package
v0.34.5 Latest Latest
Warning

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

Go to latest
Published: Jan 5, 2026 License: MIT Imports: 22 Imported by: 0

README

Console Rendering Package

The console package provides utilities for rendering Go structs and data structures to formatted console output, as well as progress bar and spinner components for long-running operations.

Spinner Component

The Spinner component provides animated visual feedback during long-running operations with automatic TTY detection and accessibility support.

Features
  • MiniDot animation: Minimal dot spinner (⣾ ⣽ ⣻ ⢿ ⡿ ⣟ ⣯ ⣷)
  • TTY detection: Automatically disabled in pipes/redirects
  • Accessibility support: Respects ACCESSIBLE environment variable
  • Color adaptation: Uses adaptive colors for light/dark themes
  • Thread-safe: Safe for concurrent use with mutex protection
Usage
import "github.com/githubnext/gh-aw/pkg/console"

// Create and use a spinner
spinner := console.NewSpinner("Loading...")
spinner.Start()
// Long-running operation
spinner.Stop()

// Stop with a message
spinner := console.NewSpinner("Processing...")
spinner.Start()
// Long-running operation
spinner.StopWithMessage("✓ Done!")

// Update message while running
spinner := console.NewSpinner("Starting...")
spinner.Start()
spinner.UpdateMessage("Still working...")
// Long-running operation
spinner.Stop()
Accessibility

The spinner respects the ACCESSIBLE environment variable. When 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
TTY Detection

Spinners only animate in terminal environments. When output is piped or redirected, the spinner is automatically disabled:

gh aw compile workflow.md           # Spinner animates
gh aw compile workflow.md > log.txt # Spinner disabled

ProgressBar Component

The ProgressBar component provides a reusable progress bar with TTY detection and graceful fallback for non-TTY environments.

Features
  • Scaled gradient effect: Smooth color transition from purple to cyan as progress advances
  • TTY detection: Automatically adapts to terminal environment
  • Byte formatting: Converts byte counts to human-readable sizes (KB, MB, GB)
  • Thread-safe updates: Safe for concurrent use with atomic operations
Visual Styling

The progress bar uses bubbles v0.21.0+ gradient capabilities for enhanced visual appeal:

  • Start (0%): #BD93F9 (purple) - vibrant, attention-grabbing
  • End (100%): #8BE9FD (cyan) - cool, completion feeling
  • Empty portion: #6272A4 (muted purple-gray)
  • Gradient scaling: WithScaledGradient ensures gradient scales with filled portion
Usage
Determinate Mode (known total)

Use when the total size or count is known:

import "github.com/githubnext/gh-aw/pkg/console"

// Create a progress bar for 1GB total
totalBytes := int64(1024 * 1024 * 1024)
bar := console.NewProgressBar(totalBytes)

// Update progress (returns formatted string)
output := bar.Update(currentBytes)
fmt.Fprintf(os.Stderr, "\r%s", output)
Indeterminate Mode (unknown total)

Use when the total size or count is unknown:

import "github.com/githubnext/gh-aw/pkg/console"

// Create an indeterminate progress bar
bar := console.NewIndeterminateProgressBar()

// Update with current progress (shows activity without percentage)
output := bar.Update(currentBytes)
fmt.Fprintf(os.Stderr, "\r%s", output)
Output Examples

Determinate Mode - TTY:

████████████████████░░░░░░░░░░░░░░░░░  50%

(Displays with gradient from purple to cyan)

Determinate Mode - Non-TTY:

50% (512.0MB/1.00GB)

Indeterminate Mode - TTY:

████████████████░░░░░░░░░░░░░░░░░░░░  (pulsing animation)

(Shows pulsing progress indicator)

Indeterminate Mode - Non-TTY:

Processing... (512.0MB)

RenderStruct Function

The RenderStruct function uses reflection to automatically render Go structs based on struct tags.

Struct Tags

Use the console struct tag to control rendering behavior:

Available Tags
  • header:"Column Name" - Sets the display name for the field (used in both structs and tables)
  • title:"Section Title" - Sets the title for nested structs, slices, or maps
  • omitempty - Skips the field if it has a zero value
  • "-" - Always skips the field
Tag Examples
type Overview struct {
    RunID      int64  `console:"header:Run ID"`
    Workflow   string `console:"header:Workflow"`
    Status     string `console:"header:Status"`
    Duration   string `console:"header:Duration,omitempty"`
    Internal   string `console:"-"` // Never displayed
}
Rendering Behavior
Structs

Structs are rendered as key-value pairs with proper alignment:

  Run ID    : 12345
  Workflow  : my-workflow
  Status    : completed
  Duration  : 5m30s
Slices

Slices of structs are automatically rendered as tables using the console table renderer:

type Job struct {
    Name       string `console:"header:Name"`
    Status     string `console:"header:Status"`
    Conclusion string `console:"header:Conclusion,omitempty"`
}

jobs := []Job{
    {Name: "build", Status: "completed", Conclusion: "success"},
    {Name: "test", Status: "in_progress", Conclusion: ""},
}

fmt.Print(console.RenderStruct(jobs))

Renders as:

Name  | Status      | Conclusion
----- | ----------- | ----------
build | completed   | success
test  | in_progress | -
Maps

Maps are rendered as markdown-style headers with key-value pairs:

data := map[string]string{
    "Repository": "githubnext/gh-aw",
    "Author":     "test-user",
}

fmt.Print(console.RenderStruct(data))

Renders as:

  Repository: githubnext/gh-aw
  Author    : test-user
Special Type Handling
time.Time

time.Time fields are automatically formatted as "2006-01-02 15:04:05". Zero time values are considered empty when used with omitempty.

Unexported Fields

The rendering system safely handles unexported struct fields by checking CanInterface() before attempting to access field values.

Usage in Audit Command

The audit command uses the new rendering system for structured output:

// Render overview section
renderOverview(data.Overview)

// Render metrics with custom formatting
renderMetrics(data.Metrics)

// Render jobs as a table
renderJobsTable(data.Jobs)

This provides:

  • Consistent formatting across all audit sections
  • Automatic table generation for slice data
  • Proper handling of optional/empty fields
  • Type-safe reflection-based rendering
Migration Guide

To migrate existing rendering code to use the new system:

  1. Add struct tags to your data types:

    type MyData struct {
        Field1 string `console:"header:Field 1"`
        Field2 int    `console:"header:Field 2,omitempty"`
    }
    
  2. Use RenderStruct for simple structs:

    fmt.Print(console.RenderStruct(myData))
    
  3. Use custom rendering for special formatting needs:

    func renderMyData(data MyData) {
        fmt.Printf("  %-15s %s\n", "Field 1:", formatCustom(data.Field1))
        // ... custom formatting logic
    }
    
  4. Use console.RenderTable for tables with custom formatting:

    config := console.TableConfig{
        Headers: []string{"Name", "Value"},
        Rows: [][]string{
            {truncateString(item.Name, 40), formatNumber(item.Value)},
        },
    }
    fmt.Print(console.RenderTable(config))
    

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/githubnext/gh-aw/pkg/console"
	"github.com/githubnext/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

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

Examples

Constants

This section is empty.

Variables

View Source
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 ClearScreen added in v0.22.0

func ClearScreen()

ClearScreen clears the terminal screen if stdout is a TTY Uses ANSI escape codes for cross-platform compatibility

func ConfirmAction added in v0.34.5

func ConfirmAction(title, affirmative, negative string) (bool, error)

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 added in v0.31.4

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

func FormatCommandMessage(command string) string

FormatCommandMessage formats a command execution message

func FormatCountMessage

func FormatCountMessage(message string) string

FormatCountMessage formats a count/numeric status message

func FormatError

func FormatError(err CompilerError) string

FormatError formats a CompilerError with Rust-like rendering

func FormatErrorMessage

func FormatErrorMessage(message string) string

FormatErrorMessage formats a simple error message (for stderr output)

func FormatErrorWithSuggestions added in v0.28.7

func FormatErrorWithSuggestions(message string, suggestions []string) string

FormatErrorWithSuggestions formats an error message with actionable suggestions

func FormatFileSize added in v0.28.3

func FormatFileSize(size int64) string

FormatFileSize formats file sizes in a human-readable way (e.g., "1.2 KB", "3.4 MB")

func FormatInfoMessage

func FormatInfoMessage(message string) string

FormatInfoMessage formats an informational message

func FormatListHeader

func FormatListHeader(header string) string

FormatListHeader formats a section header for lists

func FormatListItem

func FormatListItem(item string) string

FormatListItem formats an item in a list

func FormatLocationMessage

func FormatLocationMessage(message string) string

FormatLocationMessage formats a file/directory location message

func FormatNumber added in v0.26.0

func FormatNumber(n int) string

FormatNumber formats large numbers in a human-readable way (e.g., "1k", "1.2k", "1.12M")

func FormatProgressMessage

func FormatProgressMessage(message string) string

FormatProgressMessage formats a progress/activity message

func FormatPromptMessage

func FormatPromptMessage(message string) string

FormatPromptMessage formats a user prompt message

func FormatSectionHeader added in v0.34.5

func FormatSectionHeader(header string) string

FormatSectionHeader formats a section header with proper styling This is used for major sections in CLI output (e.g., "Overview", "Metrics")

func FormatSuccessMessage

func FormatSuccessMessage(message string) string

FormatSuccessMessage formats a success message with styling

func FormatVerboseMessage

func FormatVerboseMessage(message string) string

FormatVerboseMessage formats verbose debugging output

func FormatWarningMessage

func FormatWarningMessage(message string) string

FormatWarningMessage formats a warning message

func IsAccessibleMode added in v0.34.5

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 added in v0.34.0

func LayoutEmphasisBox(content string, color lipgloss.AdaptiveColor) string

LayoutEmphasisBox renders content in a thick-bordered box with custom color. In TTY mode, uses Lipgloss styled box with thick border. 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 added in v0.34.0

func LayoutInfoSection(label, value string) string

LayoutInfoSection renders an info section with left border emphasis as a single string. In TTY mode, uses Lipgloss styled section with left border. 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 added in v0.34.0

func LayoutJoinVertical(sections ...string) string

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 added in v0.34.0

func LayoutTitleBox(title string, width int) string

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 PrintBanner added in v0.31.4

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 RenderComposedSections added in v0.34.0

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 added in v0.34.0

func RenderErrorBox(title string) []string

RenderErrorBox renders an error/warning message with a thick 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 added in v0.34.0

func RenderInfoSection(content string) []string

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 added in v0.21.0

func RenderStruct(v any) string

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 added in v0.20.0

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 added in v0.34.0

func RenderTitleBox(title string, width int) []string

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 added in v0.34.0

func RenderTree(root TreeNode) string

RenderTree renders a hierarchical tree structure using lipgloss/tree package Returns styled tree output for TTY, or simple indented text for non-TTY

func ShowInteractiveList added in v0.34.2

func ShowInteractiveList(title string, items []ListItem) (string, error)

ShowInteractiveList displays an interactive list with arrow key navigation Returns the selected item's value, or an error if cancelled or failed

func ToRelativePath

func ToRelativePath(path string) string

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

type ErrorPosition struct {
	File   string
	Line   int
	Column int
}

ErrorPosition represents a position in a source file

type ListItem added in v0.34.2

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

ListItem represents an item in an interactive list

func NewListItem added in v0.34.2

func NewListItem(title, description, value string) ListItem

NewListItem creates a new list item with title, description, and value

func (ListItem) Description added in v0.34.2

func (i ListItem) Description() string

Description returns the item's description

func (ListItem) FilterValue added in v0.34.2

func (i ListItem) FilterValue() string

FilterValue returns the value used for filtering

func (ListItem) Title added in v0.34.2

func (i ListItem) Title() string

Title returns the item's title

type ProgressBar added in v0.34.2

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 added in v0.34.2

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 added in v0.34.2

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 added in v0.34.2

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 SpinnerWrapper

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

SpinnerWrapper wraps the spinner functionality with TTY detection

func NewSpinner

func NewSpinner(message string) *SpinnerWrapper

NewSpinner creates a new spinner with the given message using MiniDot style The spinner is automatically disabled when not running in a TTY or in accessibility mode

func (*SpinnerWrapper) IsEnabled

func (s *SpinnerWrapper) IsEnabled() bool

IsEnabled returns whether the spinner is enabled (i.e., running in a TTY)

func (*SpinnerWrapper) Start

func (s *SpinnerWrapper) Start()

Start begins the spinner animation

func (*SpinnerWrapper) Stop

func (s *SpinnerWrapper) Stop()

Stop stops the spinner animation

func (*SpinnerWrapper) StopWithMessage added in v0.31.4

func (s *SpinnerWrapper) StopWithMessage(msg string)

StopWithMessage stops the spinner and displays a final message The message will only be displayed if the spinner is enabled (TTY check)

func (*SpinnerWrapper) UpdateMessage

func (s *SpinnerWrapper) UpdateMessage(message string)

UpdateMessage updates the spinner message

type TableConfig

type TableConfig struct {
	Headers   []string
	Rows      [][]string
	Title     string
	ShowTotal bool
	TotalRow  []string
}

TableConfig represents configuration for table rendering

type TreeNode added in v0.34.0

type TreeNode struct {
	Value    string
	Children []TreeNode
}

TreeNode represents a node in a hierarchical tree structure

Jump to

Keyboard shortcuts

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