model.go

  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}