1
0
mirror of https://github.com/AvengeMedia/DankMaterialShell.git synced 2025-12-05 21:15:38 -05:00
Files
DankMaterialShell/backend/internal/dms/views_features.go
2025-11-12 17:18:45 -05:00

530 lines
14 KiB
Go

//go:build !distro_binary
package dms
import (
"fmt"
"strings"
"github.com/charmbracelet/lipgloss"
)
func (m Model) renderUpdateView() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Update Dependencies"))
b.WriteString("\n")
if len(m.updateDeps) == 0 {
b.WriteString("Loading dependencies...\n")
return b.String()
}
categories := m.categorizeDependencies()
currentIndex := 0
for _, category := range []string{"Shell", "Shared Components", "Hyprland Components", "Niri Components"} {
deps, exists := categories[category]
if !exists || len(deps) == 0 {
continue
}
categoryStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#7060ac")).
Bold(true).
MarginTop(1)
b.WriteString(categoryStyle.Render(category + ":"))
b.WriteString("\n")
for _, dep := range deps {
var statusText, icon, reinstallMarker string
var style lipgloss.Style
if m.updateToggles[dep.Name] {
reinstallMarker = "🔄 "
if dep.Status == 0 {
statusText = "Will be installed"
} else {
statusText = "Will be upgraded"
}
style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
} else {
switch dep.Status {
case 1:
icon = "✓"
statusText = "Installed"
style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF"))
case 0:
icon = "○"
statusText = "Not installed"
style = lipgloss.NewStyle().Foreground(lipgloss.Color("#888888"))
case 2:
icon = "△"
statusText = "Needs update"
style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
case 3:
icon = "!"
statusText = "Needs reinstall"
style = lipgloss.NewStyle().Foreground(lipgloss.Color("#FFA500"))
}
}
line := fmt.Sprintf("%s%s%-25s %s", reinstallMarker, icon, dep.Name, statusText)
if currentIndex == m.selectedUpdateDep {
line = "▶ " + line
selectedStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#7060ac")).Bold(true)
b.WriteString(selectedStyle.Render(line))
} else {
line = " " + line
b.WriteString(style.Render(line))
}
b.WriteString("\n")
currentIndex++
}
}
b.WriteString("\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
MarginTop(1)
instructions := "↑/↓: Navigate, Space: Toggle, Enter: Update Selected, Esc: Back"
b.WriteString(instructionStyle.Render(instructions))
return b.String()
}
func (m Model) renderPasswordView() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Sudo Authentication"))
b.WriteString("\n\n")
normalStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))
b.WriteString(normalStyle.Render("Package installation requires sudo privileges."))
b.WriteString("\n")
b.WriteString(normalStyle.Render("Please enter your password to continue:"))
b.WriteString("\n\n")
inputStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA"))
maskedPassword := strings.Repeat("*", len(m.passwordInput))
b.WriteString(inputStyle.Render("Password: " + maskedPassword))
b.WriteString("\n")
if m.passwordError != "" {
errorStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF0000"))
b.WriteString(errorStyle.Render("✗ " + m.passwordError))
b.WriteString("\n")
}
b.WriteString("\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
MarginTop(1)
instructions := "Enter: Continue, Esc: Back, Ctrl+C: Cancel"
b.WriteString(instructionStyle.Render(instructions))
return b.String()
}
func (m Model) renderProgressView() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Updating Packages"))
b.WriteString("\n\n")
if !m.updateProgress.complete {
progressStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA"))
b.WriteString(progressStyle.Render(m.updateProgress.step))
b.WriteString("\n\n")
progressBar := fmt.Sprintf("[%s%s] %.0f%%",
strings.Repeat("█", int(m.updateProgress.progress*30)),
strings.Repeat("░", 30-int(m.updateProgress.progress*30)),
m.updateProgress.progress*100)
b.WriteString(lipgloss.NewStyle().Foreground(lipgloss.Color("#FFFFFF")).Render(progressBar))
b.WriteString("\n")
if len(m.updateLogs) > 0 {
b.WriteString("\n")
logHeader := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Render("Live Output:")
b.WriteString(logHeader)
b.WriteString("\n")
maxLines := 8
startIdx := 0
if len(m.updateLogs) > maxLines {
startIdx = len(m.updateLogs) - maxLines
}
logStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888"))
for i := startIdx; i < len(m.updateLogs); i++ {
if m.updateLogs[i] != "" {
b.WriteString(logStyle.Render(" " + m.updateLogs[i]))
b.WriteString("\n")
}
}
}
}
if m.updateProgress.err != nil {
errorStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF0000"))
b.WriteString("\n")
b.WriteString(errorStyle.Render(fmt.Sprintf("✗ Update failed: %v", m.updateProgress.err)))
b.WriteString("\n")
if len(m.updateLogs) > 0 {
b.WriteString("\n")
logHeader := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Render("Error Logs:")
b.WriteString(logHeader)
b.WriteString("\n")
maxLines := 15
startIdx := 0
if len(m.updateLogs) > maxLines {
startIdx = len(m.updateLogs) - maxLines
}
logStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888"))
for i := startIdx; i < len(m.updateLogs); i++ {
if m.updateLogs[i] != "" {
b.WriteString(logStyle.Render(" " + m.updateLogs[i]))
b.WriteString("\n")
}
}
}
b.WriteString("\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888"))
b.WriteString(instructionStyle.Render("Press Esc to go back"))
} else if m.updateProgress.complete {
successStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA"))
b.WriteString("\n")
b.WriteString(successStyle.Render("✓ Update complete!"))
b.WriteString("\n\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888"))
b.WriteString(instructionStyle.Render("Press Esc to return to main menu"))
}
return b.String()
}
func (m Model) getFilteredDeps() []DependencyInfo {
categories := m.categorizeDependencies()
var filtered []DependencyInfo
for _, category := range []string{"Shell", "Shared Components", "Hyprland Components", "Niri Components"} {
deps, exists := categories[category]
if exists {
filtered = append(filtered, deps...)
}
}
return filtered
}
func (m Model) getDepAtVisualIndex(index int) *DependencyInfo {
filtered := m.getFilteredDeps()
if index >= 0 && index < len(filtered) {
return &filtered[index]
}
return nil
}
func (m Model) renderGreeterPasswordView() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Sudo Authentication"))
b.WriteString("\n\n")
normalStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))
b.WriteString(normalStyle.Render("Greeter installation requires sudo privileges."))
b.WriteString("\n")
b.WriteString(normalStyle.Render("Please enter your password to continue:"))
b.WriteString("\n\n")
inputStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA"))
maskedPassword := strings.Repeat("*", len(m.greeterPasswordInput))
b.WriteString(inputStyle.Render("Password: " + maskedPassword))
b.WriteString("\n")
if m.greeterPasswordError != "" {
errorStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF0000"))
b.WriteString(errorStyle.Render("✗ " + m.greeterPasswordError))
b.WriteString("\n")
}
b.WriteString("\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
MarginTop(1)
instructions := "Enter: Continue, Esc: Back, Ctrl+C: Cancel"
b.WriteString(instructionStyle.Render(instructions))
return b.String()
}
func (m Model) renderGreeterCompositorSelect() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Select Compositor"))
b.WriteString("\n\n")
normalStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))
b.WriteString(normalStyle.Render("Multiple compositors detected. Choose which one to use for the greeter:"))
b.WriteString("\n\n")
selectedStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA")).
Bold(true)
for i, comp := range m.greeterCompositors {
if i == m.greeterSelectedComp {
b.WriteString(selectedStyle.Render(fmt.Sprintf("▶ %s", comp)))
} else {
b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", comp)))
}
b.WriteString("\n")
}
b.WriteString("\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
MarginTop(1)
instructions := "↑/↓: Navigate, Enter: Select, Esc: Back"
b.WriteString(instructionStyle.Render(instructions))
return b.String()
}
func (m Model) renderGreeterMenu() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Greeter Management"))
b.WriteString("\n")
greeterMenuItems := []string{"Install Greeter"}
selectedStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA")).
Bold(true)
normalStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))
for i, item := range greeterMenuItems {
if i == m.selectedGreeterItem {
b.WriteString(selectedStyle.Render(fmt.Sprintf("▶ %s", item)))
} else {
b.WriteString(normalStyle.Render(fmt.Sprintf(" %s", item)))
}
b.WriteString("\n")
}
b.WriteString("\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888")).
MarginTop(1)
instructions := "↑/↓: Navigate, Enter: Select, Esc: Back"
b.WriteString(instructionStyle.Render(instructions))
return b.String()
}
func (m Model) renderGreeterInstalling() string {
var b strings.Builder
b.WriteString(m.renderBanner())
b.WriteString("\n")
headerStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF")).
Bold(true).
MarginBottom(1)
b.WriteString(headerStyle.Render("Installing Greeter"))
b.WriteString("\n\n")
if !m.greeterProgress.complete {
progressStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA"))
b.WriteString(progressStyle.Render(m.greeterProgress.step))
b.WriteString("\n\n")
if len(m.greeterLogs) > 0 {
b.WriteString("\n")
logHeader := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888")).Render("Output:")
b.WriteString(logHeader)
b.WriteString("\n")
maxLines := 10
startIdx := 0
if len(m.greeterLogs) > maxLines {
startIdx = len(m.greeterLogs) - maxLines
}
logStyle := lipgloss.NewStyle().Foreground(lipgloss.Color("#888888"))
for i := startIdx; i < len(m.greeterLogs); i++ {
if m.greeterLogs[i] != "" {
b.WriteString(logStyle.Render(" " + m.greeterLogs[i]))
b.WriteString("\n")
}
}
}
}
if m.greeterProgress.err != nil {
errorStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FF0000"))
b.WriteString("\n")
b.WriteString(errorStyle.Render(fmt.Sprintf("✗ Installation failed: %v", m.greeterProgress.err)))
b.WriteString("\n\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888"))
b.WriteString(instructionStyle.Render("Press Esc to go back"))
} else if m.greeterProgress.complete {
successStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#00D4AA"))
b.WriteString("\n")
b.WriteString(successStyle.Render("✓ Greeter installation complete!"))
b.WriteString("\n\n")
normalStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#FFFFFF"))
b.WriteString(normalStyle.Render("To test the greeter, run:"))
b.WriteString("\n")
b.WriteString(normalStyle.Render(" sudo systemctl start greetd"))
b.WriteString("\n\n")
b.WriteString(normalStyle.Render("To enable on boot, run:"))
b.WriteString("\n")
b.WriteString(normalStyle.Render(" sudo systemctl enable --now greetd"))
b.WriteString("\n\n")
instructionStyle := lipgloss.NewStyle().
Foreground(lipgloss.Color("#888888"))
b.WriteString(instructionStyle.Render("Press Esc to return to main menu"))
}
return b.String()
}
func (m Model) categorizeDependencies() map[string][]DependencyInfo {
categories := map[string][]DependencyInfo{
"Shell": {},
"Shared Components": {},
"Hyprland Components": {},
"Niri Components": {},
}
excludeList := map[string]bool{
"git": true,
"polkit-agent": true,
"jq": true,
"xdg-desktop-portal": true,
"xdg-desktop-portal-wlr": true,
"xdg-desktop-portal-hyprland": true,
"xdg-desktop-portal-gtk": true,
}
for _, dep := range m.updateDeps {
if excludeList[dep.Name] {
continue
}
switch dep.Name {
case "dms (DankMaterialShell)", "quickshell":
categories["Shell"] = append(categories["Shell"], dep)
case "hyprland", "grim", "slurp", "hyprctl", "grimblast":
categories["Hyprland Components"] = append(categories["Hyprland Components"], dep)
case "niri":
categories["Niri Components"] = append(categories["Niri Components"], dep)
case "kitty", "alacritty", "ghostty", "hyprpicker":
categories["Shared Components"] = append(categories["Shared Components"], dep)
default:
categories["Shared Components"] = append(categories["Shared Components"], dep)
}
}
return categories
}