onboarding.go 3.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115
  1. package model
  2. import (
  3. "fmt"
  4. "log/slog"
  5. "strings"
  6. "charm.land/bubbles/v2/key"
  7. tea "charm.land/bubbletea/v2"
  8. "charm.land/lipgloss/v2"
  9. "github.com/charmbracelet/crush/internal/agent"
  10. "github.com/charmbracelet/crush/internal/config"
  11. "github.com/charmbracelet/crush/internal/home"
  12. "github.com/charmbracelet/crush/internal/ui/common"
  13. "github.com/charmbracelet/crush/internal/ui/util"
  14. )
  15. // markProjectInitialized marks the current project as initialized in the config.
  16. func (m *UI) markProjectInitialized() tea.Msg {
  17. // TODO: handle error so we show it in the tui footer
  18. err := config.MarkProjectInitialized(m.com.Config())
  19. if err != nil {
  20. slog.Error(err.Error())
  21. }
  22. return nil
  23. }
  24. // updateInitializeView handles keyboard input for the project initialization prompt.
  25. func (m *UI) updateInitializeView(msg tea.KeyPressMsg) (cmds []tea.Cmd) {
  26. switch {
  27. case key.Matches(msg, m.keyMap.Initialize.Enter):
  28. if m.onboarding.yesInitializeSelected {
  29. cmds = append(cmds, m.initializeProject())
  30. } else {
  31. cmds = append(cmds, m.skipInitializeProject())
  32. }
  33. case key.Matches(msg, m.keyMap.Initialize.Switch):
  34. m.onboarding.yesInitializeSelected = !m.onboarding.yesInitializeSelected
  35. case key.Matches(msg, m.keyMap.Initialize.Yes):
  36. cmds = append(cmds, m.initializeProject())
  37. case key.Matches(msg, m.keyMap.Initialize.No):
  38. cmds = append(cmds, m.skipInitializeProject())
  39. }
  40. return cmds
  41. }
  42. // initializeProject starts project initialization and transitions to the landing view.
  43. func (m *UI) initializeProject() tea.Cmd {
  44. // clear the session
  45. var cmds []tea.Cmd
  46. if cmd := m.newSession(); cmd != nil {
  47. cmds = append(cmds, cmd)
  48. }
  49. cfg := m.com.Config()
  50. initialize := func() tea.Msg {
  51. initPrompt, err := agent.InitializePrompt(*cfg)
  52. if err != nil {
  53. return util.InfoMsg{Type: util.InfoTypeError, Msg: err.Error()}
  54. }
  55. return sendMessageMsg{Content: initPrompt}
  56. }
  57. // Mark the project as initialized
  58. cmds = append(cmds, initialize, m.markProjectInitialized)
  59. return tea.Sequence(cmds...)
  60. }
  61. // skipInitializeProject skips project initialization and transitions to the landing view.
  62. func (m *UI) skipInitializeProject() tea.Cmd {
  63. // TODO: initialize the project
  64. m.setState(uiLanding, uiFocusEditor)
  65. // mark the project as initialized
  66. return m.markProjectInitialized
  67. }
  68. // initializeView renders the project initialization prompt with Yes/No buttons.
  69. func (m *UI) initializeView() string {
  70. cfg := m.com.Config()
  71. s := m.com.Styles.Initialize
  72. cwd := home.Short(cfg.WorkingDir())
  73. initFile := cfg.Options.InitializeAs
  74. header := s.Header.Render("Would you like to initialize this project?")
  75. path := s.Accent.PaddingLeft(2).Render(cwd)
  76. desc := s.Content.Render(fmt.Sprintf("When I initialize your codebase I examine the project and put the result into an %s file which serves as general context.", initFile))
  77. hint := s.Content.Render("You can also initialize anytime via ") + s.Accent.Render("ctrl+p") + s.Content.Render(".")
  78. prompt := s.Content.Render("Would you like to initialize now?")
  79. buttons := common.ButtonGroup(m.com.Styles, []common.ButtonOpts{
  80. {Text: "Yep!", Selected: m.onboarding.yesInitializeSelected},
  81. {Text: "Nope", Selected: !m.onboarding.yesInitializeSelected},
  82. }, " ")
  83. // max width 60 so the text is compact
  84. width := min(m.layout.main.Dx(), 60)
  85. return lipgloss.NewStyle().
  86. Width(width).
  87. Height(m.layout.main.Dy()).
  88. PaddingBottom(1).
  89. AlignVertical(lipgloss.Bottom).
  90. Render(strings.Join(
  91. []string{
  92. header,
  93. path,
  94. desc,
  95. hint,
  96. prompt,
  97. buttons,
  98. },
  99. "\n\n",
  100. ))
  101. }