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 if len(s.Command()) == 0 {
29 pty, changes, active := s.Pty()
30 if !active {
31 return nil
32 }
33 return NewModel(pty.Window.Width, pty.Window.Height, changes)
34 }
35 return nil
36}
37
38type Model struct {
39 state sessionState
40 error string
41 info string
42 width int
43 height int
44 windowChanges <-chan ssh.Window
45}
46
47func NewModel(width int, height int, windowChanges <-chan ssh.Window) *Model {
48 m := &Model{
49 width: width,
50 height: height,
51 windowChanges: windowChanges,
52 }
53 m.state = startState
54 return m
55}
56
57func (m *Model) Init() tea.Cmd {
58 return tea.Batch(m.windowChangesCmd, tea.HideCursor)
59}
60
61func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
62 cmds := make([]tea.Cmd, 0)
63 // Always allow state, error, info, window resize and quit messages
64 switch msg := msg.(type) {
65 case stateMsg:
66 m.state = msg.state
67 case tea.KeyMsg:
68 switch msg.String() {
69 case "q", "ctrl+c":
70 return m, tea.Quit
71 }
72 case errMsg:
73 m.error = msg.Error()
74 m.state = errorState
75 return m, nil
76 case infoMsg:
77 m.info = msg.text
78 case windowMsg:
79 cmds = append(cmds, m.windowChangesCmd)
80 case tea.WindowSizeMsg:
81 m.width = msg.Width
82 m.height = msg.Height
83 }
84 return m, tea.Batch(cmds...)
85}
86
87func (m *Model) View() string {
88 pad := 6
89 h := headerStyle.Width(m.width - pad).Render("Smoothie")
90 f := footerStyle.Render(m.info)
91 s := ""
92 content := ""
93 switch m.state {
94 case errorState:
95 s += errorStyle.Render(fmt.Sprintf("Bummer: %s", m.error))
96 default:
97 s = normalStyle.Render(fmt.Sprintf("Doing something weird %d", m.state))
98 }
99 content = h + "\n" + s + "\n" + f
100 return appBoxStyle.Render(content)
101}