main 5041353

confirm.go RawHistoryFolder

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

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 8

Tags 42

↑↓ moveenter openesc close