1package status
2
3import (
4 "time"
5
6 "charm.land/bubbles/v2/help"
7 tea "charm.land/bubbletea/v2"
8 "charm.land/lipgloss/v2"
9 "git.secluded.site/crush/internal/tui/styles"
10 "git.secluded.site/crush/internal/tui/util"
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) (util.Model, tea.Cmd) {
40 switch msg := msg.(type) {
41 case tea.WindowSizeMsg:
42 m.width = msg.Width
43 m.help.SetWidth(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 note := "OKAY!"
86 if m.info.Type == util.InfoTypeUpdate {
87 note = "HEY!"
88 }
89 infoType = t.S().Base.Foreground(t.BgSubtle).Background(t.Green).Padding(0, 1).Bold(true).Render(note)
90 widthLeft := m.width - (lipgloss.Width(infoType) + 2)
91 info := ansi.Truncate(m.info.Msg, widthLeft, "…")
92 message = t.S().Base.Background(t.GreenDark).Width(widthLeft+2).Foreground(t.BgSubtle).Padding(0, 1).Render(info)
93 }
94 return ansi.Truncate(infoType+message, m.width, "…")
95}
96
97func (m *statusCmp) ToggleFullHelp() {
98 m.help.ShowAll = !m.help.ShowAll
99}
100
101func (m *statusCmp) SetKeyMap(keyMap help.KeyMap) {
102 m.keyMap = keyMap
103}
104
105func NewStatusCmp() StatusCmp {
106 t := styles.CurrentTheme()
107 help := help.New()
108 help.Styles = t.S().Help
109 return &statusCmp{
110 messageTTL: 5 * time.Second,
111 help: help,
112 }
113}