tui

package
v1.1.3 Latest Latest
Warning

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

Go to latest
Published: Jun 22, 2026 License: MIT Imports: 17 Imported by: 0

Documentation

Index

Constants

View Source
const (
	// 3-Column Smart Ratios (Inspector visible)
	ParentColumnPercent3   = 25 // Parent context
	ActiveColumnPercent3   = 35 // Active/focused
	InspectorColumnPercent = 30 // Inspector (summary)

	// 3-Column Focus Mode (Inspector hidden) - show more navigation context
	GrandparentColumnPercent = 25 // Grandparent context
	ParentColumnPercent2     = 30 // Parent context
	ActiveColumnPercent2     = 45 // Active/focused

	// Root level (single column + inspector)
	RootColumnPercent    = 40
	RootInspectorPercent = 60

	MinColumnWidth = 15

	// Vertical layout: single footer line
	ChromeHeight = 1
)

Layout proportions for Miller Columns

Variables

View Source
var Keys = DefaultKeyMap()

Keys is the global key bindings instance

Functions

func AddToPlaylistCmd

func AddToPlaylistCmd(svc *playlist.Service, playlistID string, itemIDs []string) tea.Cmd

AddToPlaylistCmd adds items to a playlist

func AddToQueueCmd

func AddToQueueCmd(svc *playlist.Service, item *domain.MediaItem) tea.Cmd

func ClearLibraryStatusCmd

func ClearLibraryStatusCmd(libID string, delay time.Duration) tea.Cmd

ClearLibraryStatusCmd returns a command that clears library status after delay

func ClearStatusCmd

func ClearStatusCmd(delay time.Duration) tea.Cmd

ClearStatusCmd returns a command that clears status after a delay

func CreatePlaylistCmd

func CreatePlaylistCmd(svc *playlist.Service, title string, itemIDs []string) tea.Cmd

CreatePlaylistCmd creates a new playlist

func DeletePlaylistCmd

func DeletePlaylistCmd(svc *playlist.Service, playlistID string) tea.Cmd

DeletePlaylistCmd deletes a playlist

func ListenForPlaybackStatusCmd added in v0.0.2

func ListenForPlaybackStatusCmd(statusCh <-chan string) tea.Cmd

ListenForPlaybackStatusCmd waits for status updates during playback

func LoadContinueWatchingCmd added in v1.1.0

func LoadContinueWatchingCmd(svc *library.Service) tea.Cmd

LoadContinueWatchingCmd loads items currently in progress

func LoadEpisodesCmd

func LoadEpisodesCmd(svc *library.Service, libID, showID, seasonID string) tea.Cmd

LoadEpisodesCmd loads episodes for a season

func LoadLibrariesCmd

func LoadLibrariesCmd(svc *library.Service) tea.Cmd

LoadLibrariesCmd loads all available libraries

func LoadMixedLibraryCmd

func LoadMixedLibraryCmd(svc *library.Service, libID string) tea.Cmd

LoadMixedLibraryCmd loads content (movies AND shows) from a mixed library

func LoadMoviesCmd

func LoadMoviesCmd(svc *library.Service, libID string) tea.Cmd

LoadMoviesCmd loads movies from a library

func LoadPlaylistItemsCmd

func LoadPlaylistItemsCmd(svc *playlist.Service, playlistID string) tea.Cmd

LoadPlaylistItemsCmd loads items from a playlist

func LoadPlaylistModalDataCmd

func LoadPlaylistModalDataCmd(svc *playlist.Service, item *domain.MediaItem) tea.Cmd

LoadPlaylistModalDataCmd loads data for the playlist management modal

func LoadPlaylistsCmd

func LoadPlaylistsCmd(svc *playlist.Service) tea.Cmd

LoadPlaylistsCmd loads all playlists

func LoadSeasonForPlaybackCmd added in v1.1.0

func LoadSeasonForPlaybackCmd(svc *library.Service, item *domain.MediaItem, resume bool) tea.Cmd

LoadSeasonForPlaybackCmd loads all episodes for an episode's season to build a full playlist

func LoadSeasonsCmd

func LoadSeasonsCmd(svc *library.Service, libID, showID string) tea.Cmd

LoadSeasonsCmd loads seasons for a show

func LoadShowsCmd

func LoadShowsCmd(svc *library.Service, libID string) tea.Cmd

LoadShowsCmd loads TV shows from a library

func LogoutCmd

func LogoutCmd() tea.Cmd

LogoutCmd clears server config and cache, then signals completion

func MarkUnwatchedCmd

func MarkUnwatchedCmd(svc *player.Service, libID, itemID, title string) tea.Cmd

MarkUnwatchedCmd marks an item as unwatched

func MarkWatchedCmd

func MarkWatchedCmd(svc *player.Service, libID, itemID, title string) tea.Cmd

MarkWatchedCmd marks an item as watched

func PlayItemCmd

func PlayItemCmd(svc *player.Service, item domain.MediaItem, resume bool, autoplay bool, playlist ...domain.MediaItem) tea.Cmd

PlayItemCmd starts playback of an item, optionally with a playlist

func RemoteSearchCmd

func RemoteSearchCmd(svc *search.Service, query string) tea.Cmd

func RemoveFromPlaylistCmd

func RemoveFromPlaylistCmd(svc *playlist.Service, playlistID, itemID string) tea.Cmd

RemoveFromPlaylistCmd removes an item from a playlist

func RemoveFromQueueCmd

func RemoveFromQueueCmd(svc *playlist.Service, itemID string) tea.Cmd

func RenderSpinner

func RenderSpinner(frame int) string

RenderSpinner renders a loading spinner

func SyncAllLibrariesCmd

func SyncAllLibrariesCmd(svc *library.Service, libraries []domain.Library) tea.Cmd

SyncAllLibrariesCmd syncs all libraries in parallel

func SyncLibraryCmd

func SyncLibraryCmd(svc *library.Service, lib domain.Library) tea.Cmd

SyncLibraryCmd performs smart sync with streaming progress updates

func SyncPlaylistsCmd

func SyncPlaylistsCmd(svc *playlist.Service, playlistsID string) tea.Cmd

SyncPlaylistsCmd syncs playlists and their items (two levels deep, like library sync).

func TickCmd

func TickCmd(delay time.Duration) tea.Cmd

TickCmd returns a command that sends a tick after a delay

func WaitForPlaybackCmd added in v0.0.2

func WaitForPlaybackCmd(resultCh <-chan player.ScrobbleResult) tea.Cmd

WaitForPlaybackCmd waits for the playback to finish and returns a message

Types

type ApplicationState

type ApplicationState int

ApplicationState represents the current state of the application

const (
	StateBrowsing ApplicationState = iota
	StateHelp
	StateConfirmLogout
	StateConfirmResume
	StateInspecting
)

type ClearLibraryStatusMsg

type ClearLibraryStatusMsg struct {
	LibraryID string
}

ClearLibraryStatusMsg signals that the success indicator should be removed

type ClearStatusMsg

type ClearStatusMsg struct{}

ClearStatusMsg clears the status bar message

type ColumnStack

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

ColumnStack manages the stack of navigable columns in Miller Columns layout. The stack contains list columns only - the Inspector is a separate view projection.

Visual representation:

Root:     [Empty | Libraries | Inspector]
Library:  [Libraries | Movies | Inspector]
Show:     [TV Shows | Breaking Bad | Inspector]
Season:   [Breaking Bad | Season 1 | Inspector]

The "middle" column (top of stack) is always focused. The "left" column shows parent context. The "right" column (Inspector) shows details for the selection in middle column.

func NewColumnStack

func NewColumnStack() *ColumnStack

NewColumnStack creates a new empty column stack

func (*ColumnStack) CanGoBack

func (cs *ColumnStack) CanGoBack() bool

CanGoBack returns true if we can navigate back (not at root)

func (*ColumnStack) Depth

func (cs *ColumnStack) Depth() int

Depth returns the navigation depth (0 = root, 1 = first drill, etc.)

func (*ColumnStack) FindColumn added in v1.0.0

func (cs *ColumnStack) FindColumn(contentID string) *components.ListColumn

FindColumn returns the column with the matching content ID, or nil if not found

func (*ColumnStack) Get

func (cs *ColumnStack) Get(idx int) *components.ListColumn

Get returns the column at the given index (0 = bottom/oldest)

func (*ColumnStack) Len

func (cs *ColumnStack) Len() int

Len returns the number of columns in the stack

func (*ColumnStack) Parent

func (cs *ColumnStack) Parent() *components.ListColumn

Parent returns the parent column (second from top), or nil if at root

func (*ColumnStack) Pop

func (cs *ColumnStack) Pop() (*components.ListColumn, int)

Pop removes and returns the top column, along with the saved cursor position. Returns nil if stack would become empty (must have at least 1 column).

func (*ColumnStack) Push

func (cs *ColumnStack) Push(col *components.ListColumn, saveCursor int)

Push adds a new column to the stack, saving the current cursor position

func (*ColumnStack) Reset

func (cs *ColumnStack) Reset(col *components.ListColumn)

Reset resets the stack to a single column (used when switching libraries)

func (*ColumnStack) Top

func (cs *ColumnStack) Top() *components.ListColumn

Top returns the topmost (current/focused) column

func (*ColumnStack) UpdateSpinnerFrame

func (cs *ColumnStack) UpdateSpinnerFrame(frame int)

UpdateSpinnerFrame updates the spinner frame for all columns

func (*ColumnStack) UpdateTop

func (cs *ColumnStack) UpdateTop(col *components.ListColumn)

UpdateTop replaces the top column with the given column. This preserves encapsulation by avoiding direct access to the columns slice.

type ContinueWatchingLoadedMsg added in v1.1.0

type ContinueWatchingLoadedMsg struct {
	Items []*domain.MediaItem
}

ContinueWatchingLoadedMsg signals that continue watching items have been loaded

type EpisodesLoadedMsg

type EpisodesLoadedMsg struct {
	Episodes []*domain.MediaItem
	SeasonID string
}

EpisodesLoadedMsg signals that episodes have been loaded

type ErrMsg

type ErrMsg struct {
	Err     error
	Context string
}

ErrMsg represents an error

func (ErrMsg) Error

func (e ErrMsg) Error() string

Error implements the error interface

type KeyMap

type KeyMap struct {
	// Navigation
	Up       key.Binding
	Down     key.Binding
	Right    key.Binding
	Enter    key.Binding
	Back     key.Binding
	HalfUp   key.Binding
	HalfDown key.Binding
	PageUp   key.Binding
	PageDown key.Binding
	Home     key.Binding
	End      key.Binding

	// Actions
	Quit            key.Binding
	Help            key.Binding
	Escape          key.Binding
	Filter          key.Binding
	GlobalSearch    key.Binding
	Sort            key.Binding
	Refresh         key.Binding
	RefreshAll      key.Binding
	MarkWatched     key.Binding
	MarkUnwatched   key.Binding
	Play            key.Binding
	ToggleInspector key.Binding
	Logout          key.Binding
	PlaylistModal   key.Binding
	Delete          key.Binding
	NewPlaylist     key.Binding
	Queue           key.Binding
	NextEpisode     key.Binding

	// Confirmations
	Confirm key.Binding
	Deny    key.Binding
}

KeyMap defines all key bindings for the application

func DefaultKeyMap

func DefaultKeyMap() KeyMap

DefaultKeyMap returns the default key bindings

type LibrariesLoadedMsg

type LibrariesLoadedMsg struct {
	Libraries []domain.Library
}

LibrariesLoadedMsg signals that libraries have been loaded

type LibrarySyncProgressMsg

type LibrarySyncProgressMsg struct {
	LibraryID   string
	LibraryType string
	Loaded      int
	Total       int
	Done        bool
	FromCache   bool
	Error       error
	NextCmd     tea.Cmd // Continuation command for streaming
}

LibrarySyncProgressMsg sent for each chunk during streaming sync

type LogoutCompleteMsg

type LogoutCompleteMsg struct {
	Error error
}

LogoutCompleteMsg signals that logout has been completed

type MarkUnwatchedMsg

type MarkUnwatchedMsg struct {
	Title     string
	LibraryID string
}

MarkUnwatchedMsg signals a request to mark an item as unwatched

type MarkWatchedMsg

type MarkWatchedMsg struct {
	Title     string
	LibraryID string
}

MarkWatchedMsg signals a request to mark an item as watched

type MixedLibraryLoadedMsg

type MixedLibraryLoadedMsg struct {
	Items     []domain.ListItem
	LibraryID string
}

MixedLibraryLoadedMsg signals that mixed library content has been loaded

type Model

type Model struct {
	// Application state
	State ApplicationState
	Ready bool

	// Cache reads (View-safe)
	Store domain.Store

	// Network coordination (concrete types, not interfaces)
	LibraryService  *library.Service
	PlaylistService *playlist.Service

	// Other services
	SearchSvc   *search.Service
	PlaybackSvc *player.Service

	// UI Components - Miller Columns
	ColumnStack   *ColumnStack             // Stack of navigable list columns
	Inspector     components.Inspector     // View projection (always shows details for middle column selection)
	GlobalSearch  components.GlobalSearch  // Search modal
	SortModal     components.SortModal     // Sort field selector
	PlaylistModal components.PlaylistModal // Playlist management modal
	InputModal    components.InputModal    // Simple text input modal

	// Data
	Libraries []domain.Library

	// Dimensions
	Width  int
	Height int

	// UI state
	StatusMsg     string
	StatusIsErr   bool
	Loading       bool
	SpinnerFrame  int
	ShowInspector bool // Toggle inspector visibility (default true)

	// Sync state
	LibraryStates map[string]components.LibrarySyncState // Tracks progress per library
	SyncingCount  int                                    // Libraries still syncing
	MultiLibSync  bool                                   // True when syncing multiple libraries (R / startup)

	// UI preferences from config
	UIConfig  config.UIConfig
	AppConfig *config.Config
	Version   string

	PendingSelectionID string // ID of item to select after load completes
	// contains filtered or unexported fields
}

Model is the main Bubble Tea model for the application

func NewModel

func NewModel(
	store domain.Store,
	librarySvc *library.Service,
	playlistSvc *playlist.Service,
	searchSvc *search.Service,
	playbackSvc *player.Service,
	appConfig *config.Config,
	uiConfig config.UIConfig,
	version string,
) Model

NewModel creates a new application model

func (Model) Init

func (m Model) Init() tea.Cmd

Init initializes the application

func (Model) Update

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

Update handles all messages

func (Model) View

func (m Model) View() string

View renders the application

type MoviesLoadedMsg

type MoviesLoadedMsg struct {
	Movies    []*domain.MediaItem
	LibraryID string
}

MoviesLoadedMsg signals that movies have been loaded

type NavAwaitKind int

NavAwaitKind specifies what async load the plan is waiting for

const (
	AwaitNone     NavAwaitKind = iota
	AwaitMovies                // AwaitID = LibraryID
	AwaitShows                 // AwaitID = LibraryID
	AwaitMixed                 // AwaitID = LibraryID (mixed content library)
	AwaitSeasons               // AwaitID = ShowID
	AwaitEpisodes              // AwaitID = SeasonID
)
type NavPlan struct {
	Targets     []NavTarget
	CurrentStep int
	AwaitKind   NavAwaitKind
	AwaitID     string
}

NavPlan represents a multi-step navigation flow

func (p *NavPlan) Advance()
func (p *NavPlan) Current() *NavTarget
func (p *NavPlan) IsComplete() bool
type NavTarget struct {
	ID string // item ID to select (empty = no-op, just land)
}

NavTarget represents a single navigation step

type NavigationContext struct {
	LibraryID   string
	LibraryName string
	MovieID     string
	ShowID      string
	ShowTitle   string
	SeasonID    string
	EpisodeID   string
}

NavigationContext contains information needed to navigate to an item This is purely a TUI concern - the service layer provides FilterItem with LibraryID, and the TUI decides how to navigate based on that.

type PlaybackFinishedMsg added in v0.0.2

type PlaybackFinishedMsg struct {
	Item       domain.MediaItem
	Title      string
	AutoMarked bool // true if auto-scrobbled as watched
	Err        error
}

PlaybackFinishedMsg signals that playback has ended

type PlaybackStartedMsg

type PlaybackStartedMsg struct {
	Item   domain.MediaItem
	Handle player.PlaybackHandle
}

PlaybackStartedMsg signals that playback has started (player launched)

type PlaybackStatusMsg added in v0.0.2

type PlaybackStatusMsg struct {
	Message  string
	StatusCh <-chan string
}

PlaybackStatusMsg signals a real-time status update during playback

type PlaylistCreatedMsg

type PlaylistCreatedMsg struct {
	Playlist *domain.Playlist
	Error    error
}

PlaylistCreatedMsg signals that a new playlist was created

type PlaylistDeletedMsg

type PlaylistDeletedMsg struct {
	PlaylistID string
	Error      error
}

PlaylistDeletedMsg signals that a playlist was deleted

type PlaylistItemsLoadedMsg

type PlaylistItemsLoadedMsg struct {
	Items      []*domain.MediaItem
	PlaylistID string
}

PlaylistItemsLoadedMsg signals that playlist items have been loaded

type PlaylistModalDataMsg

type PlaylistModalDataMsg struct {
	Playlists  []*domain.Playlist
	Membership map[string]bool
	Item       *domain.MediaItem
}

PlaylistModalDataMsg contains data for the playlist modal

type PlaylistUpdatedMsg

type PlaylistUpdatedMsg struct {
	PlaylistID string
	Error      error
}

PlaylistUpdatedMsg signals that a playlist was updated (item added/removed)

type PlaylistsLoadedMsg

type PlaylistsLoadedMsg struct {
	Playlists []*domain.Playlist
}

PlaylistsLoadedMsg signals that playlists have been loaded

type QueueUpdatedMsg

type QueueUpdatedMsg struct {
	Message string
	Error   error
}

QueueUpdatedMsg signals local queue changes.

type RefreshCurrentMsg added in v1.1.2

type RefreshCurrentMsg struct {
	LibraryID string
}

RefreshCurrentMsg triggers a refresh of the current view

type RemoteSearchLoadedMsg

type RemoteSearchLoadedMsg struct {
	Query   string
	Results []search.FilterResult
	Error   error
}

RemoteSearchLoadedMsg carries server-side fallback search results.

type SeasonForPlaybackLoadedMsg added in v1.1.0

type SeasonForPlaybackLoadedMsg struct {
	Item     *domain.MediaItem
	Episodes []*domain.MediaItem
	Resume   bool
}

SeasonForPlaybackLoadedMsg signals that a full season has been loaded for playback

type SeasonsLoadedMsg

type SeasonsLoadedMsg struct {
	Seasons []*domain.Season
	ShowID  string
}

SeasonsLoadedMsg signals that seasons have been loaded

type ShowsLoadedMsg

type ShowsLoadedMsg struct {
	Shows     []*domain.Show
	LibraryID string
}

ShowsLoadedMsg signals that shows have been loaded

type StatusMsg

type StatusMsg struct {
	Message string
	IsError bool
}

StatusMsg sets a temporary status message

type TickMsg

type TickMsg struct{}

TickMsg is a general tick message for animations

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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