Adjust column widths for emphasis on matches

This commit is contained in:
Salastil
2025-11-22 23:25:47 -05:00
parent 5e179e84a3
commit 17f524332c
2 changed files with 58 additions and 17 deletions

View File

@@ -149,7 +149,7 @@ func New(debug bool) Model {
return fmt.Sprintf("#%d %s (%s) %s", st.StreamNo, st.Language, quality, st.Source) return fmt.Sprintf("#%d %s (%s) %s", st.StreamNo, st.Language, quality, st.Source)
}) })
m.status = fmt.Sprintf("Using API %s | Loading…", base) m.status = fmt.Sprintf("Using API %s | Loading sports and matches…", base)
return m return m
} }
@@ -179,13 +179,32 @@ func (m Model) renderMainView() string {
m.matches.View(m.styles, m.focus == focusMatches), m.matches.View(m.styles, m.focus == focusMatches),
m.streams.View(m.styles, m.focus == focusStreams), m.streams.View(m.styles, m.focus == focusStreams),
) )
status := m.styles.Status.Render(m.status) status := m.renderStatusLine()
if m.lastError != nil {
status = m.styles.Error.Render(fmt.Sprintf("⚠️ %v", m.lastError))
}
return lipgloss.JoinVertical(lipgloss.Left, cols, status, m.help.View(m.keys)) return lipgloss.JoinVertical(lipgloss.Left, cols, status, m.help.View(m.keys))
} }
func (m Model) renderStatusLine() string {
focusLabel := m.currentFocusLabel()
statusText := fmt.Sprintf("%s | Focus: %s (←/→)", m.status, focusLabel)
if m.lastError != nil {
return m.styles.Error.Render(fmt.Sprintf("⚠️ %v | Focus: %s (Esc to dismiss)", m.lastError, focusLabel))
}
return m.styles.Status.Render(statusText)
}
func (m Model) currentFocusLabel() string {
switch m.focus {
case focusSports:
return "Sports"
case focusMatches:
return "Matches"
case focusStreams:
return "Streams"
default:
return "Unknown"
}
}
func (m Model) renderHelpPanel() string { func (m Model) renderHelpPanel() string {
header := m.styles.Title.Render("Keybindings Help") header := m.styles.Title.Render("Keybindings Help")
bindings := [][]string{ bindings := [][]string{
@@ -256,12 +275,17 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
borderPadding := 4 borderPadding := 4
totalBorderSpace := borderPadding * 3 totalBorderSpace := borderPadding * 3
availableWidth := totalAvailableWidth - totalBorderSpace availableWidth := totalAvailableWidth - totalBorderSpace
colWidth := availableWidth / 3
remainder := availableWidth % 3
m.sports.SetWidth(colWidth + borderPadding) baseUnit := availableWidth / 6
m.matches.SetWidth(colWidth + borderPadding) remainder := availableWidth % 6
m.streams.SetWidth(colWidth + remainder + borderPadding)
sportsWidth := baseUnit
matchesWidth := baseUnit * 4
streamsWidth := baseUnit
m.sports.SetWidth(sportsWidth + borderPadding)
m.matches.SetWidth(matchesWidth + remainder + borderPadding)
m.streams.SetWidth(streamsWidth + borderPadding)
m.sports.SetHeight(usableHeight) m.sports.SetHeight(usableHeight)
m.matches.SetHeight(usableHeight) m.matches.SetHeight(usableHeight)
@@ -337,12 +361,14 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch m.focus { switch m.focus {
case focusSports: case focusSports:
if sport, ok := m.sports.Selected(); ok { if sport, ok := m.sports.Selected(); ok {
m.lastError = nil
m.status = fmt.Sprintf("Loading matches for %s…", sport.Name) m.status = fmt.Sprintf("Loading matches for %s…", sport.Name)
m.streams.SetItems(nil) m.streams.SetItems(nil)
return m, m.fetchMatchesForSport(sport) return m, m.fetchMatchesForSport(sport)
} }
case focusMatches: case focusMatches:
if mt, ok := m.matches.Selected(); ok { if mt, ok := m.matches.Selected(); ok {
m.lastError = nil
m.status = fmt.Sprintf("Loading streams for %s…", mt.Title) m.status = fmt.Sprintf("Loading streams for %s…", mt.Title)
return m, m.fetchStreamsForMatch(mt) return m, m.fetchStreamsForMatch(mt)
} }
@@ -360,6 +386,7 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
if m.focus == focusStreams { if m.focus == focusStreams {
if st, ok := m.streams.Selected(); ok && st.EmbedURL != "" { if st, ok := m.streams.Selected(); ok && st.EmbedURL != "" {
_ = openBrowser(st.EmbedURL) _ = openBrowser(st.EmbedURL)
m.lastError = nil
m.status = fmt.Sprintf("🌐 Opened in browser: %s", st.EmbedURL) m.status = fmt.Sprintf("🌐 Opened in browser: %s", st.EmbedURL)
} }
} }
@@ -369,27 +396,32 @@ func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
case sportsLoadedMsg: case sportsLoadedMsg:
m.sports.SetItems(msg) m.sports.SetItems(msg)
m.status = fmt.Sprintf("Loaded %d sports", len(msg)) m.lastError = nil
m.status = fmt.Sprintf("Loaded %d sports pick one with Enter or stay on Popular Matches", len(msg))
return m, nil return m, nil
case matchesLoadedMsg: case matchesLoadedMsg:
m.matches.SetTitle(msg.Title) m.matches.SetTitle(msg.Title)
m.matches.SetItems(msg.Matches) m.matches.SetItems(msg.Matches)
m.status = fmt.Sprintf("Loaded %d matches", len(msg.Matches)) m.lastError = nil
m.status = fmt.Sprintf("Loaded %d matches choose one to load streams", len(msg.Matches))
return m, nil return m, nil
case streamsLoadedMsg: case streamsLoadedMsg:
m.streams.SetItems(msg) m.streams.SetItems(msg)
m.status = fmt.Sprintf("Loaded %d streams", len(msg)) m.lastError = nil
m.status = fmt.Sprintf("Loaded %d streams Enter to launch mpv, o to open in browser", len(msg))
m.focus = focusStreams m.focus = focusStreams
return m, nil return m, nil
case launchStreamMsg: case launchStreamMsg:
m.lastError = nil
m.status = fmt.Sprintf("🎥 Launched mpv: %s", msg.URL) m.status = fmt.Sprintf("🎥 Launched mpv: %s", msg.URL)
return m, nil return m, nil
case errorMsg: case errorMsg:
m.lastError = msg m.lastError = msg
m.status = "Encountered an error while contacting the API"
return m, nil return m, nil
} }
return m, nil return m, nil

View File

@@ -17,6 +17,7 @@ type Styles struct {
Active lipgloss.Style Active lipgloss.Style
Status lipgloss.Style Status lipgloss.Style
Error lipgloss.Style // NEW: for red bold error lines Error lipgloss.Style // NEW: for red bold error lines
Subtle lipgloss.Style
} }
func NewStyles() Styles { func NewStyles() Styles {
@@ -30,6 +31,8 @@ func NewStyles() Styles {
Padding(0, 1). Padding(0, 1).
MarginRight(1), MarginRight(1),
Status: lipgloss.NewStyle().Foreground(lipgloss.Color("8")).MarginTop(1), Status: lipgloss.NewStyle().Foreground(lipgloss.Color("8")).MarginTop(1),
Error: lipgloss.NewStyle().Foreground(lipgloss.Color("9")).Bold(true),
Subtle: lipgloss.NewStyle().Foreground(lipgloss.Color("243")),
} }
} }
@@ -72,8 +75,8 @@ func (c *ListColumn[T]) SetWidth(w int) {
} }
func (c *ListColumn[T]) SetHeight(h int) { func (c *ListColumn[T]) SetHeight(h int) {
if h > 5 { if h > 6 {
c.height = h - 5 c.height = h - 6
} }
} }
@@ -109,7 +112,12 @@ func (c *ListColumn[T]) View(styles Styles, focused bool) string {
box = styles.Active box = styles.Active
} }
head := styles.Title.Render(c.title) titleText := fmt.Sprintf("%s (%d)", c.title, len(c.items))
if focused {
titleText = fmt.Sprintf("▶ %s", titleText)
}
head := styles.Title.Render(titleText)
meta := styles.Subtle.Render("Waiting for data…")
lines := []string{} lines := []string{}
if len(c.items) == 0 { if len(c.items) == 0 {
@@ -120,6 +128,7 @@ func (c *ListColumn[T]) View(styles Styles, focused bool) string {
if end > len(c.items) { if end > len(c.items) {
end = len(c.items) end = len(c.items)
} }
meta = styles.Subtle.Render(fmt.Sprintf("Showing %d%d of %d", start+1, end, len(c.items)))
for i := start; i < end; i++ { for i := start; i < end; i++ {
cursor := " " cursor := " "
lineText := c.render(c.items[i]) lineText := c.render(c.items[i])
@@ -145,5 +154,5 @@ func (c *ListColumn[T]) View(styles Styles, focused bool) string {
content := strings.Join(lines, "\n") content := strings.Join(lines, "\n")
// IMPORTANT: width = interior content width + 4 (border+padding) // IMPORTANT: width = interior content width + 4 (border+padding)
return box.Width(c.width + 4).Render(head + "\n" + content) return box.Width(c.width + 4).Render(head + "\n" + meta + "\n" + content)
} }