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/logging"
9 "github.com/charmbracelet/crush/internal/pubsub"
10 "github.com/charmbracelet/crush/internal/session"
11 "github.com/charmbracelet/crush/internal/tui/styles"
12 "github.com/charmbracelet/crush/internal/tui/util"
13)
14
15type StatusCmp interface {
16 util.Model
17 ToggleFullHelp()
18 SetKeyMap(keyMap help.KeyMap)
19}
20
21type statusCmp struct {
22 info util.InfoMsg
23 width int
24 messageTTL time.Duration
25 session session.Session
26 help help.Model
27 keyMap help.KeyMap
28}
29
30// clearMessageCmd is a command that clears status messages after a timeout
31func (m *statusCmp) clearMessageCmd(ttl time.Duration) tea.Cmd {
32 return tea.Tick(ttl, func(time.Time) tea.Msg {
33 return util.ClearStatusMsg{}
34 })
35}
36
37func (m *statusCmp) Init() tea.Cmd {
38 return nil
39}
40
41func (m *statusCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
42 switch msg := msg.(type) {
43 case tea.WindowSizeMsg:
44 m.width = msg.Width
45 m.help.Width = msg.Width - 2
46 return m, nil
47
48 // Handle status info
49 case util.InfoMsg:
50 m.info = msg
51 ttl := msg.TTL
52 if ttl == 0 {
53 ttl = m.messageTTL
54 }
55 return m, m.clearMessageCmd(ttl)
56 case util.ClearStatusMsg:
57 m.info = util.InfoMsg{}
58
59 // Handle persistent logs
60 case pubsub.Event[logging.LogMessage]:
61 if msg.Payload.Persist {
62 switch msg.Payload.Level {
63 case "error":
64 m.info = util.InfoMsg{
65 Type: util.InfoTypeError,
66 Msg: msg.Payload.Message,
67 TTL: msg.Payload.PersistTime,
68 }
69 case "info":
70 m.info = util.InfoMsg{
71 Type: util.InfoTypeInfo,
72 Msg: msg.Payload.Message,
73 TTL: msg.Payload.PersistTime,
74 }
75 case "warn":
76 m.info = util.InfoMsg{
77 Type: util.InfoTypeWarn,
78 Msg: msg.Payload.Message,
79 TTL: msg.Payload.PersistTime,
80 }
81 default:
82 m.info = util.InfoMsg{
83 Type: util.InfoTypeInfo,
84 Msg: msg.Payload.Message,
85 TTL: msg.Payload.PersistTime,
86 }
87 }
88 }
89 }
90 return m, nil
91}
92
93func (m *statusCmp) View() tea.View {
94 t := styles.CurrentTheme()
95 status := t.S().Base.Padding(0, 1, 1, 1).Render(m.help.View(m.keyMap))
96 if m.info.Msg != "" {
97 switch m.info.Type {
98 case util.InfoTypeError:
99 status = t.S().Base.Background(t.Error).Padding(0, 1).Width(m.width).Render(m.info.Msg)
100 case util.InfoTypeWarn:
101 status = t.S().Base.Background(t.Warning).Padding(0, 1).Width(m.width).Render(m.info.Msg)
102 default:
103 status = t.S().Base.Background(t.Info).Padding(0, 1).Width(m.width).Render(m.info.Msg)
104 }
105 }
106 return tea.NewView(status)
107}
108
109func (m *statusCmp) ToggleFullHelp() {
110 m.help.ShowAll = !m.help.ShowAll
111}
112
113func (m *statusCmp) SetKeyMap(keyMap help.KeyMap) {
114 m.keyMap = keyMap
115}
116
117func NewStatusCmp(keyMap help.KeyMap) StatusCmp {
118 t := styles.CurrentTheme()
119 help := help.New()
120 help.Styles = t.S().Help
121 return &statusCmp{
122 messageTTL: 10 * time.Second,
123 help: help,
124 keyMap: keyMap,
125 }
126}