app

package
v1.3.0 Latest Latest
Warning

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

Go to latest
Published: Apr 8, 2026 License: GPL-3.0 Imports: 24 Imported by: 0

Documentation

Index

Constants

View Source
const (
	ArtWidth    = 24 // columns for now-playing art
	ArtHeight   = 12 // terminal rows (= 24 pixel rows via half-blocks)
	MinTermRows = 28 // below this, hide art
	HeaderArtW  = 8  // columns for tracklist header art
	HeaderArtH  = 4  // terminal rows for tracklist header art
)

Variables

View Source
var (
	ColorAccent  = lipgloss.Color("#1DB954")
	ColorBg      = lipgloss.Color("#191414")
	ColorSurface = lipgloss.Color("#282828")
	ColorText    = lipgloss.Color("#FFFFFF")
	ColorTextSec = lipgloss.Color("#B3B3B3")
	ColorTextDim = lipgloss.Color("#535353")
	ColorBorder  = lipgloss.Color("#333333")
	ColorError   = lipgloss.Color("#E22134")
)

Spotify-inspired color palette

View Source
var (
	StyleSectionHeader = lipgloss.NewStyle().
						Foreground(ColorTextDim).
						Bold(true).
						PaddingLeft(1)

	StyleActiveItem = lipgloss.NewStyle().
					Background(ColorSurface).
					Foreground(ColorAccent).
					Bold(true)

	StyleDimText = lipgloss.NewStyle().
					Foreground(ColorTextDim)

	StyleModeNormal = lipgloss.NewStyle().
					Foreground(ColorBg).
					Background(ColorAccent).
					Bold(true).
					Padding(0, 1)

	StyleModeCommand = lipgloss.NewStyle().
						Foreground(ColorBg).
						Background(ColorText).
						Bold(true).
						Padding(0, 1)

	StyleModeSearch = lipgloss.NewStyle().
					Foreground(ColorBg).
					Background(lipgloss.Color("#E2B714")).
					Bold(true).
					Padding(0, 1)

	StyleModeFilter = lipgloss.NewStyle().
					Foreground(ColorBg).
					Background(lipgloss.Color("#BB9AF7")).
					Bold(true).
					Padding(0, 1)

	StyleStatusBar = lipgloss.NewStyle().
					Background(ColorSurface).
					Foreground(ColorText)

	StyleModeLine = lipgloss.NewStyle().
					Background(ColorBg).
					Foreground(ColorTextSec)
)

Reusable styles

Functions

func CurrentAccent

func CurrentAccent() lipgloss.Color

CurrentAccent returns the accent color used for active elements.

func DominantColor

func DominantColor(img image.Image) lipgloss.Color

DominantColor samples pixels from an image and returns the most vibrant color as a lipgloss hex Color. It skips near-black and near-white pixels to find a representative accent color. Returns "" if no suitable color is found.

func FetchImage

func FetchImage(ctx context.Context, url string) (image.Image, error)

FetchImage downloads and decodes an image from a URL. Uses a dedicated client with timeout and a body size limit to prevent hangs and OOM from slow or malicious servers.

func FormatAlbumInfo

func FormatAlbumInfo(artist, year string, tracks []source.Track) string

FormatAlbumInfo returns "Artist · Year · N tracks".

func FormatPartialTrackListInfo

func FormatPartialTrackListInfo(loaded, total int) string

FormatPartialTrackListInfo returns "N of M tracks" for partially loaded playlists.

func FormatTrackListInfo

func FormatTrackListInfo(tracks []source.Track) string

FormatTrackListInfo returns a summary string like "30 tracks · 1h 42m".

func PaneBorder

func PaneBorder(active bool) lipgloss.Style

PaneBorder returns a border style for a pane.

func PlaceholderArt

func PlaceholderArt(w, h int) string

PlaceholderArt returns a simple colored block when no art is available.

func RenderNowPlaying

func RenderNowPlaying(track *source.Track, artBlock string, albumImg image.Image, vinylMode bool, vinylAngle float64, liked bool, width, height int) string

RenderNowPlaying renders the full-screen now playing overlay with a blurred, darkened album art background.

func ViewHelp

func ViewHelp(width, height int) string

ViewHelp renders the help overlay as a centered floating panel.

Types

type ActionItem

type ActionItem struct {
	Type  ActionType
	Label string
	Icon  string
}

ActionItem is a single entry in the actions popup.

type ActionType

type ActionType int

ActionType identifies a context action.

const (
	ActionPlay ActionType = iota
	ActionQueue
	ActionLike
	ActionGoArtist
	ActionGoAlbum
	ActionOpenSpotify
	ActionCopyURI
	ActionPlayPlaylist
	ActionOpenPlaylistSpotify
	ActionLoadTracks
)

type ActionsPopup

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

ActionsPopup is a floating overlay listing context actions for the selected item.

func NewPlaylistActions

func NewPlaylistActions(playlistName, uri string, width, height int) ActionsPopup

NewPlaylistActions returns an ActionsPopup configured for a playlist.

func NewTrackActions

func NewTrackActions(trackName, artistName, uri, artistID, albumID string, liked bool, width, height int) ActionsPopup

NewTrackActions returns an ActionsPopup configured for a track.

func (ActionsPopup) AlbumID

func (a ActionsPopup) AlbumID() string

AlbumID returns the album ID stored in the popup.

func (ActionsPopup) ArtistID

func (a ActionsPopup) ArtistID() string

ArtistID returns the artist ID stored in the popup.

func (*ActionsPopup) MoveDown

func (a *ActionsPopup) MoveDown()

MoveDown moves the cursor down in the actions list.

func (*ActionsPopup) MoveUp

func (a *ActionsPopup) MoveUp()

MoveUp moves the cursor up in the actions list.

func (ActionsPopup) Selected

func (a ActionsPopup) Selected() ActionItem

Selected returns the currently highlighted action.

func (ActionsPopup) URI

func (a ActionsPopup) URI() string

URI returns the URI stored in the popup (for clipboard copy).

func (ActionsPopup) View

func (a ActionsPopup) View() string

View renders the actions popup as a centered floating overlay.

type AlbumArt

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

AlbumArt renders album artwork using Unicode half-block characters.

func NewAlbumArt

func NewAlbumArt() AlbumArt

func (*AlbumArt) Clear

func (a *AlbumArt) Clear()

func (*AlbumArt) CurrentURL

func (a *AlbumArt) CurrentURL() string

func (*AlbumArt) SetImage

func (a *AlbumArt) SetImage(img image.Image)

func (*AlbumArt) SetURL

func (a *AlbumArt) SetURL(url string)

func (AlbumArt) View

func (a AlbumArt) View() string

type ArtworkProvider

type ArtworkProvider interface {
	ArtworkImage(url string) (image.Image, bool)
}

ArtworkProvider is an optional interface that source implementations can satisfy to provide embedded artwork instead of HTTP fetching. Used by demo mode to avoid network calls.

type CmdType

type CmdType int
const (
	CmdQuit CmdType = iota
	CmdVolume
	CmdShuffle
	CmdRepeat
	CmdDevice
	CmdSearch
	CmdRecent
)

type Command

type Command struct {
	Type   CmdType
	IntArg int
	StrArg string
}

func ParseCommand

func ParseCommand(input string) (Command, error)

type DevicePicker

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

DevicePicker is a floating overlay listing available Spotify Connect devices.

func NewDevicePicker

func NewDevicePicker(devices []source.Device, width, height int) DevicePicker

NewDevicePicker creates a device picker popup from a list of devices.

func (*DevicePicker) MoveDown

func (d *DevicePicker) MoveDown()

MoveDown moves the cursor down.

func (*DevicePicker) MoveUp

func (d *DevicePicker) MoveUp()

MoveUp moves the cursor up.

func (DevicePicker) Selected

func (d DevicePicker) Selected() *source.Device

Selected returns the currently highlighted device, or nil if empty.

func (DevicePicker) View

func (d DevicePicker) View() string

View renders the device picker as a centered floating overlay.

type GAction

type GAction int

GAction identifies the action from a g-prefix key combo.

const (
	GActionNone    GAction = iota
	GActionTop             // gg — go to top
	GActionLibrary         // gl — focus library
	GActionQueue           // gq — focus queue
	GActionCurrent         // gc — jump to currently playing track
	GActionRecent          // gr — load recently played
)

type GTracker

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

GTracker tracks g-prefix two-key motions (gg, gl, gq, gc, gr).

func (*GTracker) Feed

func (t *GTracker) Feed(k string) GAction

Feed processes a key press. Returns the resolved GAction. When "g" is pressed the first time it returns GActionNone (pending). A second key resolves the action. Any unrecognised second key resets and returns GActionNone.

func (*GTracker) Pending

func (t *GTracker) Pending() bool

Pending returns whether the tracker is waiting for a second key after "g".

func (*GTracker) Reset

func (t *GTracker) Reset()

Reset clears any pending state.

type KeyMap

type KeyMap struct {
	// Navigation
	Up       key.Binding
	Down     key.Binding
	Top      key.Binding
	Bottom   key.Binding
	HalfUp   key.Binding
	HalfDown key.Binding

	// Pane
	FocusLeft  key.Binding
	FocusRight key.Binding
	CyclePane  key.Binding

	// Playback
	Enter     key.Binding
	PlayPause key.Binding
	Next      key.Binding
	Prev      key.Binding
	SeekFwd   key.Binding
	SeekBack  key.Binding

	// Actions
	AddQueue key.Binding
	Like     key.Binding
	Actions  key.Binding
	Devices  key.Binding

	// Navigation history
	Back key.Binding

	// Modes
	Filter     key.Binding
	Search     key.Binding
	Command    key.Binding
	Help       key.Binding
	NowPlaying key.Binding
	Quit       key.Binding
	Escape     key.Binding

	// Sections
	Section1 key.Binding
	Section2 key.Binding
}

func DefaultKeyMap

func DefaultKeyMap() KeyMap

func (KeyMap) FullHelp

func (k KeyMap) FullHelp() [][]key.Binding

func (KeyMap) ShortHelp

func (k KeyMap) ShortHelp() []key.Binding

type Mode

type Mode int

Mode represents the current input mode.

const (
	ModeNormal Mode = iota
	ModeCommand
	ModeSearch
	ModeFilter
	ModeHelp
	ModeActions
	ModeDevices
	ModeNowPlaying
)

func (Mode) String

func (m Mode) String() string

type Model

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

Model is the root Bubbletea model for waxon.

func NewModel

func NewModel(src source.RichSource) Model

func (Model) Init

func (m Model) Init() tea.Cmd

func (Model) Update

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

func (Model) View

func (m Model) View() string
type NavState struct {
	// contains filtered or unexported fields
}

NavState captures the tracklist state for browser-like back navigation.

type Pane

type Pane int

Pane identifies which pane has focus.

const (
	PaneSidebar Pane = iota
	PaneTrackList
)

func (Pane) String

func (p Pane) String() string
type Search struct {
	// contains filtered or unexported fields
}

Search is the floating search overlay model.

func NewSearch

func NewSearch(width, height int) Search

func (Search) SelectedAlbum

func (s Search) SelectedAlbum() *source.SearchAlbum

SelectedAlbum returns the selected album if the cursor is on an album row.

func (Search) SelectedArtist

func (s Search) SelectedArtist() *source.SearchArtist

SelectedArtist returns the selected artist if the cursor is on an artist row.

func (Search) SelectedTrack

func (s Search) SelectedTrack() *source.Track

SelectedTrack returns the selected track if the cursor is on a track row.

func (Search) Update

func (s Search) Update(msg tea.Msg) (Search, tea.Cmd)

func (Search) View

func (s Search) View() string
type Sidebar struct {
	// contains filtered or unexported fields
}

Sidebar is the left pane model.

func NewSidebar

func NewSidebar(width, height int) Sidebar

func (*Sidebar) ClearFilter

func (s *Sidebar) ClearFilter()

ClearFilter restores the full unfiltered playlist list.

func (*Sidebar) Resize

func (s *Sidebar) Resize(width, height int)

func (*Sidebar) Section

func (s *Sidebar) Section() SidebarSection

func (*Sidebar) SelectedPlaylist

func (s *Sidebar) SelectedPlaylist() *source.Playlist

func (*Sidebar) SetCursorFromClick

func (s *Sidebar) SetCursorFromClick(y int)

SetCursorFromClick maps a Y coordinate (relative to the pane top) to a list item and selects it.

func (*Sidebar) SetFilter

func (s *Sidebar) SetFilter(query string)

SetFilter filters the sidebar playlist list by name.

func (*Sidebar) SetPlaylistIcons

func (s *Sidebar) SetPlaylistIcons(icons map[string]string)

SetPlaylistIcons updates the library playlist items with rendered art icons. Merges into allItems (the library set) rather than the live list, so icons are preserved correctly even if the user switched to the queue view.

func (*Sidebar) SetPlaylists

func (s *Sidebar) SetPlaylists(playlists []source.Playlist)

func (*Sidebar) SetQueueTracks

func (s *Sidebar) SetQueueTracks(tracks []source.Track)

func (*Sidebar) SetSection

func (s *Sidebar) SetSection(sec SidebarSection)

func (Sidebar) Update

func (s Sidebar) Update(msg tea.Msg) (Sidebar, tea.Cmd)

func (Sidebar) View

func (s Sidebar) View(active bool) string

type SidebarSection

type SidebarSection int

SidebarSection identifies which section of the sidebar.

const (
	SectionLibrary SidebarSection = iota
	SectionQueue
)

type StatusBar

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

StatusBar renders the bottom two rows: now-playing bar and mode line.

func NewStatusBar

func NewStatusBar(width int) StatusBar

func (*StatusBar) Resize

func (s *StatusBar) Resize(width int)

func (StatusBar) ViewModeLine

func (s StatusBar) ViewModeLine(mode Mode, cmdInput string, filterInput string, filterActive string, volume int, deviceName string) string

ViewModeLine renders the mode/command line row.

func (StatusBar) ViewNowPlaying

func (s StatusBar) ViewNowPlaying(track *source.Track, shuffleOn bool, repeatMode source.RepeatMode, liked bool) string

ViewNowPlaying renders the now-playing row.

func (StatusBar) ViewNowPlayingWithArt

func (s StatusBar) ViewNowPlayingWithArt(track *source.Track, shuffleOn bool, repeatMode source.RepeatMode, liked bool, artBlock string, volume int, deviceName string, mode Mode, cmdInput string, filterInput string, filterActive string) string

ViewNowPlayingWithArt renders the now-playing panel with album art on the left.

type Toast

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

Toast is a floating notification rendered in the top-right corner.

func (*Toast) Hide

func (t *Toast) Hide()

Hide clears the toast.

func (Toast) Overlay

func (t Toast) Overlay(base string, screenWidth int) string

Overlay composites the toast box onto the top-right corner of the rendered view.

func (*Toast) Show

func (t *Toast) Show(message, detail string, tt ToastType)

Show makes the toast visible with the given content.

func (Toast) View

func (t Toast) View(screenWidth int) string

View renders the toast as a floating notification card.

func (Toast) Visible

func (t Toast) Visible() bool

Visible returns whether the toast is currently shown.

type ToastType

type ToastType int

ToastType controls the visual style of a toast notification.

const (
	ToastSuccess ToastType = iota
	ToastError
	ToastInfo
)

type TrackList

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

TrackList is the right pane model showing tracks in a table.

func NewTrackList

func NewTrackList(width, height int) TrackList

func (*TrackList) AppendTracks

func (tl *TrackList) AppendTracks(tracks []source.Track)

AppendTracks adds more tracks to the existing list without resetting the cursor position. Used for lazy-loading additional pages.

func (*TrackList) ClearFilter

func (tl *TrackList) ClearFilter()

ClearFilter removes the active filter and restores the full track list.

func (*TrackList) ContextURI

func (tl *TrackList) ContextURI() string

func (*TrackList) FilterText

func (tl *TrackList) FilterText() string

FilterText returns the current active filter query (empty if none).

func (*TrackList) GetState

func (tl *TrackList) GetState(pane Pane) NavState

GetState snapshots the current tracklist state (including cursor position).

func (*TrackList) JumpToTrack

func (tl *TrackList) JumpToTrack(trackID string) bool

JumpToTrack moves the cursor to the track with the given ID. Returns true if the track was found, false otherwise.

func (*TrackList) Resize

func (tl *TrackList) Resize(width, height int)

func (*TrackList) RestoreState

func (tl *TrackList) RestoreState(s NavState)

RestoreState restores the tracklist from a previously saved NavState.

func (*TrackList) SelectedTrack

func (tl *TrackList) SelectedTrack() *source.Track

func (*TrackList) SetArt

func (tl *TrackList) SetArt(artBlock string)

func (*TrackList) SetCursorFromClick

func (tl *TrackList) SetCursorFromClick(y int)

SetCursorFromClick maps a Y coordinate (relative to the pane top) to a table data row and moves the cursor there.

func (*TrackList) SetFilter

func (tl *TrackList) SetFilter(query string)

SetFilter applies a case-insensitive filter matching track name or artist. An empty query clears the filter.

func (*TrackList) SetHeaderInfo

func (tl *TrackList) SetHeaderInfo(info string)

func (*TrackList) SetLoading

func (tl *TrackList) SetLoading(title string)

func (*TrackList) SetNowPlaying

func (tl *TrackList) SetNowPlaying(trackID string)

func (*TrackList) SetSubtitle

func (tl *TrackList) SetSubtitle(s string)

func (*TrackList) SetTracks

func (tl *TrackList) SetTracks(tracks []source.Track, title, contextURI string)

func (*TrackList) TickSpinner

func (tl *TrackList) TickSpinner()

func (TrackList) Update

func (tl TrackList) Update(msg tea.Msg) (TrackList, tea.Cmd)

func (TrackList) View

func (tl TrackList) View(active bool) string

Jump to

Keyboard shortcuts

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