fbe444c3ab
- Add Bubble Tea TUI model with Init/Update/View - Implement host list screen with keyboard navigation (up/down/enter/q) - Add styled rendering for hosts, selection, tags - Create hostkeeper tui CLI command - Add tests for TUI initialization and host loading - Update project state documentation
85 lines
2.1 KiB
Go
85 lines
2.1 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"strings"
|
|
|
|
"github.com/charmbracelet/lipgloss"
|
|
|
|
"git.tukangketik.id/swanadiva/hostkeeper/internal/models"
|
|
)
|
|
|
|
var (
|
|
HostNameStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("226")).Bold(true)
|
|
HostDetailStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("245"))
|
|
SelectedStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("212")).Background(lipgloss.Color("235")).Padding(0, 1)
|
|
TagStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("86"))
|
|
)
|
|
|
|
// renderHostList renders the host list screen
|
|
func renderHostList(m *Model) string {
|
|
var b strings.Builder
|
|
|
|
b.WriteString(TitleStyle.Render("HOSTKEEPER - SSH Manager"))
|
|
b.WriteString("\n\n")
|
|
|
|
if len(m.Hosts) == 0 {
|
|
b.WriteString(SubtitleStyle.Render("No hosts found. Add your first host with: hostkeeper add"))
|
|
b.WriteString("\n\n")
|
|
b.WriteString(InfoStyle.Render("Press 'q' to quit"))
|
|
return b.String()
|
|
}
|
|
|
|
for i, host := range m.Hosts {
|
|
if i == m.SelectedIndex {
|
|
b.WriteString(renderSelectedHost(host))
|
|
} else {
|
|
b.WriteString(renderHost(host))
|
|
}
|
|
b.WriteString("\n")
|
|
}
|
|
|
|
b.WriteString("\n")
|
|
b.WriteString(SubtitleStyle.Render("\u2191\u2193: Navigate | Enter: Connect | q: Quit"))
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// renderHost renders a single host
|
|
func renderHost(host *models.Host) string {
|
|
var b strings.Builder
|
|
|
|
b.WriteString(HostNameStyle.Render(host.Name))
|
|
b.WriteString("\n")
|
|
|
|
details := fmt.Sprintf(" %s@%s:%d", host.Username, host.Hostname, host.Port)
|
|
b.WriteString(HostDetailStyle.Render(details))
|
|
|
|
if len(host.Tags) > 0 {
|
|
tags := formatTagsForTUI(host.Tags)
|
|
b.WriteString(" " + TagStyle.Render(tags))
|
|
}
|
|
|
|
return b.String()
|
|
}
|
|
|
|
// renderSelectedHost renders the selected host with highlight
|
|
func renderSelectedHost(host *models.Host) string {
|
|
hostText := renderHost(host)
|
|
return SelectedStyle.Render(hostText)
|
|
}
|
|
|
|
// formatTagsForTUI formats tags for TUI display
|
|
func formatTagsForTUI(tags []string) string {
|
|
if len(tags) == 0 {
|
|
return ""
|
|
}
|
|
|
|
var formatted []string
|
|
for _, tag := range tags {
|
|
formatted = append(formatted, "["+tag+"]")
|
|
}
|
|
|
|
return strings.Join(formatted, " ")
|
|
}
|