From a2b924d7e9492b978ad6dc33b6c1ffcb304b4bc5 Mon Sep 17 00:00:00 2001 From: Nippy Date: Sat, 9 May 2026 13:33:09 +0200 Subject: raveos update --- .../DankMaterialShell/core/internal/tui/app.go | 240 ------------ .../DankMaterialShell/core/internal/tui/banner.go | 21 -- .../core/internal/tui/messages.go | 39 -- .../DankMaterialShell/core/internal/tui/states.go | 22 -- .../DankMaterialShell/core/internal/tui/styles.go | 124 ------- .../core/internal/tui/views_config.go | 382 ------------------- .../core/internal/tui/views_dependencies.go | 304 ---------------- .../core/internal/tui/views_gentoo_gcc.go | 103 ------ .../core/internal/tui/views_gentoo_use_flags.go | 92 ----- .../core/internal/tui/views_install.go | 405 --------------------- .../core/internal/tui/views_password.go | 338 ----------------- .../core/internal/tui/views_selection.go | 224 ------------ .../core/internal/tui/views_welcome.go | 216 ----------- 13 files changed, 2510 deletions(-) delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go delete mode 100644 raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go (limited to 'raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui') diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go deleted file mode 100644 index d07430a..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/app.go +++ /dev/null @@ -1,240 +0,0 @@ -package tui - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/charmbracelet/bubbles/spinner" - "github.com/charmbracelet/bubbles/textinput" - tea "github.com/charmbracelet/bubbletea" -) - -type Model struct { - version string - state ApplicationState - - osInfo *distros.OSInfo - dependencies []deps.Dependency - err error - - spinner spinner.Model - passwordInput textinput.Model - width int - height int - isLoading bool - styles Styles - - logMessages []string - logChan chan string - logFilePath string - packageProgressChan chan packageInstallProgressMsg - packageProgress packageInstallProgressMsg - installationLogs []string - showDebugLogs bool - - selectedWM int - selectedTerminal int - selectedDep int - selectedConfig int - reinstallItems map[string]bool - disabledItems map[string]bool - replaceConfigs map[string]bool - skipGentooUseFlags bool - sudoPassword string - existingConfigs []ExistingConfigInfo - fingerprintFailed bool -} - -func NewModel(version string, logFilePath string) Model { - s := spinner.New() - s.Spinner = spinner.Dot - - theme := TerminalTheme() - styles := NewStyles(theme) - s.Style = styles.SpinnerStyle - - pi := textinput.New() - pi.Placeholder = "Enter sudo password" - pi.EchoMode = textinput.EchoPassword - pi.EchoCharacter = '•' - pi.Focus() - - logChan := make(chan string, 1000) - packageProgressChan := make(chan packageInstallProgressMsg, 100) - - return Model{ - version: version, - state: StateWelcome, - spinner: s, - passwordInput: pi, - isLoading: true, - styles: styles, - - logMessages: []string{}, - logChan: logChan, - logFilePath: logFilePath, - packageProgressChan: packageProgressChan, - packageProgress: packageInstallProgressMsg{ - progress: 0.0, - step: "Initializing package installation", - isComplete: false, - }, - showDebugLogs: false, - selectedWM: 0, - selectedTerminal: 0, // Default to Ghostty - selectedDep: 0, - selectedConfig: 0, - reinstallItems: make(map[string]bool), - disabledItems: make(map[string]bool), - replaceConfigs: make(map[string]bool), - installationLogs: []string{}, - } -} - -func (m Model) GetLogChan() <-chan string { - return m.logChan -} - -func (m Model) Init() tea.Cmd { - return tea.Batch( - m.spinner.Tick, - m.listenForLogs(), - m.detectOS(), - ) -} - -func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "ctrl+c": - return m, tea.Quit - case "ctrl+d": - // Toggle debug logs view (except during password input states) - if m.state != StatePasswordPrompt && m.state != StateFingerprintAuth { - m.showDebugLogs = !m.showDebugLogs - return m, nil - } - } - } - - if tickMsg, ok := msg.(spinner.TickMsg); ok { - var cmd tea.Cmd - m.spinner, cmd = m.spinner.Update(tickMsg) - return m, tea.Batch(cmd, m.listenForLogs()) - } - - if sizeMsg, ok := msg.(tea.WindowSizeMsg); ok { - m.width = sizeMsg.Width - m.height = sizeMsg.Height - } - - if logMsg, ok := msg.(logMsg); ok { - m.logMessages = append(m.logMessages, logMsg.message) - return m, m.listenForLogs() - } - - switch m.state { - case StateWelcome: - return m.updateWelcomeState(msg) - case StateSelectWindowManager: - return m.updateSelectWindowManagerState(msg) - case StateSelectTerminal: - return m.updateSelectTerminalState(msg) - case StateDetectingDeps: - return m.updateDetectingDepsState(msg) - case StateDependencyReview: - return m.updateDependencyReviewState(msg) - case StateGentooUseFlags: - return m.updateGentooUseFlagsState(msg) - case StateGentooGCCCheck: - return m.updateGentooGCCCheckState(msg) - case StateAuthMethodChoice: - return m.updateAuthMethodChoiceState(msg) - case StateFingerprintAuth: - return m.updateFingerprintAuthState(msg) - case StatePasswordPrompt: - return m.updatePasswordPromptState(msg) - case StateInstallingPackages: - return m.updateInstallingPackagesState(msg) - case StateConfigConfirmation: - return m.updateConfigConfirmationState(msg) - case StateDeployingConfigs: - return m.updateDeployingConfigsState(msg) - case StateInstallComplete: - return m.updateInstallCompleteState(msg) - case StateError: - return m.updateErrorState(msg) - default: - return m, m.listenForLogs() - } -} - -func (m Model) View() string { - // If debug logs are shown, show that view regardless of state - if m.showDebugLogs { - return m.viewDebugLogs() - } - - switch m.state { - case StateWelcome: - return m.viewWelcome() - case StateSelectWindowManager: - return m.viewSelectWindowManager() - case StateSelectTerminal: - return m.viewSelectTerminal() - case StateDetectingDeps: - return m.viewDetectingDeps() - case StateDependencyReview: - return m.viewDependencyReview() - case StateGentooUseFlags: - return m.viewGentooUseFlags() - case StateGentooGCCCheck: - return m.viewGentooGCCCheck() - case StateAuthMethodChoice: - return m.viewAuthMethodChoice() - case StateFingerprintAuth: - return m.viewFingerprintAuth() - case StatePasswordPrompt: - return m.viewPasswordPrompt() - case StateInstallingPackages: - return m.viewInstallingPackages() - case StateConfigConfirmation: - return m.viewConfigConfirmation() - case StateDeployingConfigs: - return m.viewDeployingConfigs() - case StateInstallComplete: - return m.viewInstallComplete() - case StateError: - return m.viewError() - default: - return m.viewWelcome() - } -} - -func (m Model) listenForLogs() tea.Cmd { - return func() tea.Msg { - select { - case msg, ok := <-m.logChan: - if !ok { - return nil - } - return logMsg{message: msg} - default: - return nil - } - } -} - -func (m Model) detectOS() tea.Cmd { - return func() tea.Msg { - info, err := distros.GetOSInfo() - osInfoMsg := &distros.OSInfo{} - if info != nil { - osInfoMsg.Distribution = info.Distribution - osInfoMsg.Version = info.Version - osInfoMsg.VersionID = info.VersionID - osInfoMsg.PrettyName = info.PrettyName - osInfoMsg.Architecture = info.Architecture - } - return osInfoCompleteMsg{info: osInfoMsg, err: err} - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go deleted file mode 100644 index e3a2ca3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/banner.go +++ /dev/null @@ -1,21 +0,0 @@ -package tui - -import "github.com/charmbracelet/lipgloss" - -func (m Model) renderBanner() string { - logo := ` -██████╗ █████╗ ███╗ ██╗██╗ ██╗ -██╔══██╗██╔══██╗████╗ ██║██║ ██╔╝ -██║ ██║███████║██╔██╗ ██║█████╔╝ -██║ ██║██╔══██║██║╚██╗██║██╔═██╗ -██████╔╝██║ ██║██║ ╚████║██║ ██╗ -╚═════╝ ╚═╝ ╚═╝╚═╝ ╚═══╝╚═╝ ╚═╝ ` - - theme := TerminalTheme() - style := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginBottom(1) - - return style.Render(logo) -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go deleted file mode 100644 index 7c03098..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/messages.go +++ /dev/null @@ -1,39 +0,0 @@ -package tui - -import ( - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" -) - -type logMsg struct { - message string -} - -type osInfoCompleteMsg struct { - info *distros.OSInfo - err error -} - -type depsDetectedMsg struct { - deps []deps.Dependency - err error -} - -type packageInstallProgressMsg struct { - progress float64 - step string - isComplete bool - needsSudo bool - commandInfo string - logOutput string - error error -} - -type packageProgressCompletedMsg struct{} - -type passwordValidMsg struct { - password string - valid bool -} - -type delayCompleteMsg struct{} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go deleted file mode 100644 index 6e9e194..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/states.go +++ /dev/null @@ -1,22 +0,0 @@ -package tui - -type ApplicationState int - -const ( - StateWelcome ApplicationState = iota - StateSelectWindowManager - StateSelectTerminal - StateDetectingDeps - StateDependencyReview - StateGentooUseFlags - StateGentooGCCCheck - StateAuthMethodChoice - StateFingerprintAuth - StatePasswordPrompt - StateInstallingPackages - StateConfigConfirmation - StateDeployingConfigs - StateInstallComplete - StateFinalComplete - StateError -) diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go deleted file mode 100644 index 45a9dae..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/styles.go +++ /dev/null @@ -1,124 +0,0 @@ -package tui - -import ( - "github.com/charmbracelet/bubbles/progress" - "github.com/charmbracelet/lipgloss" -) - -type AppTheme struct { - Primary string - Secondary string - Accent string - Text string - Subtle string - Error string - Warning string - Success string - Background string - Surface string -} - -func TerminalTheme() AppTheme { - return AppTheme{ - Primary: "6", // #625690 - purple - Secondary: "5", // #36247a - dark purple - Accent: "12", // #7060ac - light purple - Text: "7", // #2e2e2e - dark gray - Subtle: "8", // #4a4a4a - medium gray - Error: "1", // #d83636 - red - Warning: "3", // #ffff89 - yellow - Success: "2", // #53e550 - green - Background: "15", // #1a1a1a - near black - Surface: "8", // #4a4a4a - medium gray - } -} - -func NewStyles(theme AppTheme) Styles { - return Styles{ - Title: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - MarginLeft(1). - MarginBottom(1), - - Normal: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)), - - Bold: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Bold(true), - - Subtle: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)), - - Error: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Error)), - - Warning: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Warning)), - - StatusBar: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#33275e")). - Background(lipgloss.Color(theme.Primary)). - Padding(0, 1), - - Key: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true), - - SpinnerStyle: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)), - - Success: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Success)). - Bold(true), - - HighlightButton: lipgloss.NewStyle(). - Foreground(lipgloss.Color("#33275e")). - Background(lipgloss.Color(theme.Primary)). - Padding(0, 2). - Bold(true), - - SelectedOption: lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true), - - CodeBlock: lipgloss.NewStyle(). - Background(lipgloss.Color(theme.Surface)). - Foreground(lipgloss.Color(theme.Text)). - Padding(1, 2). - MarginLeft(2), - } -} - -type Styles struct { - Title lipgloss.Style - Normal lipgloss.Style - Bold lipgloss.Style - Subtle lipgloss.Style - Warning lipgloss.Style - Error lipgloss.Style - StatusBar lipgloss.Style - Key lipgloss.Style - SpinnerStyle lipgloss.Style - Success lipgloss.Style - HighlightButton lipgloss.Style - SelectedOption lipgloss.Style - CodeBlock lipgloss.Style -} - -func (s Styles) NewThemedProgress(width int) progress.Model { - theme := TerminalTheme() - prog := progress.New( - progress.WithGradient(theme.Secondary, theme.Primary), - ) - - prog.Width = width - prog.ShowPercentage = true - prog.PercentFormat = "%.0f%%" - prog.PercentageStyle = lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Bold(true) - - return prog -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go deleted file mode 100644 index 14daff3..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_config.go +++ /dev/null @@ -1,382 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os" - "path/filepath" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/config" - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - tea "github.com/charmbracelet/bubbletea" -) - -type configDeploymentResult struct { - results []config.DeploymentResult - error error -} - -type ExistingConfigInfo struct { - ConfigType string - Path string - Exists bool -} - -type configCheckResult struct { - configs []ExistingConfigInfo - error error -} - -func (m Model) viewDeployingConfigs() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Deploying Configurations") - b.WriteString(title) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Setting up configuration files...") - fmt.Fprintf(&b, "%s %s", spinner, status) - b.WriteString("\n\n") - - // Show progress information - info := m.styles.Subtle.Render("• Creating backups of existing configurations\n• Deploying optimized configurations\n• Detecting system paths") - b.WriteString(info) - - // Show live log output if available - if len(m.installationLogs) > 0 { - b.WriteString("\n\n") - logHeader := m.styles.Subtle.Render("Configuration Log:") - b.WriteString(logHeader) - b.WriteString("\n") - - // Show last few lines of logs - maxLines := 5 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - } - - return b.String() -} - -func (m Model) updateDeployingConfigsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if result, ok := msg.(configDeploymentResult); ok { - if result.error != nil { - m.err = result.error - m.state = StateError - m.isLoading = false - return m, nil - } - - for _, deployResult := range result.results { - if deployResult.Deployed { - logMsg := fmt.Sprintf("✓ %s configuration deployed", deployResult.ConfigType) - if deployResult.BackupPath != "" { - logMsg += fmt.Sprintf(" (backup: %s)", deployResult.BackupPath) - } - m.installationLogs = append(m.installationLogs, logMsg) - } - } - - m.state = StateInstallComplete - m.isLoading = false - return m, nil - } - - return m, m.listenForLogs() -} - -func (m Model) deployConfigurations() tea.Cmd { - return func() tea.Msg { - // Determine the selected window manager - var wm deps.WindowManager - switch m.selectedWM { - case 0: - wm = deps.WindowManagerNiri - case 1: - wm = deps.WindowManagerHyprland - default: - wm = deps.WindowManagerNiri - } - - // Determine the selected terminal - var terminal deps.Terminal - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalKitty - case 1: - terminal = deps.TerminalAlacritty - default: - terminal = deps.TerminalKitty - } - } else { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalGhostty - case 1: - terminal = deps.TerminalKitty - default: - terminal = deps.TerminalAlacritty - } - } - - deployer := config.NewConfigDeployer(m.logChan) - - results, err := deployer.DeployConfigurationsSelectiveWithReinstalls(context.Background(), wm, terminal, m.dependencies, m.replaceConfigs, m.reinstallItems) - - return configDeploymentResult{ - results: results, - error: err, - } - } -} - -func (m Model) viewConfigConfirmation() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Configuration Deployment") - b.WriteString(title) - b.WriteString("\n\n") - - if len(m.existingConfigs) == 0 { - // No existing configs, proceed directly - info := m.styles.Normal.Render("No existing configurations found. Proceeding with deployment...") - b.WriteString(info) - return b.String() - } - - // Show existing configurations with toggle options - for i, configInfo := range m.existingConfigs { - if configInfo.Exists { - var status string - var replaceMarker string - - shouldReplace := m.replaceConfigs[configInfo.ConfigType] - if _, exists := m.replaceConfigs[configInfo.ConfigType]; !exists { - shouldReplace = true - m.replaceConfigs[configInfo.ConfigType] = true - } - - if shouldReplace { - replaceMarker = "🔄 " - status = m.styles.Warning.Render("Will replace") - } else { - replaceMarker = "✓ " - status = m.styles.Success.Render("Keep existing") - } - - var line string - if i == m.selectedConfig { - line = fmt.Sprintf("▶ %s%-15s %s", replaceMarker, configInfo.ConfigType, status) - line += fmt.Sprintf("\n %s", configInfo.Path) - line = m.styles.SelectedOption.Render(line) - } else { - line = fmt.Sprintf(" %s%-15s %s", replaceMarker, configInfo.ConfigType, status) - line += fmt.Sprintf("\n %s", configInfo.Path) - line = m.styles.Normal.Render(line) - } - - b.WriteString(line) - b.WriteString("\n\n") - } - } - - backup := m.styles.Success.Render("✓ Replaced configurations will be backed up with timestamp") - b.WriteString(backup) - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("↑/↓: Navigate, Space: Toggle replace/keep, Enter: Continue") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateConfigConfirmationState(msg tea.Msg) (tea.Model, tea.Cmd) { - if result, ok := msg.(configCheckResult); ok { - if result.error != nil { - m.err = result.error - m.state = StateError - return m, nil - } - - m.existingConfigs = result.configs - - firstExistingSet := false - for i, config := range result.configs { - if config.Exists { - m.replaceConfigs[config.ConfigType] = true - if !firstExistingSet { - m.selectedConfig = i - firstExistingSet = true - } - } - } - - hasExisting := false - for _, config := range result.configs { - if config.Exists { - hasExisting = true - break - } - } - - if !hasExisting { - // No existing configs, proceed directly to deployment - m.state = StateDeployingConfigs - return m, m.deployConfigurations() - } - - // Show confirmation view - return m, nil - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedConfig > 0 { - for i := m.selectedConfig - 1; i >= 0; i-- { - if m.existingConfigs[i].Exists { - m.selectedConfig = i - break - } - } - } - case "down": - if m.selectedConfig < len(m.existingConfigs)-1 { - for i := m.selectedConfig + 1; i < len(m.existingConfigs); i++ { - if m.existingConfigs[i].Exists { - m.selectedConfig = i - break - } - } - } - case " ": - if len(m.existingConfigs) > 0 && m.selectedConfig < len(m.existingConfigs) { - configType := m.existingConfigs[m.selectedConfig].ConfigType - if m.existingConfigs[m.selectedConfig].Exists { - m.replaceConfigs[configType] = !m.replaceConfigs[configType] - } - } - case "enter": - m.state = StateDeployingConfigs - return m, m.deployConfigurations() - } - } - - return m, nil -} - -func (m Model) checkExistingConfigurations() tea.Cmd { - return func() tea.Msg { - var configs []ExistingConfigInfo - - if m.selectedWM == 0 { - niriPath := filepath.Join(os.Getenv("HOME"), ".config", "niri", "config.kdl") - niriExists := false - if _, err := os.Stat(niriPath); err == nil { - niriExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Niri", - Path: niriPath, - Exists: niriExists, - }) - } else { - hyprlandPath := filepath.Join(os.Getenv("HOME"), ".config", "hypr", "hyprland.conf") - hyprlandExists := false - if _, err := os.Stat(hyprlandPath); err == nil { - hyprlandExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Hyprland", - Path: hyprlandPath, - Exists: hyprlandExists, - }) - } - - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - if m.selectedTerminal == 0 { - kittyPath := filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf") - kittyExists := false - if _, err := os.Stat(kittyPath); err == nil { - kittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Kitty", - Path: kittyPath, - Exists: kittyExists, - }) - } else { - alacrittyPath := filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml") - alacrittyExists := false - if _, err := os.Stat(alacrittyPath); err == nil { - alacrittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Alacritty", - Path: alacrittyPath, - Exists: alacrittyExists, - }) - } - } else { - switch m.selectedTerminal { - case 0: - ghosttyPath := filepath.Join(os.Getenv("HOME"), ".config", "ghostty", "config") - ghosttyExists := false - if _, err := os.Stat(ghosttyPath); err == nil { - ghosttyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Ghostty", - Path: ghosttyPath, - Exists: ghosttyExists, - }) - case 1: - kittyPath := filepath.Join(os.Getenv("HOME"), ".config", "kitty", "kitty.conf") - kittyExists := false - if _, err := os.Stat(kittyPath); err == nil { - kittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Kitty", - Path: kittyPath, - Exists: kittyExists, - }) - default: - alacrittyPath := filepath.Join(os.Getenv("HOME"), ".config", "alacritty", "alacritty.toml") - alacrittyExists := false - if _, err := os.Stat(alacrittyPath); err == nil { - alacrittyExists = true - } - configs = append(configs, ExistingConfigInfo{ - ConfigType: "Alacritty", - Path: alacrittyPath, - Exists: alacrittyExists, - }) - } - } - - return configCheckResult{ - configs: configs, - error: nil, - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go deleted file mode 100644 index 13f0335..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_dependencies.go +++ /dev/null @@ -1,304 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - "github.com/AvengeMedia/DankMaterialShell/core/internal/greeter" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewDetectingDeps() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Detecting Dependencies") - b.WriteString(title) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Scanning system for existing packages and configurations...") - fmt.Fprintf(&b, "%s %s", spinner, status) - - return b.String() -} - -func (m Model) viewDependencyReview() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Dependency Review") - b.WriteString(title) - b.WriteString("\n\n") - - if len(m.dependencies) > 0 { - for i, dep := range m.dependencies { - var status string - var reinstallMarker string - var variantMarker string - - isDMS := dep.Name == "dms (DankMaterialShell)" - - if dep.CanToggle && dep.Variant == deps.VariantGit { - variantMarker = "[git] " - } - - if m.disabledItems[dep.Name] { - reinstallMarker = "✗ " - status = m.styles.Subtle.Render("Will skip") - } else if m.reinstallItems[dep.Name] { - reinstallMarker = "🔄 " - status = m.styles.Warning.Render("Will upgrade") - } else if isDMS { - reinstallMarker = "⚡ " - switch dep.Status { - case deps.StatusInstalled: - status = m.styles.Success.Render("✓ Required (installed)") - case deps.StatusMissing: - status = m.styles.Warning.Render("○ Required (will install)") - case deps.StatusNeedsUpdate: - status = m.styles.Warning.Render("△ Required (needs update)") - case deps.StatusNeedsReinstall: - status = m.styles.Error.Render("! Required (needs reinstall)") - } - } else { - switch dep.Status { - case deps.StatusInstalled: - status = m.styles.Subtle.Render("✓ Already installed") - case deps.StatusMissing: - status = m.styles.Warning.Render("○ Will install") - case deps.StatusNeedsUpdate: - status = m.styles.Warning.Render("△ Will install") - case deps.StatusNeedsReinstall: - status = m.styles.Error.Render("! Will install") - } - } - - note := "" - if dep.Name == "dms-greeter" { - note = m.styles.Subtle.Render(" (selection replaces your current display manager)") - } - - var line string - if i == m.selectedDep { - line = fmt.Sprintf("▶ %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status) - if dep.Version != "" { - line += fmt.Sprintf(" (%s)", dep.Version) - } - line = m.styles.SelectedOption.Render(line) + note - } else { - line = fmt.Sprintf(" %s%s%-25s %s", reinstallMarker, variantMarker, dep.Name, status) - if dep.Version != "" { - line += fmt.Sprintf(" (%s)", dep.Version) - } - line = m.styles.Normal.Render(line) + note - } - - b.WriteString(line) - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("↑/↓: Navigate, Space: Toggle, G: Toggle stable/git, Enter: Continue") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateDetectingDepsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if depsMsg, ok := msg.(depsDetectedMsg); ok { - m.isLoading = false - if depsMsg.err != nil { - m.err = depsMsg.err - m.state = StateError - } else { - m.dependencies = depsMsg.deps - // dms-greeter is opt-in skipped by default - for _, dep := range depsMsg.deps { - if dep.Name == "dms-greeter" { - m.disabledItems["dms-greeter"] = true - break - } - } - m.state = StateDependencyReview - } - return m, m.listenForLogs() - } - return m, m.listenForLogs() -} - -func (m Model) updateDependencyReviewState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedDep > 0 { - m.selectedDep-- - } - case "down": - if m.selectedDep < len(m.dependencies)-1 { - m.selectedDep++ - } - case " ": - if len(m.dependencies) > 0 { - depName := m.dependencies[m.selectedDep].Name - isDMS := depName == "dms (DankMaterialShell)" - - if !isDMS { - isInstalled := m.dependencies[m.selectedDep].Status == deps.StatusInstalled || - m.dependencies[m.selectedDep].Status == deps.StatusNeedsReinstall - - if isInstalled { - m.reinstallItems[depName] = !m.reinstallItems[depName] - m.disabledItems[depName] = false - } else { - m.disabledItems[depName] = !m.disabledItems[depName] - m.reinstallItems[depName] = false - } - } - } - case "g", "G": - if len(m.dependencies) > 0 && m.dependencies[m.selectedDep].CanToggle { - if m.dependencies[m.selectedDep].Variant == deps.VariantStable { - m.dependencies[m.selectedDep].Variant = deps.VariantGit - } else { - m.dependencies[m.selectedDep].Variant = deps.VariantStable - } - } - case "enter": - // Check if on Gentoo - show USE flags screen - if m.osInfo != nil { - if config, exists := distros.Registry[m.osInfo.Distribution.ID]; exists && config.Family == distros.FamilyGentoo { - m.state = StateGentooUseFlags - return m, nil - } - } - // Check if fingerprint is enabled - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 // Default to fingerprint - return m, nil - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - return m, nil - } - case "esc": - m.state = StateSelectWindowManager - return m, nil - } - } - return m, m.listenForLogs() -} - -func (m Model) installPackages() tea.Cmd { - return func() tea.Msg { - if m.osInfo == nil { - return packageInstallProgressMsg{ - progress: 0.0, - step: "Error: OS info not available", - isComplete: true, - } - } - - installer, err := distros.NewPackageInstaller(m.osInfo.Distribution.ID, m.logChan) - if err != nil { - return packageInstallProgressMsg{ - progress: 0.0, - step: fmt.Sprintf("Error: %s", err.Error()), - isComplete: true, - } - } - - // Convert TUI selection to deps enum - var wm deps.WindowManager - if m.selectedWM == 0 { - wm = deps.WindowManagerNiri - } else { - wm = deps.WindowManagerHyprland - } - - installerProgressChan := make(chan distros.InstallProgressMsg, 100) - - go func() { - defer close(installerProgressChan) - err := installer.InstallPackages(context.Background(), m.dependencies, wm, m.sudoPassword, m.reinstallItems, m.disabledItems, m.skipGentooUseFlags, installerProgressChan) - if err != nil { - installerProgressChan <- distros.InstallProgressMsg{ - Progress: 0.0, - Step: fmt.Sprintf("Installation error: %s", err.Error()), - IsComplete: true, - Error: err, - } - } - }() - - // Convert installer messages to TUI messages - go func() { - for msg := range installerProgressChan { - // Run optional greeter setup - if msg.Phase == distros.PhaseComplete && msg.IsComplete && msg.Error == nil { - greeterSelected := false - for _, dep := range m.dependencies { - if dep.Name == "dms-greeter" && !m.disabledItems["dms-greeter"] { - greeterSelected = true - break - } - } - if greeterSelected { - compositorName := "niri" - if m.selectedWM == 1 { - compositorName = "Hyprland" - } - m.packageProgressChan <- packageInstallProgressMsg{ - progress: 0.92, - step: "Configuring DMS greeter...", - logOutput: "Starting automated greeter setup...", - } - greeterLogFunc := func(line string) { - m.packageProgressChan <- packageInstallProgressMsg{ - progress: 0.94, - step: "Configuring DMS greeter...", - logOutput: line, - } - } - if err := greeter.AutoSetupGreeter(compositorName, m.sudoPassword, greeterLogFunc); err != nil { - m.packageProgressChan <- packageInstallProgressMsg{ - progress: 0.96, - step: "Greeter setup warning", - logOutput: fmt.Sprintf("⚠ Greeter auto-setup warning (non-fatal): %v", err), - } - } - } - } - tuiMsg := packageInstallProgressMsg{ - progress: msg.Progress, - step: msg.Step, - isComplete: msg.IsComplete, - needsSudo: msg.NeedsSudo, - commandInfo: msg.CommandInfo, - logOutput: msg.LogOutput, - error: msg.Error, - } - if msg.IsComplete { - m.logChan <- fmt.Sprintf("[DEBUG] Sending completion signal: step=%s, progress=%.2f", msg.Step, msg.Progress) - } - m.packageProgressChan <- tuiMsg - } - m.logChan <- "[DEBUG] Installer channel closed" - }() - - return packageInstallProgressMsg{ - progress: 0.05, - step: "Starting installation...", - isComplete: false, - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go deleted file mode 100644 index 74195a4..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_gcc.go +++ /dev/null @@ -1,103 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os/exec" - "strconv" - "strings" - - tea "github.com/charmbracelet/bubbletea" -) - -type gccVersionCheckMsg struct { - version string - major int - err error -} - -func (m Model) viewGentooGCCCheck() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("GCC Version Check Failed") - b.WriteString(title) - b.WriteString("\n\n") - - error := m.styles.Error.Render("⚠ Hyprland requires GCC 15 or newer") - b.WriteString(error) - b.WriteString("\n\n") - - info := m.styles.Normal.Render("Your current GCC version is too old. Please upgrade GCC before continuing.") - b.WriteString(info) - b.WriteString("\n\n") - - instructionsTitle := m.styles.Subtle.Render("To upgrade GCC:") - b.WriteString(instructionsTitle) - b.WriteString("\n\n") - - steps := []string{ - "1. Install GCC 15 (if not already installed):", - " sudo emerge -1av =sys-devel/gcc:15", - "", - "2. Switch to GCC 15 using gcc-config:", - " sudo gcc-config $(gcc-config -l | grep gcc-15 | awk '{print $2}' | head -1)", - "", - "3. Update environment:", - " source /etc/profile", - "", - "4. Restart this installer", - } - - for _, step := range steps { - stepLine := m.styles.Subtle.Render(step) - b.WriteString(stepLine) - b.WriteString("\n") - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Press Esc to go back, Ctrl+C to exit") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateGentooGCCCheckState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "esc": - m.state = StateSelectWindowManager - return m, nil - } - } - return m, m.listenForLogs() -} - -func (m Model) checkGCCVersion() tea.Cmd { - return func() tea.Msg { - cmd := exec.CommandContext(context.Background(), "gcc", "-dumpversion") - output, err := cmd.Output() - if err != nil { - return gccVersionCheckMsg{err: err} - } - - version := strings.TrimSpace(string(output)) - parts := strings.Split(version, ".") - if len(parts) == 0 { - return gccVersionCheckMsg{err: fmt.Errorf("invalid gcc version format")} - } - - major, err := strconv.Atoi(parts[0]) - if err != nil { - return gccVersionCheckMsg{err: err} - } - - return gccVersionCheckMsg{ - version: version, - major: major, - err: nil, - } - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go deleted file mode 100644 index eb259ef..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_gentoo_use_flags.go +++ /dev/null @@ -1,92 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewGentooUseFlags() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Gentoo Global USE Flags") - b.WriteString(title) - b.WriteString("\n\n") - - info := m.styles.Normal.Render("The following global USE flags will be enabled in /etc/portage/make.conf:") - b.WriteString(info) - b.WriteString("\n\n") - - for _, flag := range distros.GentooGlobalUseFlags { - flagLine := m.styles.Success.Render(fmt.Sprintf(" • %s", flag)) - b.WriteString(flagLine) - b.WriteString("\n") - } - - b.WriteString("\n") - note := m.styles.Subtle.Render("These flags ensure proper Qt6, Wayland, and compositor support.") - b.WriteString(note) - b.WriteString("\n\n") - - var toggleLine string - if m.skipGentooUseFlags { - toggleLine = "▶ [✗] Skip adding global USE flags (will use existing configuration)" - toggleLine = m.styles.Warning.Render(toggleLine) - } else { - toggleLine = " [ ] Skip adding global USE flags (will use existing configuration)" - toggleLine = m.styles.Subtle.Render(toggleLine) - } - b.WriteString(toggleLine) - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("Space: Toggle skip, Enter: Continue, Esc: Go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateGentooUseFlagsState(msg tea.Msg) (tea.Model, tea.Cmd) { - if gccMsg, ok := msg.(gccVersionCheckMsg); ok { - if gccMsg.err != nil || gccMsg.major < 15 { - m.state = StateGentooGCCCheck - return m, nil - } - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - } - return m, nil - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case " ": - m.skipGentooUseFlags = !m.skipGentooUseFlags - return m, nil - case "enter": - if m.selectedWM == 1 { - return m, m.checkGCCVersion() - } - if checkFingerprintEnabled() { - m.state = StateAuthMethodChoice - m.selectedConfig = 0 - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - } - return m, nil - case "esc": - m.state = StateDependencyReview - return m, nil - } - } - return m, m.listenForLogs() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go deleted file mode 100644 index c110711..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_install.go +++ /dev/null @@ -1,405 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -// wrapText wraps text to the specified width -func wrapText(text string, width int) string { - if len(text) <= width { - return text - } - - var result strings.Builder - words := strings.Fields(text) - currentLine := "" - - for _, word := range words { - if len(currentLine) == 0 { - currentLine = word - } else if len(currentLine)+1+len(word) <= width { - currentLine += " " + word - } else { - result.WriteString(currentLine) - result.WriteString("\n") - currentLine = word - } - } - - if len(currentLine) > 0 { - result.WriteString(currentLine) - } - - return result.String() -} - -func (m Model) viewInstallingPackages() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Installing Packages") - b.WriteString(title) - b.WriteString("\n\n") - - if !m.packageProgress.isComplete { - spinner := m.spinner.View() - status := m.styles.Normal.Render(m.packageProgress.step) - fmt.Fprintf(&b, "%s %s", spinner, status) - b.WriteString("\n\n") - - // Show progress bar - progressBar := fmt.Sprintf("[%s%s] %.0f%%", - strings.Repeat("█", int(m.packageProgress.progress*30)), - strings.Repeat("░", 30-int(m.packageProgress.progress*30)), - m.packageProgress.progress*100) - b.WriteString(m.styles.Normal.Render(progressBar)) - b.WriteString("\n") - - // Show command info if available - if m.packageProgress.commandInfo != "" { - cmdInfo := m.styles.Subtle.Render("$ " + m.packageProgress.commandInfo) - b.WriteString(cmdInfo) - b.WriteString("\n") - } - - // Show live log output - if len(m.installationLogs) > 0 { - b.WriteString("\n") - logHeader := m.styles.Subtle.Render("Live Output:") - b.WriteString(logHeader) - b.WriteString("\n") - - // Show last few lines of accumulated logs - maxLines := 8 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - } - - // Show error if any - if m.packageProgress.error != nil { - b.WriteString("\n") - wrappedErrorMsg := wrapText("Error: "+m.packageProgress.error.Error(), 80) - errorMsg := m.styles.Error.Render(wrappedErrorMsg) - b.WriteString(errorMsg) - } - - // Show sudo prompt if needed - if m.packageProgress.needsSudo { - sudoWarning := m.styles.Warning.Render("⚠ Using provided sudo password") - b.WriteString(sudoWarning) - } - } else { - if m.packageProgress.error != nil { - wrappedFailedMsg := wrapText("✗ Installation failed: "+m.packageProgress.error.Error(), 80) - errorMsg := m.styles.Error.Render(wrappedFailedMsg) - b.WriteString(errorMsg) - } else { - success := m.styles.Success.Render("✓ Installation complete!") - b.WriteString(success) - } - } - - return b.String() -} - -func dmsPackageName(distroID string, dependencies []deps.Dependency) string { - config, ok := distros.Registry[distroID] - if !ok { - return "dms" - } - - var isGit bool - for _, dep := range dependencies { - if dep.Name == "dms (DankMaterialShell)" { - isGit = dep.Variant == deps.VariantGit - break - } - } - - switch config.Family { - case distros.FamilyArch: - if isGit { - return "dms-shell-git" - } - return "dms-shell" - case distros.FamilyFedora, distros.FamilyUbuntu, distros.FamilyDebian, distros.FamilySUSE: - if isGit { - return "dms-git" - } - return "dms" - default: - return "dms" - } -} - -func uninstallCommand(distroID string, dependencies []deps.Dependency) string { - config, ok := distros.Registry[distroID] - if !ok { - return "" - } - if config.Family == distros.FamilyGentoo { - return "rm -rf ~/.config/quickshell/dms && sudo rm /usr/local/bin/dms" - } - pkg := dmsPackageName(distroID, dependencies) - switch config.Family { - case distros.FamilyArch: - return "sudo pacman -Rs " + pkg - case distros.FamilyFedora: - return "sudo dnf remove " + pkg - case distros.FamilyUbuntu, distros.FamilyDebian: - return "sudo apt remove " + pkg - case distros.FamilySUSE: - return "sudo zypper remove " + pkg - default: - return "" - } -} - -func (m Model) viewInstallComplete() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Success.Render("Setup Complete!") - b.WriteString(title) - b.WriteString("\n\n") - - success := m.styles.Success.Render("✓ All packages installed and configurations deployed.") - b.WriteString(success) - b.WriteString("\n\n") - - accomplishments := []string{ - "• Window manager and dependencies installed", - "• Terminal and development tools configured", - "• Configuration files deployed with backups", - "• System optimized for DankMaterialShell", - } - - for _, item := range accomplishments { - b.WriteString(m.styles.Subtle.Render(item)) - b.WriteString("\n") - } - - b.WriteString("\n") - info := m.styles.Normal.Render("Your system is ready! Log out and log back in to start using\nyour new desktop environment.\nIf you do not have a greeter, login with \"niri-session\" or \"Hyprland\"") - b.WriteString(info) - b.WriteString("\n\n") - - theme := TerminalTheme() - cmdStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Accent)) - labelStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Subtle)) - - b.WriteString(labelStyle.Render("Troubleshooting:") + "\n") - b.WriteString(labelStyle.Render(" Disable autostart: ") + cmdStyle.Render("systemctl --user disable dms") + "\n") - b.WriteString(labelStyle.Render(" View logs: ") + cmdStyle.Render("journalctl --user -u dms") + "\n") - - if m.osInfo != nil { - if cmd := uninstallCommand(m.osInfo.Distribution.ID, m.dependencies); cmd != "" { - b.WriteString(labelStyle.Render(" Uninstall: ") + cmdStyle.Render(cmd) + "\n") - } - } - - b.WriteString("\n") - b.WriteString(m.styles.Normal.Render("Press Enter to exit.")) - - if m.logFilePath != "" { - b.WriteString("\n\n") - logInfo := m.styles.Subtle.Render(fmt.Sprintf("Full logs: %s", m.logFilePath)) - b.WriteString(logInfo) - } - - return b.String() -} - -func (m Model) viewError() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Error.Render("Installation Failed") - b.WriteString(title) - b.WriteString("\n\n") - - if m.err != nil { - wrappedError := wrapText("✗ "+m.err.Error(), 80) - error := m.styles.Error.Render(wrappedError) - b.WriteString(error) - b.WriteString("\n\n") - } - - // Show package progress error if available - if m.packageProgress.error != nil { - wrappedPackageError := wrapText("Package Installation Error: "+m.packageProgress.error.Error(), 80) - packageError := m.styles.Error.Render(wrappedPackageError) - b.WriteString(packageError) - b.WriteString("\n\n") - } - - // Show persistent installation logs - if len(m.installationLogs) > 0 { - logHeader := m.styles.Warning.Render("Installation Logs (last 15 lines):") - b.WriteString(logHeader) - b.WriteString("\n") - - maxLines := 15 - startIdx := 0 - if len(m.installationLogs) > maxLines { - startIdx = len(m.installationLogs) - maxLines - } - - for i := startIdx; i < len(m.installationLogs); i++ { - if m.installationLogs[i] != "" { - logLine := m.styles.Subtle.Render(" " + m.installationLogs[i]) - b.WriteString(logLine) - b.WriteString("\n") - } - } - b.WriteString("\n") - } - - hint := m.styles.Subtle.Render("Press Ctrl+D for full debug logs") - b.WriteString(hint) - b.WriteString("\n") - - if m.logFilePath != "" { - b.WriteString("\n") - logInfo := m.styles.Warning.Render(fmt.Sprintf("Full logs: %s", m.logFilePath)) - b.WriteString(logInfo) - b.WriteString("\n") - } - - help := m.styles.Subtle.Render("Press Enter to exit") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateInstallingPackagesState(msg tea.Msg) (tea.Model, tea.Cmd) { - if progressMsg, ok := msg.(packageInstallProgressMsg); ok { - m.packageProgress = progressMsg - - // Accumulate log output - if progressMsg.logOutput != "" { - m.installationLogs = append(m.installationLogs, progressMsg.logOutput) - // Keep only last 50 lines to preserve more context for debugging - if len(m.installationLogs) > 50 { - m.installationLogs = m.installationLogs[len(m.installationLogs)-50:] - } - } - - if progressMsg.isComplete { - if progressMsg.error != nil { - m.state = StateError - m.isLoading = false - } else { - m.installationLogs = []string{} - m.state = StateConfigConfirmation - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.checkExistingConfigurations()) - } - } - return m, m.listenForPackageProgress() - } - return m, m.listenForLogs() -} - -func (m Model) updateInstallCompleteState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} - -func (m Model) updateErrorState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - return m, tea.Quit - } - } - return m, m.listenForLogs() -} - -func (m Model) listenForPackageProgress() tea.Cmd { - return func() tea.Msg { - msg, ok := <-m.packageProgressChan - if !ok { - return packageProgressCompletedMsg{} - } - // Always return the message, completion will be handled in updateInstallingPackagesState - return msg - } -} - -func (m Model) viewDebugLogs() string { - var b strings.Builder - - theme := TerminalTheme() - - titleStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true) - - b.WriteString(titleStyle.Render("Debug Logs")) - b.WriteString("\n\n") - - // Combine both logMessages and installationLogs - allLogs := append([]string{}, m.logMessages...) - allLogs = append(allLogs, m.installationLogs...) - - if len(allLogs) == 0 { - b.WriteString("No logs available\n") - } else { - // Calculate available height (reserve space for header and footer) - maxHeight := m.height - 6 - if maxHeight < 10 { - maxHeight = 10 - } - - // Show the most recent logs - startIdx := 0 - if len(allLogs) > maxHeight { - startIdx = len(allLogs) - maxHeight - } - - for i := startIdx; i < len(allLogs); i++ { - if allLogs[i] != "" { - fmt.Fprintf(&b, "%d: %s\n", i, allLogs[i]) - } - } - - if startIdx > 0 { - subtleStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Subtle)) - b.WriteString(subtleStyle.Render(fmt.Sprintf("... (%d older log entries hidden)\n", startIdx))) - } - } - - b.WriteString("\n") - statusStyle := lipgloss.NewStyle().Foreground(lipgloss.Color(theme.Accent)) - b.WriteString(statusStyle.Render("Press Ctrl+D to return, Ctrl+C to quit")) - - return b.String() -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go deleted file mode 100644 index deec061..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_password.go +++ /dev/null @@ -1,338 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "os" - "os/exec" - "path/filepath" - "strings" - "time" - - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewAuthMethodChoice() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Authentication Method") - b.WriteString(title) - b.WriteString("\n\n") - - message := "Fingerprint authentication is available.\nHow would you like to authenticate?" - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - // Option 0: Fingerprint - if m.selectedConfig == 0 { - option := m.styles.SelectedOption.Render("▶ Use Fingerprint") - b.WriteString(option) - } else { - option := m.styles.Normal.Render(" Use Fingerprint") - b.WriteString(option) - } - b.WriteString("\n") - - // Option 1: Password - if m.selectedConfig == 1 { - option := m.styles.SelectedOption.Render("▶ Use Password") - b.WriteString(option) - } else { - option := m.styles.Normal.Render(" Use Password") - b.WriteString(option) - } - b.WriteString("\n\n") - - help := m.styles.Subtle.Render("↑/↓: Navigate, Enter: Select, Esc: Back") - b.WriteString(help) - - return b.String() -} - -func (m Model) viewFingerprintAuth() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Fingerprint Authentication") - b.WriteString(title) - b.WriteString("\n\n") - - if m.fingerprintFailed { - errorMsg := m.styles.Error.Render("✗ Fingerprint authentication failed") - b.WriteString(errorMsg) - b.WriteString("\n") - retryMsg := m.styles.Subtle.Render("Returning to authentication menu...") - b.WriteString(retryMsg) - } else { - message := "Please place your finger on the fingerprint reader." - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - spinner := m.spinner.View() - status := m.styles.Normal.Render("Waiting for fingerprint...") - fmt.Fprintf(&b, "%s %s", spinner, status) - } - - return b.String() -} - -func (m Model) viewPasswordPrompt() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Password Authentication") - b.WriteString(title) - b.WriteString("\n\n") - - message := "Installation requires sudo privileges.\nPlease enter your password to continue:" - b.WriteString(m.styles.Normal.Render(message)) - b.WriteString("\n\n") - - // Password input - b.WriteString(m.passwordInput.View()) - b.WriteString("\n") - - // Show validation status - if m.packageProgress.step == "Validating sudo password..." { - spinner := m.spinner.View() - status := m.styles.Normal.Render(m.packageProgress.step) - b.WriteString(spinner + " " + status) - b.WriteString("\n") - } else if m.packageProgress.error != nil { - errorMsg := m.styles.Error.Render("✗ " + m.packageProgress.error.Error() + ". Please try again.") - b.WriteString(errorMsg) - b.WriteString("\n") - } else if m.packageProgress.step == "Password validation failed" { - errorMsg := m.styles.Error.Render("✗ Incorrect password. Please try again.") - b.WriteString(errorMsg) - b.WriteString("\n") - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Enter: Continue, Esc: Back, Ctrl+C: Cancel") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateAuthMethodChoiceState(msg tea.Msg) (tea.Model, tea.Cmd) { - m.fingerprintFailed = false - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "up": - if m.selectedConfig > 0 { - m.selectedConfig-- - } - case "down": - if m.selectedConfig < 1 { - m.selectedConfig++ - } - case "enter": - if m.selectedConfig == 0 { - m.state = StateFingerprintAuth - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.tryFingerprint()) - } else { - m.state = StatePasswordPrompt - m.passwordInput.Focus() - return m, nil - } - case "esc": - m.state = StateDependencyReview - return m, nil - } - } - return m, nil -} - -func (m Model) updateFingerprintAuthState(msg tea.Msg) (tea.Model, tea.Cmd) { - if validMsg, ok := msg.(passwordValidMsg); ok { - if validMsg.valid { - m.sudoPassword = "" - m.packageProgress = packageInstallProgressMsg{} - m.state = StateInstallingPackages - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.installPackages()) - } else { - m.fingerprintFailed = true - return m, m.delayThenReturn() - } - } - - if _, ok := msg.(delayCompleteMsg); ok { - m.fingerprintFailed = false - m.selectedConfig = 0 - m.state = StateAuthMethodChoice - return m, nil - } - - return m, m.listenForLogs() -} - -func (m Model) updatePasswordPromptState(msg tea.Msg) (tea.Model, tea.Cmd) { - var cmd tea.Cmd - - if validMsg, ok := msg.(passwordValidMsg); ok { - if validMsg.valid { - // Password is valid, proceed with installation - m.sudoPassword = validMsg.password - m.passwordInput.SetValue("") // Clear password input - // Clear any error state - m.packageProgress = packageInstallProgressMsg{} - m.state = StateInstallingPackages - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.installPackages()) - } else { - // Password is invalid, show error and stay on password prompt - m.packageProgress = packageInstallProgressMsg{ - progress: 0.0, - step: "Password validation failed", - error: fmt.Errorf("incorrect password"), - logOutput: "Authentication failed", - } - m.passwordInput.SetValue("") - m.passwordInput.Focus() - return m, nil - } - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - // Don't allow multiple validation attempts while one is in progress - if m.packageProgress.step == "Validating sudo password..." { - return m, nil - } - - // Validate password first - password := m.passwordInput.Value() - if password == "" { - return m, nil // Don't proceed with empty password - } - - // Clear any previous error and show validation in progress - m.packageProgress = packageInstallProgressMsg{ - progress: 0.01, - step: "Validating sudo password...", - isComplete: false, - logOutput: "Testing password with sudo -v", - } - return m, m.validatePassword(password) - case "esc": - // Go back to dependency review - m.passwordInput.SetValue("") - m.packageProgress = packageInstallProgressMsg{} // Clear any validation state - m.state = StateDependencyReview - return m, nil - } - } - - m.passwordInput, cmd = m.passwordInput.Update(msg) - return m, cmd -} - -func checkFingerprintEnabled() bool { - ctx, cancel := context.WithTimeout(context.Background(), 2*time.Second) - defer cancel() - - // Check if pam_fprintd.so is in PAM config - cmd := exec.CommandContext(ctx, "grep", "-q", "pam_fprintd.so", "/etc/pam.d/system-auth") - if err := cmd.Run(); err != nil { - return false - } - - // Check if fprintd-list exists and user has enrolled fingerprints - user := os.Getenv("USER") - if user == "" { - return false - } - - listCmd := exec.CommandContext(ctx, "fprintd-list", user) - output, err := listCmd.CombinedOutput() - if err != nil { - return false - } - - // If output contains "finger:" or similar, fingerprints are enrolled - return strings.Contains(string(output), "finger") -} - -func (m Model) delayThenReturn() tea.Cmd { - return func() tea.Msg { - time.Sleep(2 * time.Second) - return delayCompleteMsg{} - } -} - -func (m Model) tryFingerprint() tea.Cmd { - return func() tea.Msg { - clearCmd := exec.Command("sudo", "-k") - clearCmd.Run() - - tmpDir := os.TempDir() - askpassScript := filepath.Join(tmpDir, fmt.Sprintf("danklinux-fp-%d.sh", time.Now().UnixNano())) - - scriptContent := "#!/bin/sh\nexit 1\n" - if err := os.WriteFile(askpassScript, []byte(scriptContent), 0o700); err != nil { - return passwordValidMsg{password: "", valid: false} - } - defer os.Remove(askpassScript) - - ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-A", "-v") - cmd.Env = append(os.Environ(), fmt.Sprintf("SUDO_ASKPASS=%s", askpassScript)) - - err := cmd.Run() - - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: "", valid: true} - } -} - -func (m Model) validatePassword(password string) tea.Cmd { - return func() tea.Msg { - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) - defer cancel() - - cmd := exec.CommandContext(ctx, "sudo", "-S", "-v") - - stdin, err := cmd.StdinPipe() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - if err := cmd.Start(); err != nil { - return passwordValidMsg{password: "", valid: false} - } - - _, err = fmt.Fprintf(stdin, "%s\n", password) - stdin.Close() - if err != nil { - return passwordValidMsg{password: "", valid: false} - } - - err = cmd.Wait() - - if err != nil { - if ctx.Err() == context.DeadlineExceeded { - return passwordValidMsg{password: "", valid: false} - } - return passwordValidMsg{password: "", valid: false} - } - - return passwordValidMsg{password: password, valid: true} - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go deleted file mode 100644 index e45105e..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_selection.go +++ /dev/null @@ -1,224 +0,0 @@ -package tui - -import ( - "context" - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/deps" - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" -) - -func (m Model) viewSelectWindowManager() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Choose Window Manager") - b.WriteString(title) - b.WriteString("\n\n") - - options := []struct { - name string - description string - }{ - {"niri", "Scrollable-tiling Wayland compositor."}, - } - - if m.osInfo == nil || m.osInfo.Distribution.ID != "debian" { - options = append(options, struct { - name string - description string - }{"Hyprland", "Dynamic tiling Wayland compositor."}) - } - - for i, option := range options { - if i == m.selectedWM { - selected := m.styles.SelectedOption.Render("▶ " + option.name) - b.WriteString(selected) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } else { - normal := m.styles.Normal.Render(" " + option.name) - b.WriteString(normal) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } - b.WriteString("\n") - if i < len(options)-1 { - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Use ↑/↓ to navigate, Enter to select, Esc to go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) viewSelectTerminal() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - title := m.styles.Title.Render("Choose Terminal Emulator") - b.WriteString(title) - b.WriteString("\n\n") - - var options []struct { - name string - description string - } - - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - options = []struct { - name string - description string - }{ - {"kitty", "A feature-rich, customizable terminal emulator."}, - {"alacritty", "A simple terminal emulator."}, - } - } else { - options = []struct { - name string - description string - }{ - {"ghostty", "A fast, native terminal emulator built in Zig."}, - {"kitty", "A feature-rich, customizable terminal emulator."}, - {"alacritty", "A simple terminal emulator."}, - } - } - - for i, option := range options { - if i == m.selectedTerminal { - selected := m.styles.SelectedOption.Render("▶ " + option.name) - b.WriteString(selected) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } else { - normal := m.styles.Normal.Render(" " + option.name) - b.WriteString(normal) - b.WriteString("\n") - desc := m.styles.Subtle.Render(" " + option.description) - b.WriteString(desc) - } - b.WriteString("\n") - if i < len(options)-1 { - b.WriteString("\n") - } - } - - b.WriteString("\n") - help := m.styles.Subtle.Render("Use ↑/↓ to navigate, Enter to select, Esc to go back") - b.WriteString(help) - - return b.String() -} - -func (m Model) updateSelectTerminalState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - maxTerminalIndex := 2 - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - maxTerminalIndex = 1 - } - - switch keyMsg.String() { - case "up": - if m.selectedTerminal > 0 { - m.selectedTerminal-- - } - case "down": - if m.selectedTerminal < maxTerminalIndex { - m.selectedTerminal++ - } - case "enter": - m.state = StateDetectingDeps - m.isLoading = true - return m, tea.Batch(m.spinner.Tick, m.detectDependencies()) - case "esc": - m.state = StateSelectWindowManager - return m, m.listenForLogs() - } - } - return m, m.listenForLogs() -} - -func (m Model) updateSelectWindowManagerState(msg tea.Msg) (tea.Model, tea.Cmd) { - if keyMsg, ok := msg.(tea.KeyMsg); ok { - maxWMIndex := 1 - if m.osInfo != nil && m.osInfo.Distribution.ID == "debian" { - maxWMIndex = 0 - } - - switch keyMsg.String() { - case "up": - if m.selectedWM > 0 { - m.selectedWM-- - } - case "down": - if m.selectedWM < maxWMIndex { - m.selectedWM++ - } - case "enter": - m.state = StateSelectTerminal - return m, m.listenForLogs() - case "esc": - // Go back to welcome screen - m.state = StateWelcome - return m, m.listenForLogs() - } - } - return m, m.listenForLogs() -} - -func (m Model) detectDependencies() tea.Cmd { - return func() tea.Msg { - if m.osInfo == nil { - return depsDetectedMsg{deps: nil, err: fmt.Errorf("OS info not available")} - } - - detector, err := distros.NewDependencyDetector(m.osInfo.Distribution.ID, m.logChan) - if err != nil { - return depsDetectedMsg{deps: nil, err: err} - } - - // Convert TUI selection to deps enum - var wm deps.WindowManager - if m.selectedWM == 0 { - wm = deps.WindowManagerNiri // First option is Niri - } else { - wm = deps.WindowManagerHyprland // Second option is Hyprland - } - - var terminal deps.Terminal - if m.osInfo != nil && m.osInfo.Distribution.ID == "gentoo" { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalKitty - case 1: - terminal = deps.TerminalAlacritty - default: - terminal = deps.TerminalKitty - } - } else { - switch m.selectedTerminal { - case 0: - terminal = deps.TerminalGhostty - case 1: - terminal = deps.TerminalKitty - default: - terminal = deps.TerminalAlacritty - } - } - - dependencies, err := detector.DetectDependenciesWithTerminal(context.Background(), wm, terminal) - return depsDetectedMsg{deps: dependencies, err: err} - } -} diff --git a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go b/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go deleted file mode 100644 index c5ae862..0000000 --- a/raveos-hyprland-theme/theme-data/DankMaterialShell/core/internal/tui/views_welcome.go +++ /dev/null @@ -1,216 +0,0 @@ -package tui - -import ( - "fmt" - "strings" - - "github.com/AvengeMedia/DankMaterialShell/core/internal/distros" - tea "github.com/charmbracelet/bubbletea" - "github.com/charmbracelet/lipgloss" -) - -func (m Model) viewWelcome() string { - var b strings.Builder - - b.WriteString(m.renderBanner()) - b.WriteString("\n") - - theme := TerminalTheme() - - decorator := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Render("━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━") - - titleBox := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color(theme.Primary)). - Padding(0, 2). - MarginBottom(1) - - titleText := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("dankinstall") - - versionTag := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Italic(true). - Render(" // Dank Linux Installer") - - subtitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Italic(true). - Render("Quickstart for a Dank Desktop") - - b.WriteString(decorator) - b.WriteString("\n") - b.WriteString(titleBox.Render(titleText + versionTag)) - b.WriteString("\n") - b.WriteString(subtitle) - b.WriteString("\n\n") - - if m.osInfo != nil { - if distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - errorBox := lipgloss.NewStyle(). - Border(lipgloss.RoundedBorder()). - BorderForeground(lipgloss.Color("#FF6B6B")). - Padding(1, 2). - MarginBottom(1) - - errorTitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color("#FF6B6B")). - Bold(true). - Render("⚠ UNSUPPORTED DISTRIBUTION") - - var errorMsg string - switch m.osInfo.Distribution.ID { - case "ubuntu": - errorMsg = fmt.Sprintf("Ubuntu %s is not supported.\n\nOnly Ubuntu 25.04+ is supported.\n\nPlease upgrade to Ubuntu 25.04 or later.", m.osInfo.VersionID) - case "debian": - errorMsg = fmt.Sprintf("Debian %s is not supported.\n\nOnly Debian 13+ (Trixie) is supported.\n\nPlease upgrade to Debian 13 or later.", m.osInfo.VersionID) - case "nixos": - errorMsg = "See the NixOS documentation for installation instructions: https://danklinux.com/docs/dankmaterialshell/nixos." - default: - errorMsg = fmt.Sprintf("%s is not supported.\nFeel free to request on https://github.com/AvengeMedia/DankMaterialShell", m.osInfo.PrettyName) - } - - errorMsgStyled := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)). - Render(errorMsg) - - b.WriteString(errorBox.Render(errorTitle + "\n\n" + errorMsgStyled)) - b.WriteString("\n\n") - } else { - // System info box - sysBox := lipgloss.NewStyle(). - Border(lipgloss.NormalBorder()). - BorderForeground(lipgloss.Color(theme.Subtle)). - Padding(0, 1). - MarginBottom(1) - - // Style the distro name with its color - distroStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(m.osInfo.Distribution.HexColorCode)). - Bold(true) - distroName := distroStyle.Render(m.osInfo.PrettyName) - - archStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)) - - sysInfo := fmt.Sprintf("System: %s / %s", distroName, archStyle.Render(m.osInfo.Architecture)) - b.WriteString(sysBox.Render(sysInfo)) - b.WriteString("\n") - - // Feature list with better styling - featTitle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Underline(true). - Render("WHAT YOU GET") - b.WriteString(featTitle + "\n\n") - - features := []string{ - "[shell] dms (DankMaterialShell)", - "[wm] niri or Hyprland", - "[term] Ghostty, kitty, or Alacritty", - "[style] All the themes, automatically.", - "[config] DANK defaults - keybindings, rules, animations, etc.", - } - - for i, feat := range features { - prefix := feat[:9] - content := feat[10:] - - prefixStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Accent)). - Bold(true) - - contentStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Text)) - - if i == len(features)-1 { - contentStyle = contentStyle.Bold(true) - } - - fmt.Fprintf(&b, " %s %s\n", - prefixStyle.Render(prefix), - contentStyle.Render(content)) - } - - b.WriteString("\n") - - noteStyle := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Italic(true) - note := noteStyle.Render("* Existing configs can be replaced (and backed up) or preserved") - b.WriteString(note) - b.WriteString("\n") - - if m.osInfo.Distribution.ID == "gentoo" { - gentooNote := noteStyle.Render("* Will set per-package USE flags and unmask testing packages as needed") - b.WriteString(gentooNote) - b.WriteString("\n") - } - - b.WriteString("\n") - } - - } else if m.isLoading { - spinner := m.spinner.View() - loading := m.styles.Normal.Render("Detecting system...") - fmt.Fprintf(&b, "%s %s\n\n", spinner, loading) - } - - // Footer with better visual separation - footerDivider := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Subtle)). - Render("───────────────────────────────────────────────────────────") - b.WriteString(footerDivider + "\n") - - if m.osInfo != nil { - ctrlKey := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("Ctrl+C") - - if distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - b.WriteString(m.styles.Subtle.Render("Press ") + ctrlKey + m.styles.Subtle.Render(" to quit")) - } else { - enterKey := lipgloss.NewStyle(). - Foreground(lipgloss.Color(theme.Primary)). - Bold(true). - Render("Enter") - - b.WriteString(m.styles.Subtle.Render("Press ") + enterKey + m.styles.Subtle.Render(" to choose window manager, ") + ctrlKey + m.styles.Subtle.Render(" to quit")) - } - } else { - help := m.styles.Subtle.Render("Press Enter to continue, Ctrl+C to quit") - b.WriteString(help) - } - - return b.String() -} - -func (m Model) updateWelcomeState(msg tea.Msg) (tea.Model, tea.Cmd) { - if completeMsg, ok := msg.(osInfoCompleteMsg); ok { - m.isLoading = false - if completeMsg.err != nil { - m.err = completeMsg.err - m.state = StateError - } else { - m.osInfo = completeMsg.info - } - return m, m.listenForLogs() - } - - if keyMsg, ok := msg.(tea.KeyMsg); ok { - switch keyMsg.String() { - case "enter": - if m.osInfo != nil && !distros.IsUnsupportedDistro(m.osInfo.Distribution.ID, m.osInfo.VersionID) { - m.state = StateSelectWindowManager - return m, m.listenForLogs() - } - } - } - return m, m.listenForLogs() -} -- cgit v1.3