Documentation
¶
Overview ¶
Package ui provides the interactive Terminal User Interface (TUI) for gemtracker using BubbleTea.
The TUI displays gem dependency analysis across multiple views (gem list, details, search, vulnerabilities, project info) with real-time background updates for gem health, outdated versions, and health scoring. It handles user keyboard input, manages application state, and coordinates async data fetching.
Index ¶
- Constants
- Variables
- type AnalysisCompleteMsg
- type CVECompleteMsg
- type CVELoadFromCacheMsg
- type CVEProgressMsg
- type CVEScanStartedMsg
- type DependencyCompleteMsg
- type GemReport
- type GitHubBatchCompleteMsg
- type HealthCompleteMsg
- type HealthItemMsg
- type HealthRateLimitedMsg
- type Model
- type OutdatedCompleteMsg
- type OutdatedItemMsg
- type ProgressMsg
- type ProgressTickMsg
- type ReportData
- type ReportGenerator
- type SpinnerTickMsg
- type StageUpdateMsg
- type VersionCheckMsg
- type ViewMode
Constants ¶
const ( ColorBg = "235" // #262626 - base background ColorSurface = "237" // #3a3a3a - cards/panels ColorBorder = "240" // #585858 - default borders ColorBorderActive = "74" // #5fafd7 - focused border (slate blue) ColorText = "252" // #d0d0d0 - primary text ColorTextMuted = "244" // #808080 - secondary text ColorTextSubtle = "240" // #585858 - hints, tree connectors ColorPrimary = "74" // #5fafd7 - app accent ColorSuccess = "71" // #5faf5f - latest/up to date ColorWarning = "178" // #d7af00 - outdated ColorDanger = "160" // #d70000 - vulnerable ColorSelected = "24" // #005f87 - selected row background ColorTabActive = "74" // same as Primary ColorTabInactive = "244" // same as TextMuted )
Color palette - dark slate/blue theme (256-color ANSI)
const ( FixedChrome = 3 // header (1) + tabbar (1) + statusbar (1) HeaderHeight = 3 TabBarHeight = 1 StatusBarHeight = 1 )
Layout constants
Variables ¶
var AppHeaderStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorPrimary)). Bold(true). Background(lipgloss.Color(ColorSurface)). Padding(0, 2)
var AppVersionStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted))
var BadgeCriticalDotStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorDanger))
var BadgeErrorStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorDanger))
var BadgeHealthyDotStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorSuccess))
var BadgeLoadingStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted))
var BadgeOKStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorSuccess))
var BadgeOutdatedStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorWarning))
var BadgeVulnerableStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorDanger))
var BadgeWarningDotStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorWarning))
var ErrorBoxStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color(ColorDanger)). Foreground(lipgloss.Color(ColorDanger)). Bold(true). Padding(1, 2)
var ErrorTitleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorDanger)). Bold(true)
var InputBoxStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color(ColorBorder)). Padding(0, 1)
var KeyHintDescStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted))
var KeyHintKeyStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorPrimary)). Bold(true)
var LoadingMessageStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted))
var PanelBorderActiveStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color(ColorBorderActive)). Padding(0, 1)
var PanelBorderStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color(ColorBorder)). Padding(0, 1)
var PanelTitleStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorPrimary)). Bold(true)
var ProjectPathStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted)). Padding(0, 2)
var RowMutedStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted))
var RowSelectedStyle = lipgloss.NewStyle(). Background(lipgloss.Color(ColorSelected)). Foreground(lipgloss.Color(ColorText)). Bold(true)
var SearchBoxStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color(ColorBorderActive)). Padding(0, 1)
var SearchPromptStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorPrimary)). Bold(true)
var SpinnerStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorPrimary)). Bold(true)
var StatusBarStyle = lipgloss.NewStyle(). Background(lipgloss.Color(ColorSurface)). Foreground(lipgloss.Color(ColorTextMuted)). Padding(0, 2)
var TabActiveStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTabActive)). Bold(true). Padding(0, 2). Background(lipgloss.Color(ColorSurface)). Underline(true)
var TabStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTabInactive)). Padding(0, 2). Background(lipgloss.Color(ColorSurface))
var TableHeaderStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted)). Bold(true). BorderBottom(true). BorderStyle(lipgloss.NormalBorder()). BorderForeground(lipgloss.Color(ColorBorder))
var TreeConnectorStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextSubtle))
var TreeVersionStyle = lipgloss.NewStyle(). Foreground(lipgloss.Color(ColorTextMuted))
var UpdateBarStyle = lipgloss.NewStyle(). Background(lipgloss.Color(ColorSurface)). Foreground(lipgloss.Color(ColorWarning)). Padding(0, 2)
Functions ¶
This section is empty.
Types ¶
type AnalysisCompleteMsg ¶
type AnalysisCompleteMsg struct {
Result *gemfile.AnalysisResult
Error error
OutdatedChecker *gemfile.OutdatedChecker
}
type CVECompleteMsg ¶ added in v1.1.5
type CVECompleteMsg struct {
Vulnerabilities []*gemfile.Vulnerability
Error error
}
type CVELoadFromCacheMsg ¶ added in v1.1.5
type CVEProgressMsg ¶ added in v1.1.5
type CVEScanStartedMsg ¶ added in v1.1.5
type CVEScanStartedMsg struct{}
type DependencyCompleteMsg ¶
type DependencyCompleteMsg struct {
Result *gemfile.DependencyResult
Error error
}
type GemReport ¶ added in v1.1.2
type GemReport struct {
// Name is the gem name
Name string
// Version is the currently installed version
Version string
// Groups lists the bundle groups this gem belongs to
Groups []string
// IsFirstLevel indicates whether this is a directly required gem
IsFirstLevel bool
// IsOutdated indicates whether a newer version is available
IsOutdated bool
// LatestVersion is the latest available version (if IsOutdated is true)
LatestVersion string
// IsVulnerable indicates whether known CVEs affect this version
IsVulnerable bool
// VulnerabilityInfo contains CVE ID and description (if IsVulnerable is true)
VulnerabilityInfo string
// HomepageURL is the gem's homepage or source code URL
HomepageURL string
// Description is the gem description from rubygems.org
Description string
}
GemReport represents a gem's details in the generated report, including status and metadata.
type GitHubBatchCompleteMsg ¶ added in v1.1.2
type GitHubBatchCompleteMsg struct {
Error error
}
type HealthCompleteMsg ¶ added in v1.1.0
type HealthCompleteMsg struct{}
type HealthItemMsg ¶ added in v1.1.0
type HealthRateLimitedMsg ¶ added in v1.1.0
type HealthRateLimitedMsg struct {
StoppedAt string // gem name where rate limiting occurred
}
type Model ¶
type Model struct {
// Window dimensions (updated on resize)
Width int
Height int
// CurrentView is the screen currently being displayed
CurrentView ViewMode
// ActiveTab persists the tab (ViewGemList, ViewUpgradeable, ViewCVE, ViewProjectInfo)
// across navigation away and back, restoring state when returning
ActiveTab ViewMode
// Data
AnalysisResult *gemfile.AnalysisResult
DependencyResult *gemfile.DependencyResult
// Gem List screen state
FirstLevelGems []*gemfile.GemStatus
GemListCursor int
GemListOffset int
UnfilteredGems []*gemfile.GemStatus // All first-level gems (for filter operations)
SelectedGroups map[string]bool // Groups to filter by (if empty, show all)
ShowOnlyUpgradable bool // Filter to show only gems with updates
AvailableGroups []string // All unique groups found in gems
// Filter Menu screen state
FilterMenuCursor int // Position in the filter menu (0 = upgradable, 1+ = groups)
// Gem Detail screen state
SelectedGem *gemfile.GemStatus
DetailSection int // 0 = forward deps, 1 = reverse deps
DetailForwardOffset int
DetailReverseOffset int
DetailTreeCursor int // Selected line in current tree panel
DetailForwardLines []string // Gem names at each line in forward tree
DetailReverseLines []string // Gem names at each line in reverse tree
DetailCurrentlyViewing *gemfile.GemStatus // The gem currently being viewed in detail (may differ from SelectedGem)
DetailCurrentReverseDep *gemfile.DependencyInfo // Current gem's reverse dependencies
// Search screen state
SearchInput textinput.Model
SearchQuery string
SearchResults []*gemfile.GemStatus
SearchCursor int
SearchOffset int
// Upgradeable screen state
UpgradeableGems []*gemfile.GemStatus
UpgradeableFrameworkGems []*gemfile.GemStatus
UpgradeableTransitiveDeps []*gemfile.GemStatus // Transitive dependency gems that can be upgraded
UpgradeableCursor int
UpgradeableOffset int
// CVE screen state
VulnerableGems []*gemfile.GemStatus
CVECursor int
CVEOffset int
CVEVulnerabilities []*gemfile.Vulnerability // Actual vulnerability data from OSV.dev
LastGemsSignature string // SHA256 of last scanned gems
CVERefreshInProgress bool // Is a CVE refresh happening in background?
CVELastScanTime time.Time // When was CVE data last scanned?
CVECacheLoadedAt time.Time // When was cache loaded?
CVECacheTTL time.Duration // Default: 1 hour
CVELastError string // Last error message if scan failed
// Project Info screen state
RubyVersion string
RailsVersion string
BundleVersion string
OtherFramework string // For non-Rails projects
TotalGems int
FirstLevelCount int
TransitiveDeps int
FrameworkDetected string // The name of the framework detected
// Path selection modal
PathInput textinput.Model
// Loading state
Loading bool
LoadingMessage string
AnimationFrame int
AnalysisStage string // "parsing", "checking-updates", "scanning-cves"
AnalysisPercentage int // 0-100
AnalysisCurrentCount int // Current item in stage
AnalysisTotalCount int // Total items for stage
// Health loading state
HealthLoading bool
HealthRateLimited bool
HealthLoadedCount int
HealthTotalCount int
HealthPending []*gemfile.GemStatus // Queue for sequential fetching
HealthChecker *gemfile.HealthChecker
OutdatedChecker *gemfile.OutdatedChecker // Reused for health data extraction
// Outdated checking state
OutdatedLoading bool
OutdatedPending []*gemfile.GemStatus // Queue for sequential fetching
OutdatedErrorCount int
OutdatedRateLimited bool
// Error state
ErrorMessage string
// Project state
ProjectPath string
GemfileLockPath string
GemfileSource string // "Gemfile.lock", "gems.locked", ".gemspec", etc.
// App metadata
Version string
Commit string
Date string
NewVersionAvailable string // empty = no update, otherwise holds latest version tag
Quitting bool
NoCache bool // Skip cache and force fresh analysis
Verbose bool // Enable verbose logging
}
Model is the central BubbleTea model that manages all TUI state, including the current screen, gem data, navigation, filtering, async operations (health checks, outdated version checks), and error states. It implements the tea.Model interface and coordinates all UI updates.
func NewModel ¶
NewModel creates a new TUI Model and loads the project from the given path. If the path contains a Gemfile.lock, gems.locked, or .gemspec file, analysis starts automatically. The version, commit, and date are displayed in the UI header.
type OutdatedCompleteMsg ¶ added in v1.1.0
type OutdatedCompleteMsg struct{}
type OutdatedItemMsg ¶ added in v1.1.0
type ProgressMsg ¶ added in v1.0.6
type ProgressTickMsg ¶ added in v1.0.6
type ProgressTickMsg struct{}
type ReportData ¶ added in v1.1.2
type ReportData struct {
// GeneratedAt is the timestamp when the report was generated
GeneratedAt string
// ProjectPath is the path to the analyzed Ruby project
ProjectPath string
// TotalGems is the count of all gems (first-level and transitive)
TotalGems int
// FirstLevelGems is the count of directly required gems
FirstLevelGems int
// OutdatedGems lists gems with available updates
OutdatedGems []*GemReport
// VulnerableGems lists gems with known CVEs
VulnerableGems []*GemReport
// AllGems lists all gems in the project
AllGems []*GemReport
// Summary is a brief text summary of findings
Summary string
// OutdatedCount is the count of gems with updates available
OutdatedCount int
// VulnerableCount is the count of gems with known vulnerabilities
VulnerableCount int
}
ReportData holds structured gem analysis data suitable for export in multiple formats.
type ReportGenerator ¶ added in v1.1.2
type ReportGenerator struct {
// contains filtered or unexported fields
}
ReportGenerator generates gem dependency reports in multiple formats (text, CSV, JSON) for non-interactive CI/CD integration and compliance use cases.
func NewReportGenerator ¶ added in v1.1.2
func NewReportGenerator(projectPath string, noCache, verbose bool) *ReportGenerator
NewReportGenerator creates a new ReportGenerator for the given project path.
func (*ReportGenerator) Generate ¶ added in v1.1.2
func (rg *ReportGenerator) Generate(format, outputPath string) error
Generate analyzes the project and generates a report in the specified format (text, csv, or json). If outputPath is empty, writes to stdout. Returns an error if analysis or report writing fails.
type SpinnerTickMsg ¶
type SpinnerTickMsg struct{}
type StageUpdateMsg ¶ added in v1.0.6
type StageUpdateMsg struct {
Stage string // "parsing", "checking-updates", "scanning-cves"
CurrentCount int // Current gems processed
TotalCount int // Total gems to process
Percentage int // 0-100
Result *gemfile.AnalysisResult // Accumulated results so far
OutdatedGems []*gemfile.GemStatus // Updated gems with version info
VulnerableGems []*gemfile.GemStatus // Updated with CVE info
}
type VersionCheckMsg ¶ added in v1.0.3
type ViewMode ¶
type ViewMode int
ViewMode represents the current screen being displayed in the TUI.
const ( // ViewLoading displays the loading/progress screen while analyzing the project ViewLoading ViewMode = iota // ViewGemList displays all first-level gems (directly required dependencies) ViewGemList // ViewGemDetail displays forward and reverse dependencies for a selected gem ViewGemDetail // ViewSearch displays search results for a gem query ViewSearch // ViewUpgradeable displays gems with available updates, organized by type ViewUpgradeable // ViewCVE displays vulnerable gems with CVE information ViewCVE // ViewProjectInfo displays project metadata (Ruby version, framework, gem counts, etc.) ViewProjectInfo // ViewFilterMenu displays options to filter gems by group or upgradability ViewFilterMenu // ViewSelectPath displays an input prompt to select a project directory ViewSelectPath // ViewError displays an error message ViewError )