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 {
28 pty, changes, active := s.Pty()
29 if !active {
30 return nil
31 }
32 return NewModel(pty.Window.Width, pty.Window.Height, changes)
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}