135 lines
3.4 KiB
Go
135 lines
3.4 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"time"
|
|
|
|
"github.com/charmbracelet/bubbles/list"
|
|
tea "github.com/charmbracelet/bubbletea"
|
|
sessionpkg "github.com/mirivlad/sshkeeper/internal/session"
|
|
)
|
|
|
|
type sessionScreenModel struct {
|
|
list list.Model
|
|
sessions []sessionpkg.Window
|
|
width int
|
|
height int
|
|
err error
|
|
}
|
|
|
|
type sessionItem struct {
|
|
window sessionpkg.Window
|
|
}
|
|
|
|
func (i sessionItem) Title() string {
|
|
active := ""
|
|
if i.window.Active {
|
|
active = " active"
|
|
}
|
|
return fmt.Sprintf("%-28s #%d%s", truncate(i.window.ServerAlias, 28), i.window.Index, active)
|
|
}
|
|
func (i sessionItem) Description() string {
|
|
if i.window.StartedAt.IsZero() {
|
|
return "tmux window " + i.window.ID
|
|
}
|
|
return fmt.Sprintf("running %s · tmux %s", time.Since(i.window.StartedAt).Round(time.Second), i.window.ID)
|
|
}
|
|
|
|
func (i sessionItem) FilterValue() string {
|
|
return i.window.ServerAlias + " " + i.window.Name
|
|
}
|
|
|
|
func newSessionScreenModel(w, h int) *sessionScreenModel {
|
|
l := list.New([]list.Item{}, list.NewDefaultDelegate(), w, managerListHeight(h))
|
|
l.Title = "Sessions"
|
|
l.SetShowStatusBar(false)
|
|
l.SetFilteringEnabled(false)
|
|
l.SetShowHelp(false)
|
|
l.Styles.Title = titleStyle
|
|
return &sessionScreenModel{list: l, width: w, height: h}
|
|
}
|
|
|
|
func (m *sessionScreenModel) loadSessions() tea.Cmd {
|
|
return func() tea.Msg {
|
|
windows, err := sessionpkg.List()
|
|
return sessionsLoadedMsg{sessions: windows, err: err, closed: true}
|
|
}
|
|
}
|
|
func (m *sessionScreenModel) setSessions(windows []sessionpkg.Window) {
|
|
m.sessions = windows
|
|
items := make([]list.Item, len(windows))
|
|
for index, window := range windows {
|
|
items[index] = sessionItem{window: window}
|
|
}
|
|
m.list.SetItems(items)
|
|
}
|
|
|
|
func (m *sessionScreenModel) selected() *sessionpkg.Window {
|
|
item, ok := m.list.SelectedItem().(sessionItem)
|
|
if !ok {
|
|
return nil
|
|
}
|
|
window := item.window
|
|
return &window
|
|
}
|
|
|
|
func (m *sessionScreenModel) closeSelected() tea.Cmd {
|
|
selected := m.selected()
|
|
if selected == nil {
|
|
return nil
|
|
}
|
|
id := selected.ID
|
|
return func() tea.Msg {
|
|
err := sessionpkg.Close(id)
|
|
windows, listErr := sessionpkg.List()
|
|
if err == nil {
|
|
err = listErr
|
|
}
|
|
return sessionsLoadedMsg{sessions: windows, err: err, closed: true}
|
|
}
|
|
}
|
|
func (m *sessionScreenModel) View() string {
|
|
notification := ""
|
|
if m.err != nil {
|
|
notification = errorStyle.Render(fmt.Sprintf("Error: %v", m.err))
|
|
}
|
|
body := func(width, height int) string {
|
|
if len(m.sessions) == 0 {
|
|
return renderPaddedPanel(width, height, []string{dashboardHelp("No active SSH sessions.")})
|
|
}
|
|
capacity := max(1, height-2)
|
|
start, end := visibleServerRange(len(m.sessions), m.list.Index(), max(1, capacity/2))
|
|
lines := make([]string, 0, capacity)
|
|
for index := start; index < end; index++ {
|
|
item := sessionItem{window: m.sessions[index]}
|
|
marker := " "
|
|
if index == m.list.Index() {
|
|
marker = "> "
|
|
}
|
|
lines = append(lines, marker+item.Title(), " "+item.Description())
|
|
}
|
|
return renderPaddedPanel(width, height, lines)
|
|
}
|
|
return renderScreenShell(screenShell{
|
|
breadcrumb: "Sessions",
|
|
status: fmt.Sprintf("%d active · tmux", len(m.sessions)),
|
|
notification: notification,
|
|
width: m.width,
|
|
height: m.height,
|
|
body: body,
|
|
footer: []helpItem{
|
|
{Key: "Enter", Action: "attach"},
|
|
{Key: "Ctrl+D (d)", Action: "close"},
|
|
{Key: "Ctrl+R (r)", Action: "refresh"},
|
|
{Key: "Ctrl+H", Action: "help"},
|
|
{Key: "Esc", Action: "back"},
|
|
},
|
|
})
|
|
}
|
|
|
|
type sessionsLoadedMsg struct {
|
|
sessions []sessionpkg.Window
|
|
err error
|
|
closed bool
|
|
}
|