Documentation
¶
Overview ¶
Package ui owns every visual rendering concern: Lip Gloss styles, constructor color mappings, and Bubble Tea model definitions. Nothing outside this package prints to the terminal.
Index ¶
- Constants
- Variables
- func ConstructorColor(teamName string, seasonYear int) lipgloss.Color
- func FormatDelta(delta int, isNew bool) string
- func FormatPoints(pts float64) string
- func FormatPointsDelta(delta float64) string
- func PositionStyle(pos int) lipgloss.Style
- func ProgressBar(current, total, width int) string
- func RenderBanner(subtitle string) string
- func RenderContextPanel(context string) string
- func RenderDashboard(race api.Race, s state.TrackerState, totalRounds int, ...) string
- func RenderHighlightPanel(year, round int, raceName, url string) string
- func RenderHint(title string, lines []string) string
- func RenderMessage(title, message string, borderColor lipgloss.Color) string
- func RenderRaceCard(t *data.TrackInfo) string
- func RenderRaceOverview(race api.Race, s state.TrackerState, totalRounds int, highlightReady bool) string
- func RenderRaceResultsTable(title string, results []api.RaceResult, seasonYear, limit int) string
- func RenderStandingsDeltaTable(title string, before, after []api.DriverStanding, seasonYear, limit int) string
- func RenderStandingsTable(title string, standings []api.DriverStanding, seasonYear, limit int) string
- func SetWidth(w int)
- func TeamStyle(teamName string, seasonYear int) lipgloss.Style
- func TermWidth() int
- type RaceCompleted
- type RaceDataLoaded
- type RaceModel
- type RaceModelOption
- type RacePhase
Constants ¶
const ( // Ferrari — always red ColorFerrariRed lipgloss.Color = "#DC0000" // Mercedes — silver/teal ColorMercedesTeal lipgloss.Color = "#00D2BE" ColorMercedesSilver lipgloss.Color = "#C0C0C0" // Red Bull Racing — blue spectrum ColorRedBullDarkBlue lipgloss.Color = "#1E41FF" // 2017-2022 ColorRedBullBrightBlue lipgloss.Color = "#3671C6" // 2023+ (darker Verstappen era) // McLaren — papaya orange (since 2021 rebrand; 2017-2020 was darker) ColorMcLarenPapaya lipgloss.Color = "#FF8000" ColorMcLarenOrange lipgloss.Color = "#FF7A00" // 2017-2020 slight variation // Alpine / Renault ColorRenaultYellow lipgloss.Color = "#FFF500" // 2017-2020 Renault ColorAlpineBlue lipgloss.Color = "#0090FF" // 2021 Alpine ColorAlpinePink lipgloss.Color = "#FF87BC" // 2022-2023 Alpine pink accent // Aston Martin / Racing Point / Force India ColorForceIndiaPink lipgloss.Color = "#F596C8" // 2017-2018 ColorRacingPointPink lipgloss.Color = "#F596C8" // 2019-2020 ColorAstonMartinGreen lipgloss.Color = "#358C75" // 2021+ // AlphaTauri / Toro Rosso / RB ColorToroRossoRed lipgloss.Color = "#C32B44" // 2017-2019 ColorAlphaTauriBlue lipgloss.Color = "#2B4562" // 2020-2023 ColorRBBlue lipgloss.Color = "#6692FF" // 2024+ Visa Cash App RB // Haas ColorHaasRed lipgloss.Color = "#E8002D" ColorHaasGrey lipgloss.Color = "#B6BABD" // Williams ColorWilliamsBlue lipgloss.Color = "#00A3E0" // 2017-2021 (navy/azure) ColorWilliamsAzure lipgloss.Color = "#37BEDD" // 2022+ (lighter variant) // Alfa Romeo / Sauber / Kick Sauber ColorAlfaRomeoRed lipgloss.Color = "#9B0000" ColorKickSauberGreen lipgloss.Color = "#52E252" // Accent colors used for panels and decorative elements. ColorRaceRed lipgloss.Color = "#FF1E00" // primary brand accent ColorGold lipgloss.Color = "#FFD700" // podium / winner ColorSilver lipgloss.Color = "#C0C0C0" // P2 ColorBronze lipgloss.Color = "#CD7F32" // P3 ColorDimGrey lipgloss.Color = "#555555" ColorMutedWhite lipgloss.Color = "#CCCCCC" ColorGreen lipgloss.Color = "#39FF14" // neon green — positive delta ColorOrangeWarn lipgloss.Color = "#FF8C00" // warning / neutral )
const ( MinWidth = 60 MaxWidth = 160 )
Variables ¶
var ( // Borders & panels StyleBannerBorder = lipgloss.NewStyle(). Border(lipgloss.ThickBorder()). BorderForeground(ColorRaceRed). Padding(0, 2). Bold(true) StylePanelBorder = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorDimGrey). Padding(0, 1) StyleRaceCardBorder = lipgloss.NewStyle(). Border(lipgloss.DoubleBorder()). BorderForeground(ColorMercedesTeal). Padding(0, 1) StyleHighlightBorder = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(lipgloss.Color("#9933FF")). Padding(0, 1) StyleHintBorder = lipgloss.NewStyle(). Border(lipgloss.NormalBorder()). BorderForeground(ColorOrangeWarn). Padding(0, 1) StyleErrorBorder = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorHaasRed). Padding(0, 1) StyleSuccessBorder = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(ColorGreen). Padding(0, 1) // Text StyleTitle = lipgloss.NewStyle(). Bold(true). Foreground(ColorRaceRed) StyleSubtitle = lipgloss.NewStyle(). Bold(true). Foreground(ColorMutedWhite) StyleLabel = lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#888888")). Width(14). Align(lipgloss.Right) StyleValue = lipgloss.NewStyle(). Foreground(ColorMutedWhite) StyleBold = lipgloss.NewStyle().Bold(true) StyleMuted = lipgloss.NewStyle(). Foreground(ColorDimGrey) StylePositionDeltaUp = lipgloss.NewStyle(). Bold(true). Foreground(ColorGreen) StylePositionDeltaDown = lipgloss.NewStyle(). Bold(true). Foreground(ColorHaasRed) StylePositionDeltaFlat = lipgloss.NewStyle(). Foreground(ColorDimGrey) StylePositionNew = lipgloss.NewStyle(). Bold(true). Foreground(ColorGold) StyleP1 = lipgloss.NewStyle().Bold(true).Foreground(ColorGold) StyleP2 = lipgloss.NewStyle().Bold(true).Foreground(ColorSilver) StyleP3 = lipgloss.NewStyle().Bold(true).Foreground(ColorBronze) StyleURL = lipgloss.NewStyle(). Foreground(lipgloss.Color("#5588FF")). Underline(true) StyleSpinner = lipgloss.NewStyle().Foreground(ColorRaceRed) )
var ActiveWidth = 80
ActiveWidth is the outer panel width (border + padding + content). Set this once at the start of each command invocation so all Render* calls in that command use a consistent, terminal-aware width.
Default: 80 (safe for most terminals).
Functions ¶
func ConstructorColor ¶
ConstructorColor returns the Lip Gloss color for a team name in a given season. If seasonYear is 0 the most recent branding is used.
func FormatDelta ¶
FormatDelta formats a standings position change for display.
- n > 0 → ↑n (green)
- n < 0 → ↓|n| (red)
- n == 0 → — (grey)
- isNew → NEW (gold)
func FormatPoints ¶
FormatPoints formats a float as an integer when it is a whole number.
func FormatPointsDelta ¶
FormatPointsDelta formats a signed float as "+25", "0", "-4" etc.
func PositionStyle ¶
PositionStyle returns the style appropriate for a finishing position.
func ProgressBar ¶
ProgressBar renders a [####----] ASCII progress bar.
func RenderBanner ¶
func RenderContextPanel ¶
func RenderDashboard ¶
func RenderDashboard( race api.Race, s state.TrackerState, totalRounds int, standings []api.DriverStanding, track *data.TrackInfo, ctx string, highlightReady bool, seasonYear int, expertMode bool, ) string
RenderDashboard composes all status panels into a single compact screen. Left column: race overview + race card. Right column: standings + context. expertMode adjusts the banner subtitle and is used by the caller to decide which standings snapshot (before vs. after round) to pass in.
func RenderHighlightPanel ¶
func RenderHint ¶
func RenderRaceCard ¶
func RenderRaceOverview ¶
func RenderRaceResultsTable ¶
func RenderRaceResultsTable(title string, results []api.RaceResult, seasonYear, limit int) string
func RenderStandingsDeltaTable ¶
func RenderStandingsDeltaTable( title string, before, after []api.DriverStanding, seasonYear, limit int, ) string
func RenderStandingsTable ¶
func RenderStandingsTable(title string, standings []api.DriverStanding, seasonYear, limit int) string
Types ¶
type RaceCompleted ¶
type RaceCompleted struct {
Results []api.RaceResult
Points []api.RaceResult
StandingsAfter []api.DriverStanding
NewState state.TrackerState
NextRaceName string
Err error
}
RaceCompleted carries all post-race data.
type RaceDataLoaded ¶
type RaceDataLoaded struct {
Race api.Race
Track *data.TrackInfo
Standings []api.DriverStanding
TotalRounds int
HighlightURL string
PreRaceCtx string
Err error
}
RaceDataLoaded carries all pre-race data needed for the briefing view.
type RaceModel ¶
type RaceModel struct {
// contains filtered or unexported fields
}
RaceModel is the root Bubble Tea model for the race flow.
func NewRaceModel ¶
func NewRaceModel(opts ...RaceModelOption) RaceModel
NewRaceModel constructs the model. loadFn and completeFn are async Tea commands.
type RaceModelOption ¶
type RaceModelOption func(*RaceModel)
RaceModelOption configures a RaceModel.
func WithCompleteFn ¶
func WithCompleteFn(fn func() tea.Msg) RaceModelOption
func WithLoadFn ¶
func WithLoadFn(fn func() tea.Msg) RaceModelOption
func WithOpenURL ¶
func WithOpenURL(fn func(string) error) RaceModelOption
func WithSeasonYear ¶
func WithSeasonYear(year int) RaceModelOption
type RacePhase ¶
type RacePhase int
const ( PhaseLoading RacePhase = iota PhaseBriefing // show race card, standings, highlight URL PhaseWatching // user opened browser — waiting for confirmation PhaseConfirming // "Mark as watched? [y/N]" PhaseCompleting // spinner while completing PhaseResults // show post-race results PhaseDone // clean exit PhaseQuit // user pressed Q/Esc )