ui

package
v1.6.0 Latest Latest
Warning

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

Go to latest
Published: May 1, 2026 License: Apache-2.0 Imports: 25 Imported by: 0

Documentation

Index

Constants

View Source
const (
	IconSuccess = "✓"
	IconError   = "✗"
	IconWarning = "!"
	IconInfo    = "›"
	IconArrow   = "→"
	IconCached  = "cached"
	IconFresh   = "fresh"
)
View Source
const SpinnerInterval = 80 * time.Millisecond

SpinnerInterval is the shared animation speed for all spinners.

View Source
const TUIHints = "↑/↓ navigate  ⏎ select  q quit"

TUIHints contains shared TUI chrome for navigation hints.

Variables

View Source
var (
	// AdaptiveColor{Light: colorForLightBg, Dark: colorForDarkBg}
	ColorPurple = lipgloss.AdaptiveColor{Light: "93", Dark: "135"}  // Purple - primary actions
	ColorGreen  = lipgloss.AdaptiveColor{Light: "28", Dark: "76"}   // Green - success
	ColorYellow = lipgloss.AdaptiveColor{Light: "136", Dark: "220"} // Yellow - warnings
	ColorRed    = lipgloss.AdaptiveColor{Light: "124", Dark: "167"} // Red - errors
	ColorBlue   = lipgloss.AdaptiveColor{Light: "27", Dark: "75"}   // Blue - links
	ColorWhite  = lipgloss.AdaptiveColor{Light: "235", Dark: "255"} // Text - values (dark on light, white on dark)
	ColorGray   = lipgloss.AdaptiveColor{Light: "242", Dark: "245"} // Gray - secondary text
	ColorDim    = lipgloss.AdaptiveColor{Light: "247", Dark: "240"} // Dim - muted text
)
View Source
var (
	// StyleTitle for main headings.
	StyleTitle = lipgloss.NewStyle().Bold(true).Foreground(ColorPurple)

	// StyleHighlight for emphasized values.
	StyleHighlight = lipgloss.NewStyle().Foreground(ColorPurple)

	// StyleLink for URLs.
	StyleLink = lipgloss.NewStyle().Foreground(ColorBlue).Underline(true)

	// StyleDim for secondary/muted text.
	StyleDim = lipgloss.NewStyle().Foreground(ColorDim)

	// StyleValue for data values.
	StyleValue = lipgloss.NewStyle().Foreground(ColorWhite)

	// StyleNumber for numeric values.
	StyleNumber = lipgloss.NewStyle().Foreground(ColorPurple)

	// StyleSuccess for success messages.
	StyleSuccess = lipgloss.NewStyle().Foreground(ColorGreen)

	// StyleWarning for warning messages.
	StyleWarning = lipgloss.NewStyle().Foreground(ColorYellow)

	// StyleInfo for informational messages.
	StyleInfo = lipgloss.NewStyle().Foreground(ColorBlue)

	// StyleError for error messages.
	StyleError = lipgloss.NewStyle().Foreground(ColorRed)
)
View Source
var (
	StyleIconSuccess = lipgloss.NewStyle().Foreground(ColorGreen)
	StyleIconError   = lipgloss.NewStyle().Foreground(ColorRed)
	StyleIconWarning = lipgloss.NewStyle().Foreground(ColorYellow)
	StyleIconInfo    = lipgloss.NewStyle().Foreground(ColorGray)
	StyleIconSpinner = lipgloss.NewStyle().Foreground(ColorPurple)

	StyleCached   = lipgloss.NewStyle().Foreground(ColorGreen)
	StyleComputed = lipgloss.NewStyle().Foreground(ColorGray)

	StyleCommand  = lipgloss.NewStyle().Foreground(ColorBlue)
	StyleKeyLabel = lipgloss.NewStyle().Foreground(ColorGray).Width(12)
)
View Source
var (
	ListSelectedStyle = lipgloss.NewStyle().Bold(true).Foreground(ColorPurple)
	ListNormalStyle   = lipgloss.NewStyle().Foreground(ColorWhite)
	ListDimStyle      = lipgloss.NewStyle().Foreground(ColorDim)

	TUITableBorder = lipgloss.NewStyle().Foreground(ColorDim)
	TUIHeaderStyle = lipgloss.NewStyle().Foreground(ColorGray).Bold(true)
)

List styles

View Source
var SpinnerFrames = []string{"⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"}

SpinnerFrames is the shared animation sequence for all spinners and progress views. Using a consistent animation provides a cohesive visual experience across all CLI operations.

Functions

func FindRoots

func FindRoots(g *dag.DAG) []string

FindRoots returns sorted IDs of all root nodes (nodes with no parents).

func FitToWidth

func FitToWidth(s string, width int) string

FitToWidth truncates a string to fit within a terminal width.

func FormatAge added in v1.6.0

func FormatAge(t time.Time) string

FormatAge returns a coarse human-friendly "X minutes/hours/days ago" label for the given timestamp.

func FormatBytes added in v1.6.0

func FormatBytes(b int64) string

FormatBytes formats a byte count using IEC binary units (KB = 1024 B, etc.).

Examples:

FormatBytes(512)           → "512 B"
FormatBytes(2048)          → "2.0 KB"
FormatBytes(5 * 1 << 20)   → "5.0 MB"

func FormatConstraints

func FormatConstraints(constraints []string) string

FormatConstraints formats a list of constraints for display.

func FormatCount added in v1.6.0

func FormatCount(n int) string

FormatCount formats a number with K/M suffixes for readability.

Examples:

FormatCount(42)      → "42"
FormatCount(1_500)   → "1.5K"
FormatCount(25_000)  → "25.0K"
FormatCount(1_750_000) → "1.8M"

func FormatDuration

func FormatDuration(d time.Duration) string

FormatDuration formats a duration for human-readable display.

func FormatRequiredBy

func FormatRequiredBy(parents []string, isDirect bool, rootName string) string

FormatRequiredBy formats the "required by" field for display.

func FormatRuntimeSource added in v1.6.0

func FormatRuntimeSource(source string) string

FormatRuntimeSource returns a human-readable parenthesized label for the given runtime-source tag as emitted by the pipeline (e.g. "cli", "manifest", "package", "default"). Unknown values return the empty string.

func IsQuiet

func IsQuiet() bool

IsQuiet returns the current quiet mode state.

func JoinDot

func JoinDot(parts []string) string

JoinDot joins parts with a dim " · " separator.

func NodeVersion added in v1.6.0

func NodeVersion(g *dag.DAG, id string) string

NodeVersion extracts the version string from a DAG node's metadata.

func PadRight

func PadRight(s string, width int) string

PadRight pads a string to the right with spaces to reach the given display width (using runewidth for correct CJK/emoji handling).

func PrintDetail

func PrintDetail(format string, args ...any)

PrintDetail prints a detail line (indented) to stderr. Suppressed in quiet mode.

func PrintError

func PrintError(format string, args ...any)

PrintError prints an error message to stderr. Never suppressed.

func PrintFile

func PrintFile(path string)

PrintFile prints a file output line to stderr. Suppressed in quiet mode.

func PrintHeader

func PrintHeader(title string)

PrintHeader prints a styled top-level section header to stderr. Used for multi-phase commands (e.g. GitHub flow) and informational displays (e.g. whoami). Suppressed in quiet mode.

func PrintInfo

func PrintInfo(format string, args ...any)

PrintInfo prints an info/status message to stderr. Suppressed in quiet mode.

func PrintInline

func PrintInline(format string, args ...any)

PrintInline prints a dim message to stderr without a trailing newline.

func PrintKeyValue

func PrintKeyValue(key, value string)

PrintKeyValue prints a labeled value to stderr.

func PrintNewline

func PrintNewline()

PrintNewline prints an empty line to stderr. Suppressed in quiet mode.

func PrintNextStep

func PrintNextStep(description, cmd string)

PrintNextStep prints a suggested next command to stderr. Suppressed in quiet mode.

func PrintRenderStats

func PrintRenderStats(s RenderStats)

PrintRenderStats prints a curated summary of the render operation.

func PrintResolveSummary

func PrintResolveSummary(w io.Writer, result ResolveResult)

PrintResolveSummary prints a summary line for resolved dependencies.

func PrintRuntimeInfo added in v1.6.0

func PrintRuntimeInfo(langName, version, source string)

PrintRuntimeInfo prints a styled "Runtime: <lang> <version> (source)" line to stderr followed by a blank line. Suppressed in quiet mode. If version is empty, this is a no-op.

func PrintSeparator added in v1.6.0

func PrintSeparator(title string)

PrintSeparator prints a styled inline separator ("─── Title ───") on its own line, padded with blank lines above and below. Use this to delimit sub-sections WITHIN a single command's output (not as a top-level banner — use PrintHeader for that). Suppressed in quiet mode.

func PrintStats

func PrintStats(nodeCount, edgeCount, depth int, cached bool, elapsed time.Duration)

PrintStats prints graph statistics on a single line to stderr. Suppressed in quiet mode. Pass depth <= 0 to omit the depth field. Pass elapsed <= 0 to omit timing.

func PrintSuccess

func PrintSuccess(format string, args ...any)

PrintSuccess prints a success message to stderr. Suppressed in quiet mode.

func PrintTreeSummary

func PrintTreeSummary(w io.Writer, nodeCount int, stats TreeStats)

PrintTreeSummary writes a styled summary line for resolved dependencies.

func PrintVersionInfo

func PrintVersionInfo(version, commit, date string)

PrintVersionInfo prints styled version information to stderr.

func PrintWarning

func PrintWarning(format string, args ...any)

PrintWarning prints a warning message to stderr. Never suppressed.

func RenderHelp

func RenderHelp(cmd *cobra.Command) string

RenderHelp renders styled help output for a cobra command.

func RenderUsage

func RenderUsage(cmd *cobra.Command) string

RenderUsage renders styled usage output for a cobra command.

func SetQuiet

func SetQuiet(q bool)

SetQuiet sets the quiet mode flag.

func StderrIsTTY added in v1.6.0

func StderrIsTTY() bool

StderrIsTTY reports whether stderr is an interactive terminal. Progress views and spinners use this since they always write to stderr.

func StderrWidth added in v1.6.0

func StderrWidth() int

StderrWidth returns the current stderr terminal width, falling back to defaultTerminalWidth (80 columns). Used by the progress view to fit multi-line status output without wrapping.

func StdoutIsTTY added in v1.6.0

func StdoutIsTTY() bool

StdoutIsTTY reports whether stdout is an interactive terminal. Callers use this to decide between styled human output and machine-readable output suitable for pipes (e.g. `list | head`).

func StdoutWidth added in v1.6.0

func StdoutWidth() int

StdoutWidth returns the current stdout terminal width, falling back to defaultTerminalWidth (80 columns) when stdout is not a terminal or the width cannot be determined.

func SupportedManifestList

func SupportedManifestList(langs []*deps.Language) string

SupportedManifestList returns a comma-separated, alphabetically sorted list of supported manifest filenames across the given languages.

The result for the global languages.All slice is cached because it's hit from error paths that can fire multiple times per command (e.g. `resolve` surfacing both an argument hint and a file-not-found hint in one run), and the list is stable for the lifetime of the process.

func Truncate

func Truncate(s string, maxLen int) string

Truncate truncates a string to the given max display width, adding "..." if needed. Uses runewidth to avoid slicing mid-rune.

func WriteDiff added in v1.5.0

func WriteDiff(w io.Writer, d *dag.DiffResult)

WriteDiff renders a diff result to the writer in the styled terminal format.

func WritePaths added in v1.5.0

func WritePaths(w io.Writer, target, version string, paths [][]string, shortestDepth int)

WritePaths renders dependency paths to the writer in the styled terminal format.

func WriteResolveOutput

func WriteResolveOutput(w io.Writer, result ResolveResult, color bool)

WriteResolveOutput writes a formatted resolver output to w.

func WriteStats added in v1.5.0

func WriteStats(w io.Writer, r StatsReport)

WriteStats renders a stats report to the writer in the styled terminal format.

Types

type LoadBearingEntry added in v1.5.0

type LoadBearingEntry struct {
	Package     string `json:"package"`
	ReverseDeps int    `json:"reverse_deps"`
}

LoadBearingEntry records a load-bearing package and its reverse dep count.

type ManifestListModel

type ManifestListModel struct {
	Manifests []github.ManifestFile
	Cursor    int
	Selected  *github.ManifestFile
}

ManifestListModel is the bubbletea model for interactive manifest selection.

func NewManifestListModel

func NewManifestListModel(manifests []github.ManifestFile) ManifestListModel

NewManifestListModel creates a new manifest list model.

func (ManifestListModel) Init

func (m ManifestListModel) Init() tea.Cmd

func (ManifestListModel) Update

func (m ManifestListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (ManifestListModel) View

func (m ManifestListModel) View() string

type Operation

type Operation interface {
	// Stop halts the operation's progress display.
	Stop()
	// StopWithError stops and displays an error message.
	StopWithError(message string)
}

Operation represents a running CLI operation with progress indication. Both Spinner and ProgressView implement this interface, allowing code to work with either depending on the complexity of the operation.

Use Spinner for simple operations (network fetches, file I/O). Use ProgressView for complex resolver operations with observability hooks.

type ProgressView

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

ProgressView renders live resolver progress on stderr. It implements both observability.ResolverHooks and observability.RateLimitHooks so the crawler and HTTP clients feed it events without any direct coupling to CLI code.

ProgressView uses shared spinner frames from styles.go for consistent visual appearance with the simpler Spinner component.

func NewProgressView

func NewProgressView(ctx context.Context, message string, maxNodes int) *ProgressView

NewProgressView creates a new progress view.

func (*ProgressView) OnCircuitStateChange

func (pv *ProgressView) OnCircuitStateChange(_ context.Context, registry string, state observability.CircuitState, until time.Time)

func (*ProgressView) OnEnrichComplete

func (pv *ProgressView) OnEnrichComplete(_ context.Context, _ string, _ int, _ error)

func (*ProgressView) OnEnrichStart

func (pv *ProgressView) OnEnrichStart(_ context.Context, provider string, count int)

func (*ProgressView) OnFetchComplete

func (pv *ProgressView) OnFetchComplete(_ context.Context, pkg string, _ int, _ int, err error)

func (*ProgressView) OnFetchStart

func (pv *ProgressView) OnFetchStart(_ context.Context, pkg string, _ int)

func (*ProgressView) OnProgress

func (pv *ProgressView) OnProgress(_ context.Context, _, pending, _ int)

func (*ProgressView) OnRateLimitHit

func (pv *ProgressView) OnRateLimitHit(_ context.Context, registry string, retryAfterSeconds int)

func (*ProgressView) OnRateLimitWait

func (pv *ProgressView) OnRateLimitWait(_ context.Context, registry string, wait time.Duration)

func (*ProgressView) OnRetry

func (pv *ProgressView) OnRetry(_ context.Context, registry string, attempt int, backoff time.Duration)

func (*ProgressView) Start

func (pv *ProgressView) Start()

Start registers hooks and launches the render loop. If another ProgressView is already active, Start is a no-op (the other view keeps its hook registrations). This guards against accidental concurrent use, which would otherwise race for the global observability hook slots.

func (*ProgressView) Stop

func (pv *ProgressView) Stop()

Stop deregisters hooks, cancels the render loop, and clears the output area.

func (*ProgressView) StopWithError

func (pv *ProgressView) StopWithError(message string)

StopWithError deregisters hooks, clears progress, and prints an error.

type ProgressWriter

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

ProgressWriter wraps an io.Writer so that any write (e.g. from a logger) first clears the progress display. The next render tick redraws it below the newly written content, preventing interleaved output.

func NewProgressWriter

func NewProgressWriter(pv *ProgressView, w io.Writer) *ProgressWriter

NewProgressWriter creates a writer that clears progress before writing.

func (*ProgressWriter) Write

func (pw *ProgressWriter) Write(p []byte) (n int, err error)

type RefItem

type RefItem struct {
	Name      string
	Type      string // "branch" or "tag"
	Commit    string
	IsDefault bool
}

RefItem represents a branch or tag for interactive selection.

type RefListModel

type RefListModel struct {
	Items    []RefItem
	Cursor   int
	Selected *RefItem
	Filter   string
	Filtered []int // indices into Items matching the filter
	Height   int
	Offset   int
}

RefListModel is the bubbletea model for interactive ref selection.

func NewRefListModel

func NewRefListModel(branches []github.Branch, tags []github.Tag, defaultBranch string) *RefListModel

NewRefListModel creates a ref list with the default branch first.

func (*RefListModel) Init

func (m *RefListModel) Init() tea.Cmd

func (*RefListModel) Update

func (m *RefListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (*RefListModel) View

func (m *RefListModel) View() string

type RenderStats

type RenderStats struct {
	Layers      int
	Crossings   int
	OrderingRan bool // true when layout ordering was computed (not just visualization)
	Ordering    string
	Style       string
	Dimensions  string
}

RenderStats holds curated render output info.

type RepoListModel

type RepoListModel struct {
	Repos    []github.RepoWithManifests
	Cursor   int
	Selected *RepoSelection
	Height   int
	Offset   int
}

RepoListModel is the bubbletea model for interactive repo selection.

func NewRepoListModel

func NewRepoListModel(repos []github.RepoWithManifests) RepoListModel

NewRepoListModel creates a new repo list model.

func (RepoListModel) Init

func (m RepoListModel) Init() tea.Cmd

func (RepoListModel) Update

func (m RepoListModel) Update(msg tea.Msg) (tea.Model, tea.Cmd)

func (RepoListModel) View

func (m RepoListModel) View() string

type RepoSelection

type RepoSelection struct {
	Repo *github.RepoWithManifests
}

RepoSelection holds the result of the repo selection.

type ResolveEntry

type ResolveEntry = pipeline.ResolveEntry

Type aliases for pipeline types used in UI functions. These make the UI code cleaner while keeping the types in the pipeline package.

type ResolveResult

type ResolveResult = pipeline.ResolveResult

Type aliases for pipeline types used in UI functions. These make the UI code cleaner while keeping the types in the pipeline package.

type Spinner

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

Spinner provides a simple progress indicator with context cancellation support. It uses shared animation frames from styles.go for consistent visual appearance across all CLI operations.

For complex resolver operations that require observability hooks, use ProgressView instead.

func NewSpinner

func NewSpinner(message string) *Spinner

NewSpinner creates a new spinner with the given message. The spinner uses shared animation frames from styles.go.

func NewSpinnerWithContext

func NewSpinnerWithContext(ctx context.Context, message string) *Spinner

NewSpinnerWithContext creates a spinner that will stop when the context is cancelled. The spinner uses shared animation frames from styles.go.

func (*Spinner) Cancelled

func (s *Spinner) Cancelled() bool

Cancelled returns true if the spinner was stopped due to context cancellation.

func (*Spinner) ClearLine

func (s *Spinner) ClearLine()

ClearLine clears the current spinner line without stopping. Use this before printing other output while the spinner is running.

func (*Spinner) Start

func (s *Spinner) Start()

Start begins the spinner animation. It is safe to call more than once; subsequent calls are no-ops.

In quiet mode or when stderr is not a terminal, no animation is shown but Stop() remains callable.

func (*Spinner) Stop

func (s *Spinner) Stop()

Stop stops the spinner and clears the line. Stop is safe to call concurrently from multiple goroutines.

func (*Spinner) StopWithError

func (s *Spinner) StopWithError(message string)

StopWithError stops the spinner and shows an error message.

func (*Spinner) StopWithSuccess

func (s *Spinner) StopWithSuccess(message string)

StopWithSuccess stops the spinner and shows a success message.

func (*Spinner) StopWithWarning

func (s *Spinner) StopWithWarning(message string)

StopWithWarning stops the spinner and shows a warning message.

func (*Spinner) UpdateMessage

func (s *Spinner) UpdateMessage(message string)

UpdateMessage changes the spinner message while running. This is useful for multi-phase operations that need to indicate progress.

type StatsReport added in v1.5.0

type StatsReport struct {
	Root     string `json:"root"`
	Version  string `json:"version,omitempty"`
	Language string `json:"language,omitempty"`

	// Overview
	TotalPackages  int `json:"total_packages"`
	TotalEdges     int `json:"total_edges"`
	MaxDepth       int `json:"max_depth"`
	DirectDeps     int `json:"direct"`
	TransitiveDeps int `json:"transitive"`

	// Maintenance
	SingleMaintainerCount int      `json:"single_maintainer_count,omitempty"`
	SingleMaintainerPct   float64  `json:"single_maintainer_pct,omitempty"`
	Brittle               []string `json:"brittle,omitempty"`
	Archived              []string `json:"archived,omitempty"`
	MedianLastCommitDays  int      `json:"median_last_commit_days,omitempty"`
	HasMaintenanceData    bool     `json:"-"`

	// Licenses
	LicenseSummary   map[string]int `json:"license_summary,omitempty"`   // risk category -> count
	LicenseBreakdown map[string]int `json:"license_breakdown,omitempty"` // license name -> count
	Compliant        bool           `json:"compliant,omitempty"`
	HasLicenseData   bool           `json:"-"`

	// Vulnerabilities
	VulnCritical int               `json:"vuln_critical,omitempty"`
	VulnHigh     int               `json:"vuln_high,omitempty"`
	VulnMedium   int               `json:"vuln_medium,omitempty"`
	VulnLow      int               `json:"vuln_low,omitempty"`
	VulnAffected []VulnAffectedPkg `json:"vuln_affected,omitempty"`
	HasVulnData  bool              `json:"-"`

	// Load-bearing
	LoadBearing []LoadBearingEntry `json:"load_bearing,omitempty"`
}

StatsReport holds all data for the stats terminal output.

JSON tags are provided as a convenience for future callers that want to encode this value directly. Note that the CLI's `stats -f json` uses a re-grouped wire format (nested overview/maintenance/licenses/vulnerabilities sections) rather than the flat layout here, so the tags below are not used by the CLI today; see MaintenanceSummary / MaintenanceSummary etc. in internal/cli/stats.go for the actual wire schema.

type TreeOpts

type TreeOpts struct {
	Color    bool
	ShowMeta bool // display description/license/stars beneath each node
}

TreeOpts controls tree rendering behavior.

type TreeStats

type TreeStats struct {
	MaxDepth   int
	DirectDeps int
}

TreeStats holds statistics collected during tree rendering.

func WriteTree

func WriteTree(w io.Writer, g *dag.DAG, roots []string, opts TreeOpts) TreeStats

WriteTree renders a DAG as an indented dependency tree.

type VulnAffectedPkg added in v1.5.0

type VulnAffectedPkg struct {
	Package  string `json:"package"`
	Severity string `json:"severity"`
}

VulnAffectedPkg describes a package with a vulnerability.

Jump to

Keyboard shortcuts

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