status.go

  1package core
  2
  3import (
  4	"time"
  5
  6	tea "github.com/charmbracelet/bubbletea"
  7	"github.com/charmbracelet/lipgloss"
  8	"github.com/kujtimiihoxha/termai/internal/config"
  9	"github.com/kujtimiihoxha/termai/internal/llm/models"
 10	"github.com/kujtimiihoxha/termai/internal/tui/styles"
 11	"github.com/kujtimiihoxha/termai/internal/tui/util"
 12	"github.com/kujtimiihoxha/termai/internal/version"
 13)
 14
 15type statusCmp struct {
 16	info       *util.InfoMsg
 17	width      int
 18	messageTTL time.Duration
 19}
 20
 21// clearMessageCmd is a command that clears status messages after a timeout
 22func (m statusCmp) clearMessageCmd(ttl time.Duration) tea.Cmd {
 23	return tea.Tick(ttl, func(time.Time) tea.Msg {
 24		return util.ClearStatusMsg{}
 25	})
 26}
 27
 28func (m statusCmp) Init() tea.Cmd {
 29	return nil
 30}
 31
 32func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 33	switch msg := msg.(type) {
 34	case tea.WindowSizeMsg:
 35		m.width = msg.Width
 36		return m, nil
 37	case util.InfoMsg:
 38		m.info = &msg
 39		ttl := msg.TTL
 40		if ttl == 0 {
 41			ttl = m.messageTTL
 42		}
 43		return m, m.clearMessageCmd(ttl)
 44	case util.ClearStatusMsg:
 45		m.info = nil
 46	}
 47	return m, nil
 48}
 49
 50var (
 51	versionWidget = styles.Padded.Background(styles.DarkGrey).Foreground(styles.Text).Render(version.Version)
 52	helpWidget    = styles.Padded.Background(styles.Grey).Foreground(styles.Text).Render("? help")
 53)
 54
 55func (m statusCmp) View() string {
 56	status := styles.Padded.Background(styles.Grey).Foreground(styles.Text).Render("? help")
 57	if m.info != nil {
 58		infoStyle := styles.Padded.
 59			Foreground(styles.Base).
 60			Width(m.availableFooterMsgWidth())
 61		switch m.info.Type {
 62		case util.InfoTypeInfo:
 63			infoStyle = infoStyle.Background(styles.Blue)
 64		case util.InfoTypeWarn:
 65			infoStyle = infoStyle.Background(styles.Peach)
 66		case util.InfoTypeError:
 67			infoStyle = infoStyle.Background(styles.Red)
 68		}
 69		// Truncate message if it's longer than available width
 70		msg := m.info.Msg
 71		availWidth := m.availableFooterMsgWidth() - 3 // Account for ellipsis
 72		if len(msg) > availWidth && availWidth > 0 {
 73			msg = msg[:availWidth] + "..."
 74		}
 75		status += infoStyle.Render(msg)
 76	} else {
 77		status += styles.Padded.
 78			Foreground(styles.Base).
 79			Background(styles.LightGrey).
 80			Width(m.availableFooterMsgWidth()).
 81			Render("")
 82	}
 83	status += m.model()
 84	status += versionWidget
 85	return status
 86}
 87
 88func (m statusCmp) availableFooterMsgWidth() int {
 89	// -2 to accommodate padding
 90	return max(0, m.width-lipgloss.Width(helpWidget)-lipgloss.Width(versionWidget)-lipgloss.Width(m.model()))
 91}
 92
 93func (m statusCmp) model() string {
 94	model := models.SupportedModels[config.Get().Model.Coder]
 95	return styles.Padded.Background(styles.Grey).Foreground(styles.Text).Render(model.Name)
 96}
 97
 98func NewStatusCmp() tea.Model {
 99	return &statusCmp{
100		messageTTL: 10 * time.Second,
101	}
102}