status.go 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596
  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/pubsub"
  9. "github.com/kujtimiihoxha/termai/internal/tui/styles"
  10. "github.com/kujtimiihoxha/termai/internal/tui/util"
  11. "github.com/kujtimiihoxha/termai/internal/version"
  12. )
  13. type statusCmp struct {
  14. info *util.InfoMsg
  15. width int
  16. messageTTL time.Duration
  17. }
  18. // clearMessageCmd is a command that clears status messages after a timeout
  19. func (m statusCmp) clearMessageCmd() tea.Cmd {
  20. return tea.Tick(m.messageTTL, func(time.Time) tea.Msg {
  21. return util.ClearStatusMsg{}
  22. })
  23. }
  24. func (m statusCmp) Init() tea.Cmd {
  25. return nil
  26. }
  27. func (m statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  28. switch msg := msg.(type) {
  29. case tea.WindowSizeMsg:
  30. m.width = msg.Width
  31. return m, m.clearMessageCmd()
  32. case pubsub.Event[util.InfoMsg]:
  33. m.info = &msg.Payload
  34. return m, m.clearMessageCmd()
  35. case util.InfoMsg:
  36. m.info = &msg
  37. return m, m.clearMessageCmd()
  38. case util.ClearStatusMsg:
  39. m.info = nil
  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 != nil {
  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. status += infoStyle.Render(m.info.Msg)
  62. } else {
  63. status += styles.Padded.
  64. Foreground(styles.Base).
  65. Background(styles.LightGrey).
  66. Width(m.availableFooterMsgWidth()).
  67. Render("")
  68. }
  69. status += m.model()
  70. status += versionWidget
  71. return status
  72. }
  73. func (m statusCmp) availableFooterMsgWidth() int {
  74. // -2 to accommodate padding
  75. return max(0, m.width-lipgloss.Width(helpWidget)-lipgloss.Width(versionWidget)-lipgloss.Width(m.model()))
  76. }
  77. func (m statusCmp) model() string {
  78. model := models.SupportedModels[config.Get().Model.Coder]
  79. return styles.Padded.Background(styles.Grey).Foreground(styles.Text).Render(model.Name)
  80. }
  81. func NewStatusCmp() tea.Model {
  82. return &statusCmp{
  83. messageTTL: 15 * time.Second,
  84. }
  85. }