package main import ( "fmt" "strings" "path/filepath" "os" "github.com/charmbracelet/bubbles/list" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" ) // Styles var ( titleStyle = lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#7D56F4")). MarginLeft(2) selectedStyle = lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#7D56F4")) installedBadge = lipgloss.NewStyle(). Bold(true). Foreground(lipgloss.Color("#04B575")). Render(" [INSTALLED]") ) // serverItem implementiert list.Item für Bubbletea type serverItem struct { server LSPServer installed bool } func (i serverItem) FilterValue() string { return i.server.Name } func (i serverItem) Title() string { title := i.server.Name if i.installed { title += installedBadge } return title } func (i serverItem) Description() string { return fmt.Sprintf("%s | Languages: %s", i.server.Description, strings.Join(i.server.Languages, ", ")) } // Model für die Bubbletea-Anwendung type model struct { list list.Model registry *Registry installer *Installer status string quitting bool } // installMsg wird nach erfolgreicher Installation gesendet type installMsg struct { server LSPServer err error } // updateMsg wird nach Registry-Update gesendet type updateMsg struct { registry *Registry err error } // Init initialisiert das Model func (m model) Init() tea.Cmd { return nil } // Update behandelt Nachrichten und aktualisiert das Model func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { switch msg := msg.(type) { case tea.KeyMsg: switch msg.String() { case "ctrl+c", "q": m.quitting = true return m, tea.Quit case "u": // Registry aktualisieren (force update) m.status = "Updating registry from Mason..." return m, func() tea.Msg { registry, err := LoadRegistryFromMason() return updateMsg{registry: registry, err: err} } case "enter": // Server installieren/deinstallieren selectedItem := m.list.SelectedItem().(serverItem) if selectedItem.installed { // Deinstallieren m.status = fmt.Sprintf("Uninstalling %s...", selectedItem.server.Name) return m, func() tea.Msg { err := m.installer.Uninstall(selectedItem.server) return installMsg{server: selectedItem.server, err: err} } } else { // Installieren m.status = fmt.Sprintf("Installing %s...", selectedItem.server.Name) return m, func() tea.Msg { err := m.installer.Install(selectedItem.server) return installMsg{server: selectedItem.server, err: err} } } case "i": // Info anzeigen selectedItem := m.list.SelectedItem().(serverItem) m.status = fmt.Sprintf("Server: %s | Homepage: %s | License: %s | Type: %s", selectedItem.server.Name, selectedItem.server.Homepage, selectedItem.server.License, selectedItem.server.InstallType) return m, nil } case installMsg: // Installation abgeschlossen if msg.err != nil { m.status = fmt.Sprintf("❌ Error: %v", msg.err) } else { m.status = fmt.Sprintf("✅ Successfully installed %s", msg.server.Name) } // Liste aktualisieren items := m.buildListItems() m.list.SetItems(items) return m, nil case updateMsg: // Registry-Update abgeschlossen if msg.err != nil { m.status = fmt.Sprintf("❌ Registry update failed: %v", msg.err) } else { m.registry = msg.registry m.status = fmt.Sprintf("✅ Registry updated with %d LSP servers", len(msg.registry.Servers)) // Liste aktualisieren items := m.buildListItems() m.list.SetItems(items) } return m, nil case tea.WindowSizeMsg: m.list.SetWidth(msg.Width) m.list.SetHeight(msg.Height - 5) return m, nil } var cmd tea.Cmd m.list, cmd = m.list.Update(msg) return m, cmd } // View rendert die UI func (m model) View() string { if m.quitting { return "Goodbye! 👋\n" } header := titleStyle.Render("🚀 LSP Server Manager (Mason Registry)") helpText := lipgloss.NewStyle(). Foreground(lipgloss.Color("#626262")). Render("\nEnter: Install/Uninstall | i: Info | u: Update Registry | q: Quit | /: Filter") statusBar := "" if m.status != "" { statusBar = "\n" + lipgloss.NewStyle(). Foreground(lipgloss.Color("#7D56F4")). Render(m.status) } return fmt.Sprintf("%s\n\n%s%s%s\n", header, m.list.View(), statusBar, helpText) } // buildListItems erstellt die Liste der Server-Items func (m *model) buildListItems() []list.Item { items := make([]list.Item, len(m.registry.Servers)) for i, server := range m.registry.Servers { items[i] = serverItem{ server: server, installed: m.installer.IsInstalled(server), } } return items } // Hauptfunktion func main() { // Registry laden - verwende "mason" um von GitHub zu laden // oder einen Dateipfad für lokale registry.json registry, err := LoadRegistry("mason") if err != nil { fmt.Printf("Error loading registry: %v\n", err) return } fmt.Printf("Loaded %d LSP servers from registry\n\n", len(registry.Servers)) // Installer erstellen homeDir, _ := os.UserHomeDir() installDir := filepath.Join(homeDir, ".lsp-manager") installer := NewInstaller(installDir) installer.PrintPathInstructions() // Liste erstellen delegate := list.NewDefaultDelegate() delegate.Styles.SelectedTitle = selectedStyle delegate.Styles.SelectedDesc = selectedStyle initialModel := model{ registry: registry, installer: installer, } items := initialModel.buildListItems() initialModel.list = list.New(items, delegate, 0, 0) initialModel.list.Title = "Available LSP Servers" initialModel.list.SetFilteringEnabled(true) // Bubbletea-Programm starten p := tea.NewProgram(initialModel, tea.WithAltScreen()) if _, err := p.Run(); err != nil { fmt.Printf("Error: %v\n", err) os.Exit(1) } }