1package styles
2
3import (
4 "github.com/charmbracelet/bubbles/key"
5 tea "github.com/charmbracelet/bubbletea"
6 "github.com/charmbracelet/lipgloss"
7)
8
9// ConfirmModel is a yes/no confirmation dialog.
10type ConfirmModel struct {
11 Message string
12 Yes bool // Current selection
13 Confirmed bool // User pressed enter
14 Canceled bool // User pressed escape
15 theme *Theme
16}
17
18// ConfirmKeyMap defines keybindings for the confirm dialog.
19type ConfirmKeyMap struct {
20 Yes key.Binding
21 No key.Binding
22 Left key.Binding
23 Right key.Binding
24 Confirm key.Binding
25 Cancel key.Binding
26}
27
28// DefaultConfirmKeyMap returns the default keybindings.
29func DefaultConfirmKeyMap() ConfirmKeyMap {
30 return ConfirmKeyMap{
31 Yes: key.NewBinding(key.WithKeys("y"), key.WithHelp("y", "yes")),
32 No: key.NewBinding(key.WithKeys("n"), key.WithHelp("n", "no")),
33 Left: key.NewBinding(key.WithKeys("left", "h"), key.WithHelp("←/h", "no")),
34 Right: key.NewBinding(key.WithKeys("right", "l"), key.WithHelp("→/l", "yes")),
35 Confirm: key.NewBinding(key.WithKeys("enter"), key.WithHelp("enter", "confirm")),
36 Cancel: key.NewBinding(key.WithKeys("esc"), key.WithHelp("esc", "cancel")),
37 }
38}
39
40// NewConfirm creates a new confirmation dialog.
41func NewConfirm(theme *Theme, message string) ConfirmModel {
42 return ConfirmModel{
43 Message: message,
44 Yes: false, // Default to "No"
45 theme: theme,
46 }
47}
48
49// Init implements tea.Model.
50func (m ConfirmModel) Init() tea.Cmd {
51 if m.theme == nil {
52 return nil
53 }
54 return nil
55}
56
57// Update implements tea.Model.
58func (m ConfirmModel) Update(msg tea.Msg) (ConfirmModel, tea.Cmd) {
59 keys := DefaultConfirmKeyMap()
60
61 switch msg := msg.(type) {
62 case tea.KeyMsg:
63 switch {
64 case key.Matches(msg, keys.Yes):
65 m.Yes = true
66 case key.Matches(msg, keys.No):
67 m.Yes = false
68 case key.Matches(msg, keys.Left):
69 m.Yes = false
70 case key.Matches(msg, keys.Right):
71 m.Yes = true
72 case key.Matches(msg, keys.Confirm):
73 m.Confirmed = true
74 case key.Matches(msg, keys.Cancel):
75 m.Canceled = true
76 }
77 }
78
79 return m, nil
80}
81
82// View implements tea.Model.
83func (m ConfirmModel) View() string {
84 t := m.theme
85
86 // Build buttons
87 yesStyle := t.InactiveTab
88 noStyle := t.InactiveTab
89
90 if m.Yes {
91 yesStyle = t.ActiveTab
92 } else {
93 noStyle = t.ActiveTab
94 }
95
96 yes := yesStyle.Render(" Yes ")
97 no := noStyle.Render(" No ")
98
99 buttons := lipgloss.JoinHorizontal(lipgloss.Center, no, " ", yes)
100
101 // Build dialog box
102 content := lipgloss.JoinVertical(
103 lipgloss.Center,
104 t.Title.Render(m.Message),
105 "",
106 buttons,
107 "",
108 t.Subtle.Render("y/n or ←/→ to select • enter to confirm • esc to cancel"),
109 )
110
111 return t.Box.Render(content)
112}
113
114// Done returns true if the dialog is complete.
115func (m ConfirmModel) Done() bool {
116 return m.Confirmed || m.Canceled
117}
118
119// Result returns true if user confirmed "Yes".
120func (m ConfirmModel) Result() bool {
121 return m.Confirmed && m.Yes
122}
123
124// ConfirmResult wraps the confirm dialog result for parent models.
125type ConfirmResult struct {
126 Confirmed bool
127 Yes bool
128}
129
130// ConfirmResultMsg is sent when the confirm dialog completes.
131type ConfirmResultMsg ConfirmResult