status.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148
  1. package status
  2. import (
  3. "os"
  4. "strings"
  5. tea "github.com/charmbracelet/bubbletea/v2"
  6. "github.com/charmbracelet/lipgloss/v2"
  7. "github.com/charmbracelet/lipgloss/v2/compat"
  8. "github.com/sst/opencode/internal/app"
  9. "github.com/sst/opencode/internal/commands"
  10. "github.com/sst/opencode/internal/styles"
  11. "github.com/sst/opencode/internal/theme"
  12. )
  13. type StatusComponent interface {
  14. tea.Model
  15. tea.ViewModel
  16. }
  17. type statusComponent struct {
  18. app *app.App
  19. width int
  20. cwd string
  21. }
  22. func (m statusComponent) Init() tea.Cmd {
  23. return nil
  24. }
  25. func (m statusComponent) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
  26. switch msg := msg.(type) {
  27. case tea.WindowSizeMsg:
  28. m.width = msg.Width
  29. return m, nil
  30. }
  31. return m, nil
  32. }
  33. func (m statusComponent) logo() string {
  34. t := theme.CurrentTheme()
  35. base := styles.NewStyle().Foreground(t.TextMuted()).Background(t.BackgroundElement()).Render
  36. emphasis := styles.NewStyle().
  37. Foreground(t.Text()).
  38. Background(t.BackgroundElement()).
  39. Bold(true).
  40. Render
  41. open := base("open")
  42. code := emphasis("code ")
  43. version := base(m.app.Version)
  44. return styles.NewStyle().
  45. Background(t.BackgroundElement()).
  46. Padding(0, 1).
  47. Render(open + code + version)
  48. }
  49. func (m statusComponent) View() string {
  50. t := theme.CurrentTheme()
  51. logo := m.logo()
  52. cwd := styles.NewStyle().
  53. Foreground(t.TextMuted()).
  54. Background(t.BackgroundPanel()).
  55. Padding(0, 1).
  56. Render(m.cwd)
  57. var modeBackground compat.AdaptiveColor
  58. var modeForeground compat.AdaptiveColor
  59. switch m.app.ModeIndex {
  60. case 0:
  61. modeBackground = t.BackgroundElement()
  62. modeForeground = t.TextMuted()
  63. case 1:
  64. modeBackground = t.Secondary()
  65. modeForeground = t.BackgroundPanel()
  66. case 2:
  67. modeBackground = t.Accent()
  68. modeForeground = t.BackgroundPanel()
  69. case 3:
  70. modeBackground = t.Success()
  71. modeForeground = t.BackgroundPanel()
  72. case 4:
  73. modeBackground = t.Warning()
  74. modeForeground = t.BackgroundPanel()
  75. case 5:
  76. modeBackground = t.Primary()
  77. modeForeground = t.BackgroundPanel()
  78. case 6:
  79. modeBackground = t.Error()
  80. modeForeground = t.BackgroundPanel()
  81. default:
  82. modeBackground = t.Secondary()
  83. modeForeground = t.BackgroundPanel()
  84. }
  85. command := m.app.Commands[commands.SwitchModeCommand]
  86. kb := command.Keybindings[0]
  87. key := kb.Key
  88. if kb.RequiresLeader {
  89. key = m.app.Config.Keybinds.Leader + " " + kb.Key
  90. }
  91. modeStyle := styles.NewStyle().Background(modeBackground).Foreground(modeForeground)
  92. modeNameStyle := modeStyle.Bold(true).Render
  93. modeDescStyle := modeStyle.Render
  94. mode := modeNameStyle(strings.ToUpper(m.app.Mode.Name)) + modeDescStyle(" MODE")
  95. mode = modeStyle.
  96. Padding(0, 1).
  97. BorderLeft(true).
  98. BorderStyle(lipgloss.ThickBorder()).
  99. BorderForeground(modeBackground).
  100. BorderBackground(t.BackgroundPanel()).
  101. Render(mode)
  102. mode = styles.NewStyle().
  103. Faint(true).
  104. Background(t.BackgroundPanel()).
  105. Foreground(t.TextMuted()).
  106. Render(key+" ") +
  107. mode
  108. space := max(
  109. 0,
  110. m.width-lipgloss.Width(logo)-lipgloss.Width(cwd)-lipgloss.Width(mode),
  111. )
  112. spacer := styles.NewStyle().Background(t.BackgroundPanel()).Width(space).Render("")
  113. status := logo + cwd + spacer + mode
  114. blank := styles.NewStyle().Background(t.Background()).Width(m.width).Render("")
  115. return blank + "\n" + status
  116. }
  117. func NewStatusCmp(app *app.App) StatusComponent {
  118. statusComponent := &statusComponent{
  119. app: app,
  120. }
  121. homePath, err := os.UserHomeDir()
  122. cwdPath := app.Info.Path.Cwd
  123. if err == nil && homePath != "" && strings.HasPrefix(cwdPath, homePath) {
  124. cwdPath = "~" + cwdPath[len(homePath):]
  125. }
  126. statusComponent.cwd = cwdPath
  127. return statusComponent
  128. }