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