package tui import ( "sort" "strings" "gitgud.io/mike/mpv-manager/pkg/config" "gitgud.io/mike/mpv-manager/pkg/installer" "gitgud.io/mike/mpv-manager/pkg/platform" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) func (m *Model) Init() tea.Cmd { return checkForUpdateCmd() } func NewModel() *Model { platformInfo := PlatformInfo{ AppVersion: "", OSType: "Detecting...", Arch: "Detecting...", Distro: "", DistroFamily: "", CPU: "Detecting...", GPU: "Detecting...", HWA: "auto", } outputViewport := viewport.New(0, 0) outputViewport.Style = lipgloss.NewStyle() delegate := list.NewDefaultDelegate() delegate.ShowDescription = true delegate.Styles.SelectedTitle = selectedItemStyle delegate.Styles.SelectedDesc = infoStyle // Initialize textinput for screenshot string options ti := textinput.New() ti.Placeholder = "Enter value..." ti.CharLimit = 156 ti.Width = 50 return &Model{ state: StateMainMenu, platform: platformInfo, menuList: list.New(nil, list.NewDefaultDelegate(), 0, 0), installMenuList: list.New(nil, list.NewDefaultDelegate(), 0, 0), uninstallList: list.New(nil, delegate, 0, 0), updateList: list.New(nil, list.NewDefaultDelegate(), 0, 0), restoreConfigList: list.New(nil, list.NewDefaultDelegate(), 0, 0), outputViewport: outputViewport, statusMessage: "Initializing...", installOutput: []string{}, autoScroll: true, majorLangList: list.New(nil, list.NewDefaultDelegate(), 0, 0), regionLangList: list.New(nil, list.NewDefaultDelegate(), 0, 0), selectedLangList: list.New(nil, list.NewDefaultDelegate(), 0, 0), langPreferenceList: list.New(nil, list.NewDefaultDelegate(), 0, 0), configOptionsList: list.New(nil, list.NewDefaultDelegate(), 0, 0), hwaOptionsList: list.New(nil, list.NewDefaultDelegate(), 0, 0), webUIList: list.New(nil, list.NewDefaultDelegate(), 0, 0), screenshotOptionsList: list.New(nil, list.NewDefaultDelegate(), 0, 0), uiSelectList: list.New(nil, list.NewDefaultDelegate(), 0, 0), installerCheckType: "", screenshotTextInput: ti, screenshotValueList: list.New(nil, list.NewDefaultDelegate(), 0, 0), } } func (m *Model) SetPlatform(info PlatformInfo) { m.platform = info } func NewMenuItemsFromInstaller(methods []installer.InstallMethod) []menuItem { items := make([]menuItem, len(methods)) for i, method := range methods { items[i] = menuItem{ id: method.ID, title: method.Name, description: method.Description, recommended: method.Recommended, methodID: method.ID, // Set methodID for badge determination } } return items } func NewMainMenuItems(installedAppsCount int) []menuItem { inPath := isInstallMPVInPATH() var pathTitle, pathDesc string if inPath { pathTitle = "🗑️ Remove mpv-manager from PATH" pathDesc = "Remove mpv-manager from your system PATH" } else { pathTitle = "📥 Add mpv-manager to your PATH" pathDesc = "Add mpv-manager to your system PATH for easy access" } if installedAppsCount == 0 { return []menuItem{ { id: "mpv-manager", title: "📥 Install an MPV Player", description: "See options for installing MPV and MPV-based media players", recommended: false, }, { id: "updates", title: "🔄 Updates", description: "Update installer or load config backups", recommended: false, }, { id: "set-language-preferences", title: "💬 Set Language Preferences", description: "Configure audio and subtitle language priority for MPV", recommended: false, }, { id: "config-options", title: "⚙️ MPV Config Options", description: "Configure MPV player settings and hardware acceleration", recommended: false, }, { id: "hotkeys", title: "⌨️ Keyboard Shortcuts", description: "View all MPV keyboard shortcuts", recommended: false, }, { id: "web-ui", title: "🌐 Web UI", description: "Create desktop shortcuts for Web UI mode", recommended: false, }, { id: "install-to-path", title: pathTitle, description: pathDesc, recommended: false, }, } } menuItems := []menuItem{ { id: "updates", title: "🔄 Updates", description: "Update MPV installer or load config backups", recommended: false, }, { id: "set-language-preferences", title: "💬 Set Language Preferences", description: "Configure audio and subtitle language priority for MPV", recommended: false, }, { id: "config-options", title: "⚙️ MPV Config Options", description: "Configure MPV player settings and hardware acceleration", recommended: false, }, { id: "hotkeys", title: "⌨️ Keyboard Shortcuts", description: "View all MPV keyboard shortcuts", recommended: false, }, { id: "mpv-manager", title: "📥 Install an MPV Player", description: "See options for installing MPV and MPV-based media players", recommended: false, }, { id: "unmpv-manager", title: "🗑️ Uninstall an MPV Player", description: "Remove MPV clients", recommended: false, }, { id: "web-ui", title: "🌐 Web UI", description: "Create desktop shortcuts for Web UI mode", recommended: false, }, { id: "install-to-path", title: pathTitle, description: pathDesc, recommended: false, }, } return menuItems } func (m *Model) SetInstaller(inst *installer.Installer, p *platform.Platform) { m.installer = inst m.platformInfo = p } func (m *Model) SetMenuItems(items []menuItem, title string) { listItems := make([]list.Item, len(items)) for i, item := range items { listItems[i] = item } m.menuList = m.createStyledList(title, listItems) } func (m *Model) RefreshMainMenu() { apps := config.GetInstalledApps() menuItems := NewMainMenuItems(len(apps)) m.SetMenuItems(menuItems, "Main Menu") } func (m *Model) SetInstallMenuItems(items []menuItem, title string) { listItems := make([]list.Item, len(items)) for i, item := range items { listItems[i] = item } m.installMenuList = m.createStyledList(title, listItems) } func (m *Model) SetInstallerWithMethods(inst *installer.Installer, p *platform.Platform, methods []installer.InstallMethod) { m.installer = inst m.platformInfo = p m.methods = methods } func (m *Model) setupInstallMenuList() { if len(m.methods) == 0 { m.statusMessage = "No installation methods available" return } items := NewMenuItemsFromInstaller(m.methods) // Sort items: MPV methods (Best Playback) first, then GUI methods (Easy UI) sort.Slice(items, func(i, j int) bool { iIsMPV := isMPVMethod(items[i].methodID) jIsMPV := isMPVMethod(items[j].methodID) // MPV methods come first if iIsMPV && !jIsMPV { return true } if !iIsMPV && jIsMPV { return false } // Within same category, sort by name if items[i].title != items[j].title { return items[i].title < items[j].title } // Same name, sort by ID (e.g., package before flatpak) return items[i].id < items[j].id }) listItems := make([]list.Item, len(items)) for i, item := range items { listItems[i] = item } m.installMenuList = m.createStyledList("Select Installation Method", listItems) } func (m *Model) SetState(state State) { m.state = state } func (m *Model) SetStatusMessage(msg string) { m.statusMessage = msg } func (m *Model) SetErrorMessage(msg string) { m.errorMessage = msg } func (m *Model) SetProgress(progress int) { m.installProgress = progress } func (m *Model) Installing() bool { return m.installing } func (m *Model) GetSelectedMethodID() string { return m.selectedMethodID } func (m *Model) GetSelectedMethod() string { return m.selectedMethod } func (m *Model) updateOutputViewport() { content := strings.Join(m.installOutput, "\n") m.outputViewport.SetContent(content) } func (m *Model) createStyledList(title string, items []list.Item) list.Model { delegate := list.NewDefaultDelegate() delegate.ShowDescription = true delegate.Styles.SelectedTitle = selectedItemStyle delegate.Styles.SelectedDesc = infoStyle l := list.New(items, delegate, 0, 0) l.Title = title l.Styles.Title = titleStyle l.Styles.FilterPrompt = infoStyle l.Styles.FilterCursor = selectedItemStyle if m.termWidth > 0 && m.termHeight > 0 { l.SetWidth(m.termWidth - 10) l.SetHeight(m.termHeight - 15) } return l } func (m *Model) setupWebUIList() { items := []list.Item{ menuItem{ id: "create-webui-shortcut", title: "🌐 Create Web UI Shortcut", description: "Create a shortcut to launch MPV Installer in Web UI mode", recommended: false, }, } m.webUIList = m.createStyledList("Web UI Options", items) }