// Shared screen chrome: a header rule (app name + server) and a footer rule // (status + help) used by the browse and playing screens so they feel like one // app rather than three separate views. package tui import ( "net/url" "strings" "github.com/charmbracelet/lipgloss" ) // hostLabel extracts a friendly host from a server URL — no scheme or port, and // a trailing ".local" trimmed (so "http://omabox.local:8096" → "omabox"). func hostLabel(raw string) string { u, err := url.Parse(raw) if err != nil || u.Host == "" { return raw } return strings.TrimSuffix(u.Hostname(), ".local") } // serverLabel prefers a discovered server's advertised name (matched by URL), // otherwise falls back to the host portion of the configured URL. func (m Model) serverLabel() string { for _, s := range m.discovered { if s.Address == m.cfg.ServerURL && s.Name != "" { return s.Name } } return hostLabel(m.cfg.ServerURL) } // frameWidth returns the usable width, with a sane fallback before the first // WindowSizeMsg arrives. func (m Model) frameWidth() int { if m.width <= 0 { return 80 } return m.width } // headerBar renders the top rule: " tsplay ───────────────── server ". func (m Model) headerBar() string { title := m.st.title.Render(" tsplay ") srv := "" if s := m.serverLabel(); s != "" { srv = m.st.muted.Render(" " + s + " ") } dash := m.frameWidth() - lipgloss.Width(title) - lipgloss.Width(srv) if dash < 1 { dash = 1 } return title + m.st.barEmpty.Render(strings.Repeat("─", dash)) + srv } // footerBar renders a thin rule, an optional accent status line, then the help // keys — the consistent bottom of every framed screen. func (m Model) footerBar(help, status string) string { rule := m.st.barEmpty.Render(strings.Repeat("─", m.frameWidth())) lines := []string{rule} if status != "" { lines = append(lines, " "+m.st.accent.Render(status)) } lines = append(lines, " "+m.st.help.Render(help)) return strings.Join(lines, "\n") }