status.go

  1package status
  2
  3import (
  4	"strings"
  5	"time"
  6
  7	"github.com/charmbracelet/bubbles/v2/help"
  8	tea "github.com/charmbracelet/bubbletea/v2"
  9	"github.com/charmbracelet/crush/internal/tui/styles"
 10	"github.com/charmbracelet/crush/internal/tui/util"
 11	"github.com/charmbracelet/lipgloss/v2"
 12	"github.com/charmbracelet/x/ansi"
 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	help       help.Model
 26	keyMap     help.KeyMap
 27}
 28
 29// clearMessageCmd is a command that clears status messages after a timeout
 30func (m *statusCmp) clearMessageCmd(ttl time.Duration) tea.Cmd {
 31	return tea.Tick(ttl, func(time.Time) tea.Msg {
 32		return util.ClearStatusMsg{}
 33	})
 34}
 35
 36func (m *statusCmp) Init() tea.Cmd {
 37	return nil
 38}
 39
 40func (m *statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 41	switch msg := msg.(type) {
 42	case tea.WindowSizeMsg:
 43		m.width = msg.Width
 44		m.help.Width = msg.Width - 2
 45		return m, nil
 46
 47	// Handle status info
 48	case util.InfoMsg:
 49		m.info = msg
 50		ttl := msg.TTL
 51		if ttl == 0 {
 52			ttl = m.messageTTL
 53		}
 54		return m, m.clearMessageCmd(ttl)
 55	case util.ClearStatusMsg:
 56		m.info = util.InfoMsg{}
 57	}
 58	return m, nil
 59}
 60
 61func (m *statusCmp) View() string {
 62	t := styles.CurrentTheme()
 63	status := t.S().Base.Padding(0, 1, 1, 1).Render(m.help.View(m.keyMap))
 64	if m.info.Msg != "" {
 65		status = m.infoMsg()
 66	}
 67	return status
 68}
 69
 70func (m *statusCmp) infoMsg() string {
 71	t := styles.CurrentTheme()
 72	message := ""
 73	infoType := ""
 74	switch m.info.Type {
 75	case util.InfoTypeError:
 76		infoType = t.S().Base.Background(t.Red).Padding(0, 1).Render("ERROR")
 77		width := m.width - lipgloss.Width(infoType)
 78		message = t.S().Base.Background(t.Error).Foreground(t.White).Padding(0, 1).Width(width).Render(ansi.Truncate(m.info.Msg, width, "…"))
 79	case util.InfoTypeWarn:
 80		infoType = t.S().Base.Foreground(t.BgOverlay).Background(t.Yellow).Padding(0, 1).Render("WARNING")
 81		width := m.width - lipgloss.Width(infoType)
 82		message = t.S().Base.Foreground(t.BgOverlay).Background(t.Warning).Padding(0, 1).Width(width).Render(ansi.Truncate(m.info.Msg, width, "…"))
 83	default:
 84		infoType = t.S().Base.Foreground(t.BgOverlay).Background(t.Green).Padding(0, 1).Render("OKAY!")
 85		width := m.width - lipgloss.Width(infoType)
 86		message = t.S().Base.Background(t.Success).Foreground(t.White).Padding(0, 1).Width(width).Render(ansi.Truncate(m.info.Msg, width, "…"))
 87	}
 88	return strings.Join([]string{infoType, message}, "")
 89}
 90
 91func (m *statusCmp) ToggleFullHelp() {
 92	m.help.ShowAll = !m.help.ShowAll
 93}
 94
 95func (m *statusCmp) SetKeyMap(keyMap help.KeyMap) {
 96	m.keyMap = keyMap
 97}
 98
 99func NewStatusCmp() StatusCmp {
100	t := styles.CurrentTheme()
101	help := help.New()
102	help.Styles = t.S().Help
103	return &statusCmp{
104		messageTTL: 5 * time.Second,
105		help:       help,
106	}
107}