feat: Task 16 — SSH Session Tab (multi-session)

- pkg/tui/session.go — SessionTab with live SSH terminal in a tab
- pkg/tui/messages.go — quitMsg, openSessionMsg, sessionOutputMsg
- pkg/tui/tabs.go — Tab.Close() interface, Add() returns tea.Cmd
- pkg/tui/host_list_tab.go — Enter opens session tab, q quits
- pkg/tui/tui.go — handle openSessionMsg/quitMsg, SetDataDir
- cmd/hostkeeper/tui.go — pass dataDir to model
This commit is contained in:
swanadiva
2026-06-23 14:54:44 +07:00
parent 5d9e38f8db
commit 98dfc1c79c
6 changed files with 432 additions and 5 deletions
+23 -2
View File
@@ -23,6 +23,7 @@ type Model struct {
SelectedIndex int // deprecated
Error error
Quit bool
dataDir string
}
// New creates a new TUI model
@@ -47,11 +48,26 @@ func (m *Model) Init() tea.Cmd {
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":
// Only hard quit on ctrl+c (let tabs handle q)
if msg.String() == "ctrl+c" {
m.Quit = true
return m, tea.Quit
}
case quitMsg:
m.Quit = true
return m, tea.Quit
case openSessionMsg:
tab := NewSessionTab(msg.host, m.dataDir)
cmd := m.tabs.Add(tab)
return m, cmd
case sessionDoneMsg:
if msg.err != nil {
m.Error = msg.err
}
return m, nil
}
cmd, err := m.tabs.Update(msg)
@@ -94,6 +110,11 @@ func (m *Model) LoadHosts(hosts []*models.Host) {
}
}
// SetDataDir sets the data directory for SSH connections
func (m *Model) SetDataDir(dir string) {
m.dataDir = dir
}
// TabManager returns the underlying tab manager
func (m *Model) TabManager() *TabManager {
return m.tabs