status.go 2.7 KB

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