status.go 2.9 KB

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