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