Help us shape the future of Go! Take the 2026 Go Developer Survey

ui

package
v1.0.0 Latest Latest
Warning

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

Go to latest
Published: May 10, 2026 License: MIT Imports: 23 Imported by: 0

Documentation

Overview

Package ui provides terminal user interface components for scoreline.

Package ui provides rendering functions for the terminal user interface.

Index

Constants

View Source
const (
	DefaultDialogMaxWidth  = 104
	DefaultDialogMaxHeight = 39
)

Dialog sizing constants (30% larger for better readability).

View Source
const (
	StandingsDialogID  = "standings"
	FormationsDialogID = "formations"
	StatisticsDialogID = "statistics"
)

Dialog IDs

View Source
const (
	CardSymbolYellow = "▪" // Small square for yellow cards
	CardSymbolRed    = "■" // Filled square for red cards
)

Card symbols - consistent across all views

View Source
const ReplayLinkIndicator = "[▶REPLAY]"

ReplayLinkIndicator is the visual indicator for replay links.

View Source
const ReplayLinkIndicatorAlt = "[replay]"

ReplayLinkIndicatorAlt is an alternative ASCII indicator for terminals without emoji.

View Source
const SpinnerTickInterval = 70 * time.Millisecond

SpinnerTickInterval is the unified tick rate for all spinners (70ms ≈ 14 fps). This balances smooth animation with keyboard responsiveness.

Variables

This section is empty.

Functions

func AdaptiveGradientColors

func AdaptiveGradientColors() (startHex, endHex string)

AdaptiveGradientColors returns the appropriate gradient start/end hex colors based on the terminal background (light or dark). This is a convenience wrapper around design.AdaptiveGradientColors.

func CreateGoalLinkDisplay

func CreateGoalLinkDisplay(goalText, replayURL string) string

CreateGoalLinkDisplay creates a display string for a goal with replay link. Returns the text with hyperlink if available, or plain text if not. If the terminal doesn't support hyperlinks OR no URL is provided, returns the original goalText unchanged (no visible difference).

func DialogBadge

func DialogBadge(value string) string

DialogBadge wraps a value with a subtle background.

func DialogBadgeHighlight

func DialogBadgeHighlight(value string) string

DialogBadgeHighlight wraps a value with a highlighted background.

func DialogSize

func DialogSize(screenWidth, screenHeight, contentWidth, contentHeight int) (width, height int)

DialogSize calculates appropriate dialog dimensions based on content and screen size.

func FilterInputStyles

func FilterInputStyles() (cursorStyle, promptStyle lipgloss.Style)

FilterInputStyles returns cursor and prompt styles for list filter input. Cursor: neon cyan (solid color), Prompt: neon red to match theme.

func Hyperlink(text, url string) string

Hyperlink creates a terminal hyperlink using OSC 8 escape sequences. Falls back to plain text with URL suffix if terminal doesn't support OSC 8.

func HyperlinkWithFallback

func HyperlinkWithFallback(text, url, fallbackIndicator string) string

HyperlinkWithFallback creates a hyperlink with a visible fallback indicator. If OSC 8 is supported, returns a clickable link. Otherwise, returns text with a link indicator like [📹].

func IsValidReplayURL

func IsValidReplayURL(url string) bool

IsValidReplayURL validates that a URL is a valid HTTP/HTTPS URL and not a marker. Returns true only for valid http:// or https:// URLs. Filters out empty strings, "__NOT_FOUND__" markers, and invalid URL schemes.

func MakeGoalLinkKey

func MakeGoalLinkKey(matchID, minute int) string

MakeGoalLinkKey creates a key for the goal links map.

func NewMatchListDelegate

func NewMatchListDelegate() list.DefaultDelegate

NewMatchListDelegate creates a custom list delegate for match items. Height is set to 3 to accommodate title + 2-line description (with KO time). Uses Neon Gradient styling: red title, cyan description on selection.

func OpenURL

func OpenURL(url string) error

OpenURL opens a URL in the default browser. Use this as a fallback when OSC 8 hyperlinks aren't supported.

func RenderCricketArchivesView

func RenderCricketArchivesView(width, height int, matches []cricket.Match, selected int, details *cricket.MatchDetails, loading bool, lastError string, bannerType constants.StatusBannerType) string

RenderCricketArchivesView renders locally indexed Cricsheet archive matches.

func RenderCricketLiveView

func RenderCricketLiveView(width, height int, matches []cricket.Match, selected int, details *cricket.MatchDetails, bannerType constants.StatusBannerType) string

RenderCricketLiveView renders a cricket-native live dashboard.

func RenderCricketMenu

func RenderCricketMenu(width, height, selected int, bannerType constants.StatusBannerType, animatedLogo *logo.AnimatedLogo) string

RenderCricketMenu renders the cricket main menu.

func RenderCricketSettingsView

func RenderCricketSettingsView(width, height int, state *CricketSettingsState, apiKeyConfigured bool, bannerType constants.StatusBannerType) string

RenderCricketSettingsView renders cricket-specific settings.

func RenderDialogFrame

func RenderDialogFrame(title, content string, width, height int) string

RenderDialogFrame wraps content in a dialog frame with title bar.

func RenderDialogFrameWithHelp

func RenderDialogFrameWithHelp(title, content, help string, width, height int) string

RenderDialogFrameWithHelp wraps content in a dialog frame with title bar and help text.

func RenderDialogTitleBar

func RenderDialogTitleBar(title string, width int) string

RenderDialogTitleBar creates a full-width title bar with background.

func RenderGradientText

func RenderGradientText(text string) string

RenderGradientText applies a gradient (cyan to red) to multi-line text. Exported wrapper for external use.

func RenderLiveMatchesListPanel

func RenderLiveMatchesListPanel(width, height int, listModel list.Model, upcomingMatches []MatchDisplay) string

RenderLiveMatchesListPanel renders the left panel using bubbletea list component.

func RenderMainMenu

func RenderMainMenu(width, height, selected int, sp spinner.Model, randomSpinner *RandomCharSpinner, loading bool, bannerType constants.StatusBannerType, animatedLogo *logo.AnimatedLogo) string

RenderMainMenu renders the soccer main menu view with navigation options. width and height specify the terminal dimensions. selected indicates which menu item is currently selected (0-indexed). sp is the spinner model to display when loading (for other views). randomSpinner is the random character spinner for main view. loading indicates if the spinner should be shown. bannerType determines what status banner (if any) to display at the top. animatedLogo is the animated logo instance for the main view.

func RenderMatchDetails

func RenderMatchDetails(cfg MatchDetailsConfig) (headerContent, scrollableContent string)

RenderMatchDetails renders match details content, returning header and scrollable content separately. This unified function is used by both live and stats views.

func RenderMatchDetailsPanel

func RenderMatchDetailsPanel(width, height int, details *api.MatchDetails) string

RenderMatchDetailsPanel is an exported version for debug scripts.

func RenderMultiPanelViewWithList

func RenderMultiPanelViewWithList(width, height int, listModel list.Model, details *api.MatchDetails, liveUpdates []string, sp spinner.Model, loading bool, randomSpinner *RandomCharSpinner, viewLoading bool, leaguesLoaded int, totalLeagues int, pollingSpinner *RandomCharSpinner, isPolling bool, upcomingMatches []MatchDisplay, goalLinks GoalLinksMap, bannerType constants.StatusBannerType, lastError string) string

RenderMultiPanelViewWithList renders the live matches view with list component.

func RenderPlaceholderView

func RenderPlaceholderView(width, height int, title, message, help string, bannerType constants.StatusBannerType) string

RenderPlaceholderView renders a simple sport-native placeholder screen.

func RenderSettingsView

func RenderSettingsView(width, height int, state *SettingsState, bannerType constants.StatusBannerType) string

RenderSettingsView renders the settings view for league customization. Uses minimal styling consistent with the rest of the app (red/cyan neon theme). bannerType determines what status banner (if any) to display at the top.

func RenderSportSelector

func RenderSportSelector(width, height, selected int, bannerType constants.StatusBannerType, animatedLogo *logo.AnimatedLogo) string

RenderSportSelector renders the first-run sport selection menu.

func RenderStatsListPanel

func RenderStatsListPanel(width, height int, finishedList list.Model, dateRange int, rightPanelFocused bool) string

RenderStatsListPanel renders the left panel for stats view.

func RenderStatsViewWithList

func RenderStatsViewWithList(width, height int, finishedList list.Model, details *api.MatchDetails, randomSpinner *RandomCharSpinner, viewLoading bool, dateRange int, daysLoaded int, totalDays int, goalLinks GoalLinksMap, bannerType constants.StatusBannerType, detailsViewport *viewport.Model, rightPanelFocused bool, scrollOffset int, lastError string) string

RenderStatsViewWithList renders the stats view with list component.

func SpinnerStyle

func SpinnerStyle() lipgloss.Style

SpinnerStyle returns the style for the spinner.

func SpinnerTick

func SpinnerTick() tea.Cmd

SpinnerTick returns a command that generates a TickMsg after the standard interval. This is the ONLY function that should create spinner ticks - ensures single tick chain.

func ToMatchListItems

func ToMatchListItems(matches []MatchDisplay) []list.Item

ToMatchListItems converts a slice of MatchDisplay to list items.

func Truncate

func Truncate(text string, width int) string

Truncate truncates text to fit the specified width, appending "..." if truncated.

Types

type CricketSettingsState

type CricketSettingsState struct {
	Settings data.CricketSettings
	Cursor   int
	Saved    bool
	Editing  bool
	EditRow  int
	EditText string
}

CricketSettingsState holds editable cricket-specific settings.

func NewCricketSettingsState

func NewCricketSettingsState() *CricketSettingsState

NewCricketSettingsState loads cricket settings for the settings view.

func (*CricketSettingsState) Adjust

func (s *CricketSettingsState) Adjust(delta int)

Adjust changes numeric settings on the selected row.

func (*CricketSettingsState) BackspaceEditText

func (s *CricketSettingsState) BackspaceEditText()

BackspaceEditText removes the last byte from the active editor.

func (*CricketSettingsState) BeginEdit

func (s *CricketSettingsState) BeginEdit()

BeginEdit starts text editing for list-style settings.

func (*CricketSettingsState) CancelEdit

func (s *CricketSettingsState) CancelEdit()

CancelEdit exits the active text editor without applying changes.

func (*CricketSettingsState) CommitEdit

func (s *CricketSettingsState) CommitEdit()

CommitEdit saves the active text editor into settings.

func (*CricketSettingsState) InsertEditText

func (s *CricketSettingsState) InsertEditText(text string)

InsertEditText appends typed text to the active editor.

func (*CricketSettingsState) Move

func (s *CricketSettingsState) Move(delta int)

Move moves the selected settings row.

func (*CricketSettingsState) Save

func (s *CricketSettingsState) Save() error

Save persists cricket-specific settings without changing soccer settings.

func (*CricketSettingsState) Toggle

func (s *CricketSettingsState) Toggle()

Toggle toggles a format row.

type Dialog

type Dialog interface {
	// ID returns the unique identifier of the dialog.
	ID() string
	// Update processes a message and returns the updated dialog and an optional action.
	Update(msg tea.Msg) (Dialog, DialogAction)
	// View renders the dialog content within the specified dimensions.
	View(width, height int) string
}

Dialog is a component that can be displayed as an overlay on top of the UI.

type DialogAction

type DialogAction any

DialogAction represents an action returned by a dialog after handling a message.

type DialogActionClose

type DialogActionClose struct{}

DialogActionClose signals that the dialog should be closed.

type DialogOverlay

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

DialogOverlay manages multiple dialogs as an overlay stack.

func NewDialogOverlay

func NewDialogOverlay() *DialogOverlay

NewDialogOverlay creates a new DialogOverlay instance.

func (*DialogOverlay) CloseDialog

func (o *DialogOverlay) CloseDialog(dialogID string)

CloseDialog removes the dialog with the specified ID from the stack.

func (*DialogOverlay) CloseFrontDialog

func (o *DialogOverlay) CloseFrontDialog()

CloseFrontDialog closes the front (topmost) dialog in the stack.

func (*DialogOverlay) ContainsDialog

func (o *DialogOverlay) ContainsDialog(dialogID string) bool

ContainsDialog checks if a dialog with the specified ID exists.

func (*DialogOverlay) FrontDialog

func (o *DialogOverlay) FrontDialog() Dialog

FrontDialog returns the front (topmost) dialog, or nil if there are no dialogs.

func (*DialogOverlay) HasDialogs

func (o *DialogOverlay) HasDialogs() bool

HasDialogs checks if there are any active dialogs.

func (*DialogOverlay) OpenDialog

func (o *DialogOverlay) OpenDialog(dialog Dialog)

OpenDialog adds a new dialog to the stack.

func (*DialogOverlay) Update

func (o *DialogOverlay) Update(msg tea.Msg) DialogAction

Update handles message routing to the front dialog. Returns the action from the dialog, if any.

func (*DialogOverlay) View

func (o *DialogOverlay) View(width, height int) string

View renders the overlay with the front dialog centered.

type FormationsDialog

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

FormationsDialog displays the match formations for both teams.

func NewFormationsDialog

func NewFormationsDialog(
	homeTeam, awayTeam string,
	homeFormation, awayFormation string,
	homeStarting, awayStarting []api.PlayerInfo,
) *FormationsDialog

NewFormationsDialog creates a new formations dialog.

func (*FormationsDialog) ID

func (d *FormationsDialog) ID() string

ID returns the dialog identifier.

func (*FormationsDialog) Update

func (d *FormationsDialog) Update(msg tea.Msg) (Dialog, DialogAction)

Update handles input for the formations dialog.

func (*FormationsDialog) View

func (d *FormationsDialog) View(width, height int) string

View renders the formations view.

type GoalLinksMap

type GoalLinksMap map[string]string

GoalLinksMap maps goal keys (matchID:minute) to replay URLs.

func (GoalLinksMap) GetReplayURL

func (g GoalLinksMap) GetReplayURL(matchID, minute int) string

GetReplayURL returns the replay URL for a goal if available.

type LeagueListDelegate

type LeagueListDelegate struct {
	list.DefaultDelegate
}

LeagueListDelegate is a custom delegate that renders checkboxes separately from titles. This fixes the filter cursor positioning issue by keeping the checkbox out of the title.

func NewLeagueListDelegate

func NewLeagueListDelegate() LeagueListDelegate

NewLeagueListDelegate creates a custom list delegate for league selection. Height is set to 2 to show league name (with checkbox) and country. Uses same red/cyan neon styling as match delegate for consistency. The checkbox is rendered separately from the title to fix filter cursor positioning.

func (LeagueListDelegate) HighlightMatches

func (d LeagueListDelegate) HighlightMatches(text, filterValue string) string

HighlightMatches highlights matching text in the title using FilterMatch style.

func (LeagueListDelegate) Render

func (d LeagueListDelegate) Render(w io.Writer, m list.Model, index int, item list.Item)

Render renders a league list item with a checkbox prefix. The checkbox is rendered separately from the title to prevent filter cursor shift.

type LeagueListItem

type LeagueListItem struct {
	League   data.LeagueInfo
	Selected bool
}

LeagueListItem implements the list.Item interface for league selection.

func (LeagueListItem) Description

func (l LeagueListItem) Description() string

Description returns the country.

func (LeagueListItem) FilterValue

func (l LeagueListItem) FilterValue() string

FilterValue returns the value used for filtering (league name + country).

func (LeagueListItem) Title

func (l LeagueListItem) Title() string

Title returns the league name with selection indicator.

type MatchDetailsConfig

type MatchDetailsConfig struct {
	Width, Height int
	Details       *api.MatchDetails
	GoalLinks     GoalLinksMap

	// View-specific features
	ShowStatistics bool // Stats view only
	ShowHighlights bool // Stats view only

	// Live view state
	LiveUpdates    []string
	PollingSpinner *RandomCharSpinner
	IsPolling      bool
	Loading        bool

	// Stats view state
	Focused bool
}

MatchDetailsConfig holds all parameters for rendering match details.

type MatchDisplay

type MatchDisplay struct {
	api.Match
}

MatchDisplay wraps a match with display information for rendering.

func (MatchDisplay) Description

func (m MatchDisplay) Description() string

Description returns a formatted description for the match. Shows score, league, live time on first line; KO time on second line.

func (MatchDisplay) Title

func (m MatchDisplay) Title() string

Title returns a formatted title for the match.

type MatchListItem

type MatchListItem struct {
	Match   api.Match
	Display MatchDisplay
}

MatchListItem implements the list.Item interface for matches.

func (MatchListItem) Description

func (m MatchListItem) Description() string

Description returns the match description for the list item.

func (MatchListItem) FilterValue

func (m MatchListItem) FilterValue() string

FilterValue returns the value to use for filtering. Returns team names for searching (e.g., "Arsenal vs Chelsea").

func (MatchListItem) Title

func (m MatchListItem) Title() string

Title returns the match title for the list item.

type RandomCharSpinner

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

RandomCharSpinner is a custom spinner that displays a wave of random characters. Note: Spinners do NOT self-tick. The app manages the tick chain centrally.

func NewRandomCharSpinner

func NewRandomCharSpinner() *RandomCharSpinner

NewRandomCharSpinner creates a new random character spinner.

func (*RandomCharSpinner) SetWidth

func (r *RandomCharSpinner) SetWidth(width int)

SetWidth sets the width of the spinner and resizes the display buffer.

func (*RandomCharSpinner) Tick

func (r *RandomCharSpinner) Tick()

Tick advances the spinner animation - randomizes all characters for trendy effect. Does NOT return a tick command - the app manages the tick chain.

func (*RandomCharSpinner) View

func (r *RandomCharSpinner) View() string

View renders the spinner with gradient colors.

type SettingsState

type SettingsState struct {
	List          list.Model        // List component for league navigation
	Selected      map[int]bool      // Map of league ID -> selected
	Leagues       []data.LeagueInfo // All leagues for current region
	AllLeagues    []data.LeagueInfo // All leagues across all regions
	Regions       []string          // Available regions
	CurrentRegion int               // Index of current region
	HasChanges    bool              // Whether there are unsaved changes
}

SettingsState holds the state for the settings view.

func NewSettingsState

func NewSettingsState() *SettingsState

NewSettingsState creates a new settings state with current saved preferences.

func (*SettingsState) NextRegion

func (s *SettingsState) NextRegion()

NextRegion switches to the next region (with wraparound).

func (*SettingsState) PreviousRegion

func (s *SettingsState) PreviousRegion()

PreviousRegion switches to the previous region (with wraparound).

func (*SettingsState) Save

func (s *SettingsState) Save() error

Save persists the current selection to settings.yaml.

func (*SettingsState) SelectedCount

func (s *SettingsState) SelectedCount() int

SelectedCount returns the number of selected leagues.

func (*SettingsState) Toggle

func (s *SettingsState) Toggle()

Toggle toggles the selection state of the currently highlighted league.

type StandingsDialog

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

StandingsDialog displays the league standings table for a match.

func NewStandingsDialog

func NewStandingsDialog(leagueName string, standings []api.LeagueTableEntry, homeTeamID, awayTeamID int) *StandingsDialog

NewStandingsDialog creates a new standings dialog.

func (*StandingsDialog) ID

func (d *StandingsDialog) ID() string

ID returns the dialog identifier.

func (*StandingsDialog) Update

func (d *StandingsDialog) Update(msg tea.Msg) (Dialog, DialogAction)

Update handles input for the standings dialog.

func (*StandingsDialog) View

func (d *StandingsDialog) View(width, height int) string

View renders the standings table.

type StatisticsDialog

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

StatisticsDialog displays all match statistics in a comparison view.

func NewStatisticsDialog

func NewStatisticsDialog(homeTeam, awayTeam string, homeScore, awayScore int, statistics []api.MatchStatistic) *StatisticsDialog

NewStatisticsDialog creates a new statistics dialog.

func (*StatisticsDialog) ID

func (d *StatisticsDialog) ID() string

ID returns the dialog identifier.

func (*StatisticsDialog) Update

func (d *StatisticsDialog) Update(msg tea.Msg) (Dialog, DialogAction)

Update handles input for the statistics dialog.

func (*StatisticsDialog) View

func (d *StatisticsDialog) View(width, height int) string

View renders the statistics comparison.

type TickMsg

type TickMsg struct{}

TickMsg is the unified message type for all spinner updates. Only ONE tick chain should exist at any time to prevent message queue flooding.

Directories

Path Synopsis
Package design provides reusable visual design components.
Package design provides reusable visual design components.
Package logo renders a SCORELINE wordmark in a stylized way.
Package logo renders a SCORELINE wordmark in a stylized way.

Jump to

Keyboard shortcuts

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