package tui import ( "strings" "gitgud.io/mike/mpv-manager/pkg/config" "gitgud.io/mike/mpv-manager/pkg/installer" "gitgud.io/mike/mpv-manager/pkg/locale" "gitgud.io/mike/mpv-manager/pkg/platform" "gitgud.io/mike/mpv-manager/pkg/version" "github.com/charmbracelet/bubbles/list" "github.com/charmbracelet/bubbles/textinput" "github.com/charmbracelet/bubbles/viewport" "github.com/charmbracelet/lipgloss" ) const ( banner = " ▶ MPV.Rocks" ) var ( purple = lipgloss.Color("#9B59B6") lightPurple = lipgloss.Color("#BB8FCE") darkPurple = lipgloss.Color("#6C3483") white = lipgloss.Color("#FFFFFF") black = lipgloss.Color("#000000") gray = lipgloss.Color("#95A5A6") green = lipgloss.Color("#2ECC71") blue = lipgloss.Color("#085dd3") red = lipgloss.Color("#E74C3C") yellow = lipgloss.Color("#F1C40F") orange = lipgloss.Color("#E67E22") bannerStyle = lipgloss.NewStyle(). Foreground(white). Background(purple). Bold(true) titleStyle = lipgloss.NewStyle(). Foreground(purple). Bold(true) subtitleStyle = lipgloss.NewStyle(). Foreground(lightPurple) infoStyle = lipgloss.NewStyle(). Foreground(gray) successStyle = lipgloss.NewStyle(). Foreground(green) errorStyle = lipgloss.NewStyle(). Foreground(red) warningStyle = lipgloss.NewStyle(). Foreground(yellow) helpStyle = lipgloss.NewStyle(). Foreground(gray) listStyle = lipgloss.NewStyle(). Border(lipgloss.RoundedBorder()). BorderForeground(purple). Padding(1) selectedItemStyle = lipgloss.NewStyle(). Foreground(green). Bold(true) itemStyle = lipgloss.NewStyle(). Foreground(white) footerStyle = lipgloss.NewStyle(). Foreground(gray) linkStyle = lipgloss.NewStyle(). Foreground(blue). Underline(true) recommendedBadgeStyle = lipgloss.NewStyle(). Foreground(blue). Bold(true) av2Style = lipgloss.NewStyle().Foreground(red).Bold(true) vvcStyle = lipgloss.NewStyle().Foreground(blue).Bold(true) av1Style = lipgloss.NewStyle().Foreground(yellow).Bold(true) vp9Style = lipgloss.NewStyle().Foreground(green).Bold(true) hevcStyle = lipgloss.NewStyle().Foreground(blue).Bold(true) vp8Style = lipgloss.NewStyle().Foreground(green).Bold(true) proresStyle = lipgloss.NewStyle().Foreground(purple).Bold(true) vc1Style = lipgloss.NewStyle().Foreground(orange).Bold(true) avcStyle = lipgloss.NewStyle().Foreground(red).Bold(true) mpeg2Style = lipgloss.NewStyle().Foreground(purple).Bold(true) ) var codecDisplayOrder = []string{"av2", "vvc", "av1", "vp9", "hevc", "vp8", "prores", "vc1", "avc", "mpeg2"} var codecDisplayNames = map[string]string{ "av2": "AV2", "vvc": "VVC", "av1": "AV1", "vp9": "VP9", "hevc": "HEVC", "vp8": "VP8", "prores": "ProRes", "vc1": "VC-1", "avc": "AVC", "mpeg2": "MPEG-2", } var codecStyles = map[string]lipgloss.Style{ "av2": av2Style, "vvc": vvcStyle, "av1": av1Style, "vp9": vp9Style, "hevc": hevcStyle, "vp8": vp8Style, "prores": proresStyle, "vc1": vc1Style, "avc": avcStyle, "mpeg2": mpeg2Style, } type State int const ( StateMainMenu State = iota StateInstallMenu StateInstalling StateSuccess StateError StateUpdateCheck StateUpdateInstaller StateUpdates StateUpdateList StateRestoreConfig StateUninstall StateInstallToPath StateLanguagePreferences StateLanguageMajorSelect StateLanguageRegionSelect StateLanguageSelectedList StateLanguageConfirm StateConfigOptions StateHWAOptions StateScreenshotOptions StateFileAssociations StateFileAssociationsMenu StateWebUIMenu // Screenshot editing sub-states StateScreenshotFormatEdit StateScreenshotTagColorspaceEdit StateScreenshotHighBitDepthEdit StateScreenshotTemplateEdit StateScreenshotDirectoryEdit StateScreenshotJpegQualityEdit StateScreenshotPngCompressionEdit StateScreenshotWebpLosslessEdit StateScreenshotWebpQualityEdit StateScreenshotJxlDistanceEdit StateScreenshotAvifEncoderEdit StateScreenshotAvifPixfmtEdit StateScreenshotAvifOptsEdit // Additional config editing sub-states StateSavePositionOnQuitEdit // Hotkeys view StateHotkeys StateHotkeysCategory StateHotkeysPreset // Keybinding preset selection StateUISelect // UI overlay selection state ) type InstallOperationType int const ( OpTypeNone InstallOperationType = iota OpTypeInstall OpTypeUpdate OpTypeConfig ) type Model struct { state State platform PlatformInfo platformInfo *platform.Platform installer *installer.Installer methods []installer.InstallMethod menuList list.Model installMenuList list.Model updateList list.Model uninstallList list.Model restoreConfigList list.Model majorLangList list.Model regionLangList list.Model selectedLangList list.Model langPreferenceList list.Model configOptionsList list.Model hwaOptionsList list.Model screenshotOptionsList list.Model fileAssociationsList list.Model webUIList list.Model hotkeysList list.Model hotkeysCategoryList list.Model hotkeysPresetList list.Model viewport viewport.Model outputViewport viewport.Model statusMessage string errorMessage string installProgress int showHelp bool showVersion bool selectedMethod string selectedMethodID string installing bool versionCheckResult *version.VersionCheckResult updateCheckResult *version.UpdateCheckResult installOutput []string currentCommand string installComplete bool installError error installSuccess bool loadingConfig bool spinnerFrame int autoScroll bool outputChan <-chan string installDone <-chan error termWidth int termHeight int uninstallItems []list.Item selectedUninstall *config.InstalledApp selectedUpdateItem *updateItem langPreferenceType string currentMajorLanguage *locale.LocaleEntry selectedLanguageCodes []string loadedLocales []locale.LocaleEntry currentMPVAudioLangs []string currentMPVSubtitleLangs []string updateItems []list.Item restoreConfigItems []list.Item selectedRestoreBackup *version.ConfigBackupItem installerCheckType string downloadWritten int64 downloadTotal int64 progressChan <-chan downloadProgressMsg selectedHWAOption string currentHWAValue string installOperationType InstallOperationType postInstallMessage string langSearchTerm string isLangSearching bool langSearchResults []locale.LanguageOption // Screenshot editing fields selectedScreenshotOption string screenshotTextInput textinput.Model screenshotValueList list.Model // Hotkeys view fields hotkeySearchTerm string hotkeySearchResults []hotkeyItem presetStatusMessage string // Status message after preset apply // UI selection fields uiSelectList list.Model pendingMethodID string // Method waiting for UI selection pendingAppName string // App name for display selectedUIType string // Selected UI overlay type } // hotkeyItem implements list.Item for hotkey display type hotkeyItem struct { keys string description string category string } func (h hotkeyItem) Title() string { return h.keys } func (h hotkeyItem) Description() string { return h.description + " (" + h.category + ")" } func (h hotkeyItem) FilterValue() string { return h.keys + " " + h.description + " " + h.category } func formatCodecsDisplay(codecs []string) string { if len(codecs) == 0 { return "Unknown" } var ordered []string for _, codecKey := range codecDisplayOrder { for _, detectedCodec := range codecs { if strings.ToLower(detectedCodec) == codecKey || strings.ToLower(detectedCodec) == codecKey+"265" || strings.ToLower(detectedCodec) == codecKey+"266" || strings.ToLower(detectedCodec) == "h26"+codecKey[1:] { displayName := codecDisplayNames[codecKey] style := codecStyles[codecKey] ordered = append(ordered, style.Render(displayName)) break } } } if len(ordered) == 0 { return "Unknown" } return strings.Join(ordered, " ") } func getVendorColor(brand string) lipgloss.Style { switch strings.ToLower(brand) { case "amd": return lipgloss.NewStyle().Foreground(red).Bold(true) case "nvidia": return lipgloss.NewStyle().Foreground(green).Bold(true) case "intel": return lipgloss.NewStyle().Foreground(blue).Bold(true) case "apple": return titleStyle case "arm", "armel", "qualcomm", "mediatek", "samsung", "hisilicon": return lipgloss.NewStyle().Foreground(orange).Bold(true) default: return infoStyle } }