ui

package
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Apr 12, 2026 License: MIT Imports: 11 Imported by: 0

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

View Source
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
)
View Source
const (
	MinWidth = 60
	MaxWidth = 160
)

Variables

View Source
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)
)
View Source
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

func ConstructorColor(teamName string, seasonYear int) lipgloss.Color

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

func FormatDelta(delta int, isNew bool) string

FormatDelta formats a standings position change for display.

  • n > 0 → ↑n (green)
  • n < 0 → ↓|n| (red)
  • n == 0 → — (grey)
  • isNew → NEW (gold)

func FormatPoints

func FormatPoints(pts float64) string

FormatPoints formats a float as an integer when it is a whole number.

func FormatPointsDelta

func FormatPointsDelta(delta float64) string

FormatPointsDelta formats a signed float as "+25", "0", "-4" etc.

func PositionStyle

func PositionStyle(pos int) lipgloss.Style

PositionStyle returns the style appropriate for a finishing position.

func ProgressBar

func ProgressBar(current, total, width int) string

ProgressBar renders a [####----] ASCII progress bar.

func RenderBanner

func RenderBanner(subtitle string) string

func RenderContextPanel

func RenderContextPanel(context string) string

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 RenderHighlightPanel(year, round int, raceName, url string) string

func RenderHint

func RenderHint(title string, lines []string) string

func RenderMessage

func RenderMessage(title, message string, borderColor lipgloss.Color) string

func RenderRaceCard

func RenderRaceCard(t *data.TrackInfo) string

func RenderRaceOverview

func RenderRaceOverview(
	race api.Race,
	s state.TrackerState,
	totalRounds int,
	highlightReady bool,
) string

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

func SetWidth

func SetWidth(w int)

SetWidth updates ActiveWidth, clamped to [MinWidth, MaxWidth].

func TeamStyle

func TeamStyle(teamName string, seasonYear int) lipgloss.Style

TeamStyle returns a Lip Gloss style with the constructor's brand foreground.

func TermWidth

func TermWidth() int

TermWidth returns the current stdout terminal width. Falls back to the COLUMNS env var, then to 80 (piped / CI context).

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.

func (RaceModel) Init

func (m RaceModel) Init() tea.Cmd

func (RaceModel) Update

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

func (RaceModel) View

func (m RaceModel) View() string

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
)

Jump to

Keyboard shortcuts

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