diff --git a/docs/plans/2024-06-22-hostkeeper-implementation.md b/docs/plans/2024-06-22-hostkeeper-implementation.md index ce8e0fd..d6565a9 100644 --- a/docs/plans/2024-06-22-hostkeeper-implementation.md +++ b/docs/plans/2024-06-22-hostkeeper-implementation.md @@ -4662,11 +4662,263 @@ git tag -a v1.0.0 -m "Initial MVP release with core SSH management features" --- +## Phase 2: TUI Overhaul & Enhanced Features + +### Task 15: TUI Tab Framework & Orange Theme + +**Files:** +- Create: `pkg/tui/tabs.go` — TabManager, tab bar renderer +- Create: `pkg/tui/styles.go` — All lipgloss styles (orange palette) +- Modify: `pkg/tui/tui.go` — Integrate TabManager, replace single-screen with tab routing + +**Step 1: Create styles.go** + +```go +// pkg/tui/styles.go +package tui + +import "github.com/charmbracelet/lipgloss" + +// Orange theme palette +var ( + OrangePrimary = lipgloss.Color("#FF6B00") + OrangeSecondary = lipgloss.Color("#FF9F43") + OrangeLight = lipgloss.Color("#FFB800") + DarkBg = lipgloss.Color("#1A1A1A") + LightBg = lipgloss.Color("#2D2D2D") + TextPrimary = lipgloss.Color("#FFFFFF") + TextSecondary = lipgloss.Color("#AAAAAA") +) + +// Component styles +var ( + TabActiveStyle = lipgloss.NewStyle().Background(OrangePrimary).Foreground(DarkBg).Bold(true).Padding(0, 2) + TabInactiveStyle = lipgloss.NewStyle().Background(LightBg).Foreground(TextSecondary).Padding(0, 2) + TabBarStyle = lipgloss.NewStyle().Background(DarkBg) + StatusBarStyle = lipgloss.NewStyle().Background(OrangePrimary).Foreground(DarkBg).Padding(0, 1) + TitleStyle = lipgloss.NewStyle().Foreground(OrangeSecondary).Bold(true) + HighlightStyle = lipgloss.NewStyle().Foreground(OrangePrimary).Bold(true) + SelectedStyle = lipgloss.NewStyle().Foreground(DarkBg).Background(OrangePrimary).Padding(0, 1) +) +``` + +**Step 2: Create tabs.go — TabManager** + +Core structure: +- `Tab` interface with `Init()`, `Update(msg)`, `View()`, `Name() string` +- `TabManager` holds slice of tabs + active index +- Tab bar rendered via `TabActiveStyle` / `TabInactiveStyle` +- Keybind routing: tab-level keys (Ctrl+Tab, Ctrl+Q, Ctrl+N) vs tab-content keys + +```go +type Tab interface { + Init() tea.Cmd + Update(tea.Msg) (Tab, tea.Cmd) + View() string + Name() string +} + +type TabManager struct { + tabs []Tab + active int +} +``` + +**Step 3: Modify tui.go** + +Replace `CurrentScreen Screen` with `*TabManager`. Init creates default `HostsTab`. Update delegates to `TabManager.Update()`. View renders tab bar + active tab view + status bar. + +**Step 4: Verify** + +```bash +make build +./bin/hostkeeper tui +``` + +Expected: Tab bar visible at top with "Hosts" tab, orange theme, status bar at bottom. Keyboard navigation works (Ctrl+Tab cycles tabs if multiple exist). + +--- + +### Task 16: SSH Session Tab (Multi-Session) + +**Files:** +- Create: `pkg/tui/session.go` — SessionTab with live SSH terminal + +**Architecture:** +``` +┌─ Hosts ── Server Nico ── Web Prod ────────────────┐ +│ │ +│ $ htop │ +│ $ cd /var/log │ +│ $ tail -f syslog │ +│ │ +│ [live SSH session output] │ +├─────────────────────────────────────────────────────┤ +│ Connected to Server Nico — Ctrl+Q to disconnect │ +└─────────────────────────────────────────────────────┘ +``` + +**SessionTab struct:** +```go +type SessionTab struct { + host *models.Host + client *ssh.Client // Go SSH client + pty *ssh.Session + width int + height int + input chan string // keyboard input → SSH stdin + output chan string // SSH stdout → TUI render + done chan struct{} +} +``` + +**Key flows:** +1. User selects host in HostsTab → Enter → open SessionTab +2. SessionTab connects via Go SSH client (auto-auth with stored password) +3. Start PTY → 2 goroutines: stdin pump, stdout pump +4. Keyboard input in TUI → `input` channel → SSH stdin +5. SSH stdout → `output` channel → TUI render (via tea.Batch) +6. Window resize → `msg tea.WindowSizeMsg` → SSH WindowChange +7. Ctrl+Q → close session → remove tab → back to HostsTab +8. Multiple sessions = multiple tabs, each with its own goroutines + +**Verify:** +```bash +make build +./bin/hostkeeper tui +# Select a host → Enter → SSH session tab opens +# Ctrl+Tab to switch between session and host list +# Ctrl+Q to close session +``` + +--- + +### Task 17: TUI Host Forms (Add/Edit) + +**Files:** +- Create: `pkg/tui/host_form.go` + +**Form fields:** +- Name (text input) +- Hostname/IP (text input) +- Port (number input, default 22) +- Username (text input) +- Auth Type (select: password/key/both) +- Password (text input, masked) +- Key (file selector or paste) +- Group (text input) +- Tags (text input, comma-separated) +- Notes (textarea) + +**Implementation:** +- Use Bubble Tea `textinput` model for each field +- Tab/Shift+Tab to cycle fields +- Enter on last field → submit → save via storage +- Edit mode: pre-populate fields from existing host +- Cancel (Escape) → back to HostsTab without saving + +**Verify:** +```bash +make build +./bin/hostkeeper tui +# HostsTab → press 'a' or Ctrl+N → form opens +# Fill fields → Enter → host saved → back to HostsTab +``` + +--- + +### Task 18: TUI SFTP Browser + +**Files:** +- Create: `pkg/tui/sftp_browser.go` + +**Layout:** +``` +┌─ Hosts ── Server Nico ── SFTP ────────────────────┐ +│ /var/www/ │ +│ ──────────────────────────────────────────────── │ +│ 📁 .