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