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