1package models
2
3import (
4 "charm.land/bubbles/v2/key"
5)
6
7type KeyMap struct {
8 Select,
9 Next,
10 Previous,
11 Choose,
12 Tab,
13 Close key.Binding
14
15 isAPIKeyHelp bool
16 isAPIKeyValid bool
17
18 isHyperDeviceFlow bool
19 isCopilotDeviceFlow bool
20 isCopilotUnavailable bool
21
22 isClaudeAuthChoiceHelp bool
23 isClaudeOAuthHelp bool
24 isClaudeOAuthURLState bool
25 isClaudeOAuthHelpComplete bool
26}
27
28func DefaultKeyMap() KeyMap {
29 return KeyMap{
30 Select: key.NewBinding(
31 key.WithKeys("enter", "ctrl+y"),
32 key.WithHelp("enter", "choose"),
33 ),
34 Next: key.NewBinding(
35 key.WithKeys("down", "ctrl+n"),
36 key.WithHelp("↓", "next item"),
37 ),
38 Previous: key.NewBinding(
39 key.WithKeys("up", "ctrl+p"),
40 key.WithHelp("↑", "previous item"),
41 ),
42 Choose: key.NewBinding(
43 key.WithKeys("left", "right", "h", "l"),
44 key.WithHelp("←→", "choose"),
45 ),
46 Tab: key.NewBinding(
47 key.WithKeys("tab"),
48 key.WithHelp("tab", "toggle type"),
49 ),
50 Close: key.NewBinding(
51 key.WithKeys("esc", "alt+esc"),
52 key.WithHelp("esc", "exit"),
53 ),
54 }
55}
56
57// KeyBindings implements layout.KeyMapProvider
58func (k KeyMap) KeyBindings() []key.Binding {
59 return []key.Binding{
60 k.Select,
61 k.Next,
62 k.Previous,
63 k.Tab,
64 k.Close,
65 }
66}
67
68// FullHelp implements help.KeyMap.
69func (k KeyMap) FullHelp() [][]key.Binding {
70 m := [][]key.Binding{}
71 slice := k.KeyBindings()
72 for i := 0; i < len(slice); i += 4 {
73 end := min(i+4, len(slice))
74 m = append(m, slice[i:end])
75 }
76 return m
77}
78
79// ShortHelp implements help.KeyMap.
80func (k KeyMap) ShortHelp() []key.Binding {
81 if k.isHyperDeviceFlow || k.isCopilotDeviceFlow {
82 return []key.Binding{
83 key.NewBinding(
84 key.WithKeys("c"),
85 key.WithHelp("c", "copy code"),
86 ),
87 key.NewBinding(
88 key.WithKeys("enter"),
89 key.WithHelp("enter", "copy & open"),
90 ),
91 k.Close,
92 }
93 }
94 if k.isCopilotUnavailable {
95 return []key.Binding{
96 key.NewBinding(
97 key.WithKeys("enter"),
98 key.WithHelp("enter", "open signup"),
99 ),
100 k.Close,
101 }
102 }
103 if k.isClaudeAuthChoiceHelp {
104 return []key.Binding{
105 key.NewBinding(
106 key.WithKeys("left", "right", "h", "l"),
107 key.WithHelp("←→", "choose"),
108 ),
109 key.NewBinding(
110 key.WithKeys("enter"),
111 key.WithHelp("enter", "accept"),
112 ),
113 key.NewBinding(
114 key.WithKeys("esc"),
115 key.WithHelp("esc", "back"),
116 ),
117 }
118 }
119 if k.isClaudeOAuthHelp {
120 if k.isClaudeOAuthHelpComplete {
121 return []key.Binding{
122 key.NewBinding(
123 key.WithKeys("enter"),
124 key.WithHelp("enter", "close"),
125 ),
126 }
127 }
128
129 enterHelp := "submit"
130 if k.isClaudeOAuthURLState {
131 enterHelp = "open"
132 }
133
134 bindings := []key.Binding{
135 key.NewBinding(
136 key.WithKeys("enter"),
137 key.WithHelp("enter", enterHelp),
138 ),
139 }
140
141 if k.isClaudeOAuthURLState {
142 bindings = append(bindings, key.NewBinding(
143 key.WithKeys("c"),
144 key.WithHelp("c", "copy url"),
145 ))
146 }
147
148 bindings = append(bindings, key.NewBinding(
149 key.WithKeys("esc"),
150 key.WithHelp("esc", "back"),
151 ))
152
153 return bindings
154 }
155 if k.isAPIKeyHelp && !k.isAPIKeyValid {
156 return []key.Binding{
157 key.NewBinding(
158 key.WithKeys("enter"),
159 key.WithHelp("enter", "submit"),
160 ),
161 k.Close,
162 }
163 } else if k.isAPIKeyValid {
164 return []key.Binding{
165 k.Select,
166 }
167 }
168 return []key.Binding{
169 key.NewBinding(
170 key.WithKeys("down", "up"),
171 key.WithHelp("↑↓", "choose"),
172 ),
173 k.Tab,
174 k.Select,
175 k.Close,
176 }
177}