1package dialog
2
3import (
4 "charm.land/bubbles/v2/key"
5 tea "charm.land/bubbletea/v2"
6 "charm.land/lipgloss/v2"
7 "github.com/charmbracelet/crush/internal/ui/common"
8)
9
10// QuitID is the identifier for the quit dialog.
11const QuitID = "quit"
12
13// Quit represents a confirmation dialog for quitting the application.
14type Quit struct {
15 com *common.Common
16 selectedNo bool // true if "No" button is selected
17 keyMap struct {
18 LeftRight,
19 EnterSpace,
20 Yes,
21 No,
22 Tab,
23 Close,
24 Quit key.Binding
25 }
26}
27
28// NewQuit creates a new quit confirmation dialog.
29func NewQuit(com *common.Common) *Quit {
30 q := &Quit{
31 com: com,
32 selectedNo: true,
33 }
34 q.keyMap.LeftRight = key.NewBinding(
35 key.WithKeys("left", "right"),
36 key.WithHelp("←/→", "switch options"),
37 )
38 q.keyMap.EnterSpace = key.NewBinding(
39 key.WithKeys("enter", " "),
40 key.WithHelp("enter/space", "confirm"),
41 )
42 q.keyMap.Yes = key.NewBinding(
43 key.WithKeys("y", "Y", "ctrl+c"),
44 key.WithHelp("y/Y/ctrl+c", "yes"),
45 )
46 q.keyMap.No = key.NewBinding(
47 key.WithKeys("n", "N"),
48 key.WithHelp("n/N", "no"),
49 )
50 q.keyMap.Tab = key.NewBinding(
51 key.WithKeys("tab"),
52 key.WithHelp("tab", "switch options"),
53 )
54 q.keyMap.Close = CloseKey
55 q.keyMap.Quit = key.NewBinding(
56 key.WithKeys("ctrl+c"),
57 key.WithHelp("ctrl+c", "quit"),
58 )
59 return q
60}
61
62// ID implements [Model].
63func (*Quit) ID() string {
64 return QuitID
65}
66
67// Update implements [Model].
68func (q *Quit) Update(msg tea.Msg) tea.Msg {
69 switch msg := msg.(type) {
70 case tea.KeyPressMsg:
71 switch {
72 case key.Matches(msg, q.keyMap.Quit):
73 return QuitMsg{}
74 case key.Matches(msg, q.keyMap.Close):
75 return CloseMsg{}
76 case key.Matches(msg, q.keyMap.LeftRight, q.keyMap.Tab):
77 q.selectedNo = !q.selectedNo
78 case key.Matches(msg, q.keyMap.EnterSpace):
79 if !q.selectedNo {
80 return QuitMsg{}
81 }
82 return CloseMsg{}
83 case key.Matches(msg, q.keyMap.Yes):
84 return QuitMsg{}
85 case key.Matches(msg, q.keyMap.No, q.keyMap.Close):
86 return CloseMsg{}
87 }
88 }
89
90 return nil
91}
92
93// View implements [Dialog].
94func (q *Quit) View() string {
95 const question = "Are you sure you want to quit?"
96 baseStyle := q.com.Styles.Base
97 buttonOpts := []common.ButtonOpts{
98 {Text: "Yep!", Selected: !q.selectedNo, Padding: 3},
99 {Text: "Nope", Selected: q.selectedNo, Padding: 3},
100 }
101 buttons := common.ButtonGroup(q.com.Styles, buttonOpts, " ")
102 content := baseStyle.Render(
103 lipgloss.JoinVertical(
104 lipgloss.Center,
105 question,
106 "",
107 buttons,
108 ),
109 )
110
111 return q.com.Styles.BorderFocus.Render(content)
112}
113
114// ShortHelp implements [help.KeyMap].
115func (q *Quit) ShortHelp() []key.Binding {
116 return []key.Binding{
117 q.keyMap.LeftRight,
118 q.keyMap.EnterSpace,
119 }
120}
121
122// FullHelp implements [help.KeyMap].
123func (q *Quit) FullHelp() [][]key.Binding {
124 return [][]key.Binding{
125 {q.keyMap.LeftRight, q.keyMap.EnterSpace, q.keyMap.Yes, q.keyMap.No},
126 {q.keyMap.Tab, q.keyMap.Close},
127 }
128}