status.go

  1package status
  2
  3import (
  4	"time"
  5
  6	"github.com/charmbracelet/bubbles/v2/help"
  7	tea "github.com/charmbracelet/bubbletea/v2"
  8	"github.com/charmbracelet/crush/internal/logging"
  9	"github.com/charmbracelet/crush/internal/pubsub"
 10	"github.com/charmbracelet/crush/internal/session"
 11	"github.com/charmbracelet/crush/internal/tui/styles"
 12	"github.com/charmbracelet/crush/internal/tui/util"
 13)
 14
 15type StatusCmp interface {
 16	util.Model
 17	ToggleFullHelp()
 18	SetKeyMap(keyMap help.KeyMap)
 19}
 20
 21type statusCmp struct {
 22	info       util.InfoMsg
 23	width      int
 24	messageTTL time.Duration
 25	session    session.Session
 26	help       help.Model
 27	keyMap     help.KeyMap
 28}
 29
 30// clearMessageCmd is a command that clears status messages after a timeout
 31func (m *statusCmp) clearMessageCmd(ttl time.Duration) tea.Cmd {
 32	return tea.Tick(ttl, func(time.Time) tea.Msg {
 33		return util.ClearStatusMsg{}
 34	})
 35}
 36
 37func (m *statusCmp) Init() tea.Cmd {
 38	return nil
 39}
 40
 41func (m *statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 42	switch msg := msg.(type) {
 43	case tea.WindowSizeMsg:
 44		m.width = msg.Width
 45		m.help.Width = msg.Width - 2
 46		return m, nil
 47
 48	// Handle status info
 49	case util.InfoMsg:
 50		m.info = msg
 51		ttl := msg.TTL
 52		if ttl == 0 {
 53			ttl = m.messageTTL
 54		}
 55		return m, m.clearMessageCmd(ttl)
 56	case util.ClearStatusMsg:
 57		m.info = util.InfoMsg{}
 58
 59	// Handle persistent logs
 60	case pubsub.Event[logging.LogMessage]:
 61		if msg.Payload.Persist {
 62			switch msg.Payload.Level {
 63			case "error":
 64				m.info = util.InfoMsg{
 65					Type: util.InfoTypeError,
 66					Msg:  msg.Payload.Message,
 67					TTL:  msg.Payload.PersistTime,
 68				}
 69			case "info":
 70				m.info = util.InfoMsg{
 71					Type: util.InfoTypeInfo,
 72					Msg:  msg.Payload.Message,
 73					TTL:  msg.Payload.PersistTime,
 74				}
 75			case "warn":
 76				m.info = util.InfoMsg{
 77					Type: util.InfoTypeWarn,
 78					Msg:  msg.Payload.Message,
 79					TTL:  msg.Payload.PersistTime,
 80				}
 81			default:
 82				m.info = util.InfoMsg{
 83					Type: util.InfoTypeInfo,
 84					Msg:  msg.Payload.Message,
 85					TTL:  msg.Payload.PersistTime,
 86				}
 87			}
 88		}
 89	}
 90	return m, nil
 91}
 92
 93func (m *statusCmp) View() tea.View {
 94	t := styles.CurrentTheme()
 95	status := t.S().Base.Padding(0, 1, 1, 1).Render(m.help.View(m.keyMap))
 96	if m.info.Msg != "" {
 97		switch m.info.Type {
 98		case util.InfoTypeError:
 99			status = t.S().Base.Background(t.Error).Padding(0, 1).Width(m.width).Render(m.info.Msg)
100		case util.InfoTypeWarn:
101			status = t.S().Base.Background(t.Warning).Padding(0, 1).Width(m.width).Render(m.info.Msg)
102		default:
103			status = t.S().Base.Background(t.Info).Padding(0, 1).Width(m.width).Render(m.info.Msg)
104		}
105	}
106	return tea.NewView(status)
107}
108
109func (m *statusCmp) ToggleFullHelp() {
110	m.help.ShowAll = !m.help.ShowAll
111}
112
113func (m *statusCmp) SetKeyMap(keyMap help.KeyMap) {
114	m.keyMap = keyMap
115}
116
117func NewStatusCmp(keyMap help.KeyMap) StatusCmp {
118	t := styles.CurrentTheme()
119	help := help.New()
120	help.Styles = t.S().Help
121	return &statusCmp{
122		messageTTL: 10 * time.Second,
123		help:       help,
124		keyMap:     keyMap,
125	}
126}