bubble.go

  1package tui
  2
  3import (
  4	"fmt"
  5	"smoothie/git"
  6	"smoothie/tui/bubbles/commits"
  7
  8	tea "github.com/charmbracelet/bubbletea"
  9	"github.com/gliderlabs/ssh"
 10)
 11
 12type sessionState int
 13
 14const (
 15	startState sessionState = iota
 16	errorState
 17	loadedState
 18	quittingState
 19	quitState
 20)
 21
 22type stateMsg struct{ state sessionState }
 23type infoMsg struct{ text string }
 24type errMsg struct{ err error }
 25
 26func (e errMsg) Error() string {
 27	return e.err.Error()
 28}
 29
 30func SessionHandler(reposPath string) func(ssh.Session) (tea.Model, []tea.ProgramOption) {
 31	rs := git.NewRepoSource(reposPath)
 32	return func(s ssh.Session) (tea.Model, []tea.ProgramOption) {
 33		if len(s.Command()) == 0 {
 34			pty, changes, active := s.Pty()
 35			if !active {
 36				return nil, nil
 37			}
 38			return NewModel(pty.Window.Width, pty.Window.Height, changes, rs), []tea.ProgramOption{tea.WithAltScreen()}
 39		}
 40		return nil, nil
 41	}
 42}
 43
 44type Model struct {
 45	state          sessionState
 46	error          string
 47	info           string
 48	width          int
 49	height         int
 50	windowChanges  <-chan ssh.Window
 51	repoSource     *git.RepoSource
 52	commitTimeline *commits.Bubble
 53}
 54
 55func NewModel(width int, height int, windowChanges <-chan ssh.Window, repoSource *git.RepoSource) *Model {
 56	m := &Model{
 57		width:         width,
 58		height:        height,
 59		windowChanges: windowChanges,
 60		repoSource:    repoSource,
 61	}
 62	m.state = startState
 63	return m
 64}
 65
 66func (m *Model) Init() tea.Cmd {
 67	return tea.Batch(m.windowChangesCmd, m.getCommitsCmd)
 68}
 69
 70func (m *Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 71	cmds := make([]tea.Cmd, 0)
 72	// Always allow state, error, info, window resize and quit messages
 73	switch msg := msg.(type) {
 74	case stateMsg:
 75		m.state = msg.state
 76	case tea.KeyMsg:
 77		switch msg.String() {
 78		case "q", "ctrl+c":
 79			return m, tea.Quit
 80		case "j", "k", "up", "down":
 81			_, cmd := m.commitTimeline.Update(msg)
 82			cmds = append(cmds, cmd)
 83		}
 84	case errMsg:
 85		m.error = msg.Error()
 86		m.state = errorState
 87		return m, nil
 88	case infoMsg:
 89		m.info = msg.text
 90	case windowMsg:
 91		cmds = append(cmds, m.windowChangesCmd)
 92	case tea.WindowSizeMsg:
 93		m.width = msg.Width
 94		m.height = msg.Height
 95		m.commitTimeline.Height = msg.Height
 96	}
 97	return m, tea.Batch(cmds...)
 98}
 99
100func (m *Model) View() string {
101	pad := 6
102	h := headerStyle.Width(m.width - pad).Render("Charm Beta")
103	f := footerStyle.Render(m.info)
104	s := ""
105	content := ""
106	switch m.state {
107	case loadedState:
108		s += m.commitTimeline.View()
109	case errorState:
110		s += errorStyle.Render(fmt.Sprintf("Bummer: %s", m.error))
111	default:
112		s = normalStyle.Render(fmt.Sprintf("Doing something weird %d", m.state))
113	}
114	content = h + "\n" + s + "\n" + f
115	return appBoxStyle.Render(content)
116}