1package tui
 2
 3import (
 4	"fmt"
 5
 6	tea "github.com/charmbracelet/bubbletea"
 7	"github.com/gliderlabs/ssh"
 8)
 9
10type sessionState int
11
12const (
13	startState sessionState = iota
14	errorState
15	quittingState
16	quitState
17)
18
19type stateMsg struct{ state sessionState }
20type infoMsg struct{ text string }
21type errMsg struct{ err error }
22
23func (e errMsg) Error() string {
24	return e.err.Error()
25}
26
27func SessionHandler(s ssh.Session) (tea.Model, error) {
28	pty, changes, active := s.Pty()
29	if !active {
30		return nil, fmt.Errorf("you need to do this from a terminal with PTY support")
31	}
32	return NewModel(pty.Window.Width, pty.Window.Height, changes), nil
33}
34
35type Model struct {
36	state         sessionState
37	error         string
38	info          string
39	width         int
40	height        int
41	windowChanges <-chan ssh.Window
42}
43
44func NewModel(width int, height int, windowChanges <-chan ssh.Window) *Model {
45	m := &Model{
46		width:         width,
47		height:        height,
48		windowChanges: windowChanges,
49	}
50	m.state = startState
51	return m
52}
53
54func (m *Model) Init() tea.Cmd {
55	return tea.Batch(m.windowChangesCmd, tea.HideCursor)
56}
57
58func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
59	cmds := make([]tea.Cmd, 0)
60	// Always allow state, error, info, window resize and quit messages
61	switch msg := msg.(type) {
62	case stateMsg:
63		m.state = msg.state
64	case tea.KeyMsg:
65		switch msg.String() {
66		case "q", "ctrl+c":
67			return m, tea.Quit
68		}
69	case errMsg:
70		m.error = msg.Error()
71		m.state = errorState
72		return m, nil
73	case infoMsg:
74		m.info = msg.text
75	case windowMsg:
76		cmds = append(cmds, m.windowChangesCmd)
77	case tea.WindowSizeMsg:
78		m.width = msg.Width
79		m.height = msg.Height
80	}
81	return m, tea.Batch(cmds...)
82}
83
84func (m *Model) View() string {
85	pad := 6
86	h := headerStyle.Width(m.width - pad).Render("Smoothie")
87	f := footerStyle.Render(m.info)
88	s := ""
89	content := ""
90	switch m.state {
91	case errorState:
92		s += errorStyle.Render(fmt.Sprintf("Bummer: %s", m.error))
93	default:
94		s = normalStyle.Render(fmt.Sprintf("Doing something weird %d", m.state))
95	}
96	content = h + "\n" + s + "\n" + f
97	return appBoxStyle.Render(content)
98}