1package status
2
3import (
4 "time"
5
6 "github.com/charmbracelet/bubbles/v2/help"
7 tea "github.com/charmbracelet/bubbletea/v2"
8 "github.com/charmbracelet/crush/internal/tui/styles"
9 "github.com/charmbracelet/crush/internal/tui/util"
10 "github.com/charmbracelet/lipgloss/v2"
11 "github.com/charmbracelet/x/ansi"
12)
13
14type StatusCmp interface {
15 util.Model
16 ToggleFullHelp()
17 SetKeyMap(keyMap help.KeyMap)
18}
19
20type statusCmp struct {
21 info util.InfoMsg
22 width int
23 messageTTL time.Duration
24 help help.Model
25 keyMap help.KeyMap
26}
27
28// clearMessageCmd is a command that clears status messages after a timeout
29func (m *statusCmp) clearMessageCmd(ttl time.Duration) tea.Cmd {
30 return tea.Tick(ttl, func(time.Time) tea.Msg {
31 return util.ClearStatusMsg{}
32 })
33}
34
35func (m *statusCmp) Init() tea.Cmd {
36 return nil
37}
38
39func (m *statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
40 switch msg := msg.(type) {
41 case tea.WindowSizeMsg:
42 m.width = msg.Width
43 m.help.Width = msg.Width - 2
44 return m, nil
45
46 // Handle status info
47 case util.InfoMsg:
48 m.info = msg
49 ttl := msg.TTL
50 if ttl == 0 {
51 ttl = m.messageTTL
52 }
53 return m, m.clearMessageCmd(ttl)
54 case util.ClearStatusMsg:
55 m.info = util.InfoMsg{}
56 }
57 return m, nil
58}
59
60func (m *statusCmp) View() string {
61 t := styles.CurrentTheme()
62 status := t.S().Base.Padding(0, 1, 1, 1).Render(m.help.View(m.keyMap))
63 if m.info.Msg != "" {
64 status = m.infoMsg()
65 }
66 return status
67}
68
69func (m *statusCmp) infoMsg() string {
70 t := styles.CurrentTheme()
71 message := ""
72 infoType := ""
73 switch m.info.Type {
74 case util.InfoTypeError:
75 infoType = t.S().Base.Background(t.Red).Padding(0, 1).Render("ERROR")
76 widthLeft := m.width - (lipgloss.Width(infoType) + 2)
77 info := ansi.Truncate(m.info.Msg, widthLeft, "…")
78 message = t.S().Base.Background(t.Error).Width(widthLeft+2).Foreground(t.White).Padding(0, 1).Render(info)
79 case util.InfoTypeWarn:
80 infoType = t.S().Base.Foreground(t.BgOverlay).Background(t.Yellow).Padding(0, 1).Render("WARNING")
81 widthLeft := m.width - (lipgloss.Width(infoType) + 2)
82 info := ansi.Truncate(m.info.Msg, widthLeft, "…")
83 message = t.S().Base.Foreground(t.BgOverlay).Width(widthLeft+2).Background(t.Warning).Padding(0, 1).Render(info)
84 default:
85 infoType = t.S().Base.Foreground(t.BgOverlay).Background(t.Green).Padding(0, 1).Render("OKAY!")
86 widthLeft := m.width - (lipgloss.Width(infoType) + 2)
87 info := ansi.Truncate(m.info.Msg, widthLeft, "…")
88 message = t.S().Base.Background(t.Success).Width(widthLeft+2).Foreground(t.White).Padding(0, 1).Render(info)
89 }
90 return ansi.Truncate(infoType+message, m.width, "…")
91}
92
93func (m *statusCmp) ToggleFullHelp() {
94 m.help.ShowAll = !m.help.ShowAll
95}
96
97func (m *statusCmp) SetKeyMap(keyMap help.KeyMap) {
98 m.keyMap = keyMap
99}
100
101func NewStatusCmp() StatusCmp {
102 t := styles.CurrentTheme()
103 help := help.New()
104 help.Styles = t.S().Help
105 return &statusCmp{
106 messageTTL: 5 * time.Second,
107 help: help,
108 }
109}