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