app

package
v0.1.0-rc3 Latest Latest
Warning

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

Go to latest
Published: May 8, 2026 License: MIT Imports: 27 Imported by: 0

Documentation

Overview

Package app contains the root Bubble Tea model that wires together all panes, the central store, and the active theme. It is the single entry point for the TUI application.

Package app — command factories extracted from app.go. All functions in this file are methods on *App or package-level helpers that create tea.Cmd values. No routing or state-mutation logic lives here — this file is a pure command factory for the Spotify API calls.

Elm Architecture contract: build*Cmd and fetch*Cmd functions MUST NOT write to the Store. All Store mutations happen in Update() when the returned Msg is processed. Commands return data in Msg payloads; Update() decides what to store.

central message dispatch for the root Bubble Tea model — called by Update() for every incoming message; routes Msg payloads to Store writes and returns follow-up commands.

preference persistence — theme, preset, and visualizer changes are debounced and flushed to disk via a generation-counter pattern to avoid excessive writes during rapid key presses.

Package app — rendering extracted from app.go. This file contains View() and all render* helper methods on *App. No state mutation or command dispatch lives here — only pure string rendering.

Package app — key and message routing helpers extracted from app.go. These methods handle the routing logic within Update() so that app.go stays focused on the Bubble Tea lifecycle (Init/Update/View structure) rather than the full dispatch table.

Index

Constants

View Source
const SearchPageSize = 10

SearchPageSize is the number of results fetched per page. Matches Spotify's recommended default; named for test clarity.

Variables

This section is empty.

Functions

func BuildSearchPageCmd

func BuildSearchPageCmd(ctx context.Context, client api.SearchAPI, query string, types []string, page int) tea.Cmd

BuildSearchPageCmd is an exported wrapper around buildSearchPageCmd for testing. Exported for tests only — production code must use buildSearchPageCmd.

func ConvertSearchResult

func ConvertSearchResult(r *api.SearchResult) ([]panes.SearchListItem, int)

ConvertSearchResult is an exported wrapper around convertSearchResult for testing. Exported for tests only.

Types

type App

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

App is the root application model. It owns the active theme, the central store, the API clients, and all pane models. It is the ONLY layer that calls the Spotify API — panes emit request messages and app.go dispatches them.

func New

func New(cfg *config.Config, opts AppOptions) *App

New creates a new App, loading the theme from cfg.Preferences.Theme.

func (*App) ActivePresetIndex

func (a *App) ActivePresetIndex() int

ActivePresetIndex returns the active preset index from the layout manager (exported for testing).

func (*App) AlbumTracksID

func (a *App) AlbumTracksID() string

AlbumTracksID returns the current album tracks staleness key (album ID). Exported for tests.

func (*App) BackoffTicks

func (a *App) BackoffTicks() int

BackoffTicks returns the remaining backoff ticks (exported for testing).

func (*App) CallSearchCancel

func (a *App) CallSearchCancel()

CallSearchCancel calls the current searchCancel function. Exported for tests to verify cancellation without exposing context.CancelFunc directly.

func (*App) DeviceOverlayOpen

func (a *App) DeviceOverlayOpen() bool

DeviceOverlayOpen returns true while the device switcher overlay is visible.

func (*App) FocusedPane

func (a *App) FocusedPane() layout.PaneID

FocusedPane returns the PaneID of the pane that currently has keyboard focus. Returns layout.PaneID(-1) if no pane is focused.

func (*App) GatewayHealthPane

func (a *App) GatewayHealthPane() *panes.GatewayHealthPane

GatewayHealthPane returns the GatewayHealthPane from the panes map (exported for testing).

func (*App) GatewayLivePane

func (a *App) GatewayLivePane() *panes.GatewayLivePane

GatewayLivePane returns the GatewayLivePane from the panes map (exported for testing).

func (*App) GridViewOpen

func (a *App) GridViewOpen() bool

GridViewOpen returns true while the grid view is the active top-level view.

func (*App) HelpOpen

func (a *App) HelpOpen() bool

HelpOpen returns true while the help keybinding overlay is visible.

func (*App) Init

func (a *App) Init() tea.Cmd

Init starts the splash timer. If the user is already authenticated, it also starts data fetching and the polling loop. If not, those are deferred until auth succeeds.

func (*App) InitAPIClients

func (a *App) InitAPIClients(token string)

InitAPIClients constructs and wires all Spotify API clients with the gateway and event recorder. Called from cmd/ for pre-authenticated startup.

func (*App) IsIdle

func (a *App) IsIdle() bool

IsIdle returns true if no user input has been received within idleThreshold. Exported for testing.

func (*App) Layout

func (a *App) Layout() *layout.Manager

Layout returns the layout manager, exposed for testing.

func (*App) NeedsRegister

func (a *App) NeedsRegister() bool

NeedsRegister returns true when the app was started without a client ID in config (exported for testing).

func (*App) NetworkLogPane

func (a *App) NetworkLogPane() *panes.NetworkLogPane

NetworkLogPane returns the NetworkLogPane from the panes map (exported for testing).

func (*App) NowPlayingFocused

func (a *App) NowPlayingFocused() bool

NowPlayingFocused returns true if the NowPlaying pane currently has keyboard focus.

func (*App) NowPlayingPane

func (a *App) NowPlayingPane() *panes.NowPlayingPane

NowPlayingPane returns the NowPlayingPane from the panes map (exported for testing).

func (*App) OnboardingAuthURL

func (a *App) OnboardingAuthURL() string

OnboardingAuthURL returns the full OAuth authorization URL for the onboarding flow (exported for testing).

func (*App) OnboardingError

func (a *App) OnboardingError() string

OnboardingError returns the onboarding error message (exported for testing).

func (*App) OnboardingStep

func (a *App) OnboardingStep() int

OnboardingStep returns the current onboarding sub-step (exported for testing).

func (*App) PlaylistTracksID

func (a *App) PlaylistTracksID() string

PlaylistTracksID returns the current playlist tracks staleness key (playlist ID). Exported for tests.

func (*App) PlaylistsFocused

func (a *App) PlaylistsFocused() bool

PlaylistsFocused returns true if the Playlists pane currently has keyboard focus.

func (*App) PollIntervals

func (a *App) PollIntervals() (playbackInterval, queueInterval int)

PollIntervals returns the current playback and queue polling intervals based on user activity and playback state. Exported for testing.

func (*App) PollingTrafficPane

func (a *App) PollingTrafficPane() *panes.PollingTrafficPane

PollingTrafficPane returns the PollingTrafficPane from the panes map (exported for testing).

func (*App) Prefs

func (a *App) Prefs() *prefs.PreferenceStore

Prefs returns the underlying PreferenceStore for inspection in tests.

func (*App) PrefsDirtyGen

func (a *App) PrefsDirtyGen() int

PrefsDirtyGen returns the current preference dirty generation counter. Used in tests to verify that schedulePrefsFlush increments it correctly.

func (*App) ProfileOverlayOpen

func (a *App) ProfileOverlayOpen() bool

ProfileOverlayOpen returns true while the user profile overlay is visible.

func (*App) QueueFocused

func (a *App) QueueFocused() bool

QueueFocused returns true if the Queue pane currently has keyboard focus.

func (*App) SchedulePrefsFlush

func (a *App) SchedulePrefsFlush() tea.Cmd

SchedulePrefsFlush is the exported test accessor for schedulePrefsFlush. It increments the generation counter and returns the debounce tick Cmd.

func (*App) SearchCancelCtx

func (a *App) SearchCancelCtx() context.Context

SearchCancelCtx returns the context associated with the current in-flight search. Returns nil when no search is in-flight (searchCancel is the no-op sentinel). Exported for tests to observe context cancellation.

func (*App) SearchLoading

func (a *App) SearchLoading() bool

SearchLoading returns true while a search HTTP call is in-flight. Exported for tests.

func (*App) SearchOpen

func (a *App) SearchOpen() bool

SearchOpen returns true while the search overlay is visible.

func (*App) SearchPage

func (a *App) SearchPage() int

SearchPage returns the current search staleness key page. Exported for tests.

func (*App) SearchPane

func (a *App) SearchPane() *panes.SearchOverlay

SearchPane returns the search overlay pane. Exported for tests that need to inspect overlay state after openSearch().

func (*App) SearchQuery

func (a *App) SearchQuery() string

SearchQuery returns the current search staleness key query. Exported for tests.

func (*App) SetAlbumTracksID

func (a *App) SetAlbumTracksID(id string)

SetAlbumTracksID sets the album tracks staleness key for testing. Exported for tests only.

func (*App) SetDevices

func (a *App) SetDevices(devices api.DevicesAPI)

SetDevices injects the Spotify Connect devices API client into the app.

func (*App) SetLastInteraction

func (a *App) SetLastInteraction(t time.Time)

SetLastInteraction sets the lastInteraction timestamp (exported for testing).

func (*App) SetLibrary

func (a *App) SetLibrary(library api.LibraryAPI)

SetLibrary injects the Spotify API library client into the app.

func (*App) SetPlayer

func (a *App) SetPlayer(player api.PlayerAPI)

SetPlayer injects the Spotify API player client into the app.

func (*App) SetPlaylistTracksID

func (a *App) SetPlaylistTracksID(id string)

SetPlaylistTracksID sets the playlist tracks staleness key for testing. Exported for tests only.

func (*App) SetPlaylistsAPI

func (a *App) SetPlaylistsAPI(p api.PlaylistsAPI)

SetPlaylistsAPI injects the Spotify playlists mutation client into the app.

func (*App) SetSearch

func (a *App) SetSearch(search api.SearchAPI)

SetSearch injects the Spotify API search client into the app.

func (*App) SetSearchSession

func (a *App) SetSearchSession(query string, page int, loading bool)

SetSearchSession sets the search staleness keys and loading flag for testing. This bypasses the normal SearchRequestMsg pathway so tests can set up state directly. Exported for tests only.

func (*App) SetUserAPI

func (a *App) SetUserAPI(userAPI api.UserAPI)

SetUserAPI injects the Spotify user identity and statistics API client into the app.

func (*App) Store

func (a *App) Store() *state.Store

Store returns the central state store.

func (*App) Theme

func (a *App) Theme() theme.Theme

Theme returns the active theme instance.

func (*App) ThemeSwitcherOpen

func (a *App) ThemeSwitcherOpen() bool

ThemeSwitcherOpen returns true while the theme switcher overlay is visible.

func (*App) TickCount

func (a *App) TickCount() int

TickCount returns the current tick counter (exported for testing).

func (*App) Update

func (a *App) Update(msg tea.Msg) (tea.Model, tea.Cmd)

Update handles all messages routed through the root model. It delegates to handleMsg for application logic, then forwards the message to the BubbleUp alerts model so alert lifecycle timers (auto-dismiss) keep running.

func (*App) View

func (a *App) View() string

View renders the full terminal UI. IMPORTANT: The final step calls a.alerts.Render(view) to overlay any active toast notification on top of the complete rendered view. Never call alerts.View() — BubbleUp's View() returns empty string by design.

type AppOptions

type AppOptions struct {
	// NeedsRegister is true when no client_id is present in config.
	// The TUI will show the onboarding flow (stepRegister) on first launch.
	NeedsRegister bool
	NeedsAuth     bool
	ClientID      string
	TokenStore    keychain.TokenStore
	// TokenBaseURL overrides the Spotify token endpoint for tests.
	// Leave empty for production (uses the real Spotify endpoint).
	TokenBaseURL string
	// Version is the build-time injected version string (e.g. "v0.1.0").
	// Falls back to "dev" when not provided.
	Version string
	// CallbackPort is the port the OAuth callback server is listening on.
	// Non-zero when the server was started early (needsRegister || needsAuth).
	CallbackPort int
	// CallbackCodeCh receives the OAuth authorization code from the callback server.
	// Non-nil when the server was started early.
	CallbackCodeCh <-chan api.CallbackResult
	// CallbackClose closes the callback server. Non-nil when started early.
	CallbackClose func()
}

AppOptions carries optional startup configuration into the app. Zero value means the user is already authenticated and no auth flow is needed.

Jump to

Keyboard shortcuts

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