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}