@@ -12,18 +12,29 @@ import (
12
12
tea "github.com/charmbracelet/bubbletea"
13
13
)
14
14
15
+ var (
16
+ focusedStyle = lipgloss .NewStyle ().Foreground (lipgloss .Color ("#6FD0FB" )).Bold (true )
17
+ blurredStyle = lipgloss .NewStyle ().Foreground (lipgloss .Color ("240" ))
18
+ cursorStyle = focusedStyle .Copy ()
19
+ noStyle = lipgloss .NewStyle ()
20
+
21
+ stagedFilesStyle = lipgloss .NewStyle ().Foreground (lipgloss .Color ("#10ffcb" ))
22
+ cursorModeHelpStyle = lipgloss .NewStyle ().Foreground (lipgloss .Color ("244" ))
23
+
24
+ focusedButton = focusedStyle .Copy ().Render ("[ Commit ]" )
25
+ blurredButton = fmt .Sprintf ("[ %s ]" , blurredStyle .Render ("Commit" ))
26
+ )
27
+
15
28
type (
16
29
errMsg error
17
30
)
18
31
19
32
type model struct {
20
- inputLabel string
21
- commitMessageInput textinput.Model
22
- flagsInput textinput.Model
23
- branch string
24
- err error
25
- askingForFlags bool
26
- stagedFiles []string
33
+ focusInputIndex int
34
+ inputs []textinput.Model
35
+ branch string
36
+ err error
37
+ stagedFiles []string
27
38
}
28
39
29
40
func main () {
@@ -38,11 +49,8 @@ func initialModel() model {
38
49
if err != nil {
39
50
log .Fatal (err )
40
51
}
41
- stagedFiles , errStaged := getStagedFiles ()
42
52
43
- if errStaged != nil {
44
- log .Fatal (errStaged )
45
- }
53
+ stagedFiles := getStagedFiles ()
46
54
47
55
result := strings .Split (branch , "/" )
48
56
@@ -55,34 +63,77 @@ func initialModel() model {
55
63
branchType := strings .ToUpper (result [0 ])
56
64
ticketId := strings .ToUpper (result [1 ])
57
65
58
- ti := textinput .New ()
59
- ti .SetValue (fmt .Sprintf ("[%s] [%s] " , branchType , ticketId ))
60
- ti .Focus ()
61
- ti .CharLimit = 156
62
-
63
- return model {
64
- inputLabel : "Enter commit message:" ,
65
- commitMessageInput : ti ,
66
- branch : branch ,
67
- err : nil ,
68
- stagedFiles : stagedFiles ,
66
+ m := model {
67
+ inputs : make ([]textinput.Model , 2 ),
68
+ branch : branch ,
69
+ err : nil ,
70
+ focusInputIndex : 0 ,
71
+ stagedFiles : stagedFiles ,
72
+ }
73
+
74
+ var t textinput.Model
75
+ for i := range m .inputs {
76
+ t = textinput .New ()
77
+ t .Cursor .Style = cursorStyle
78
+ t .CharLimit = 72
79
+
80
+ switch i {
81
+ case 0 :
82
+ t .SetValue (fmt .Sprintf ("[%s] [%s] " , branchType , ticketId ))
83
+ t .Focus ()
84
+ t .PromptStyle = focusedStyle
85
+ t .TextStyle = focusedStyle
86
+ case 1 :
87
+ t .ShowSuggestions = true
88
+ commitFlags := []string {
89
+ "--message" ,
90
+ "--all" ,
91
+ "--patch" ,
92
+ "--reuse-message" ,
93
+ "--amend" ,
94
+ "--signoff" ,
95
+ "--no-verify" ,
96
+ "--allow-empty" ,
97
+ "--no-edit" ,
98
+ }
99
+ t .SetSuggestions (commitFlags )
100
+ t .Placeholder = "Commit flags"
101
+ }
102
+
103
+ m .inputs [i ] = t
69
104
}
105
+
106
+ return m
107
+ }
108
+
109
+ func getCurrentGitBranch () (string , error ) {
110
+ cmd := exec .Command ("git" , "branch" , "--show-current" )
111
+ output , err := cmd .CombinedOutput ()
112
+ if err != nil {
113
+ return "" , fmt .Errorf ("error getting branch name: %v" , err )
114
+ }
115
+
116
+ return strings .TrimSpace (string (output )), nil
70
117
}
71
118
72
119
func (m model ) Init () tea.Cmd {
73
120
return textinput .Blink
74
121
}
75
122
76
123
func (m model ) Update (msg tea.Msg ) (tea.Model , tea.Cmd ) {
77
- var cmd tea.Cmd
78
-
79
124
switch msg := msg .(type ) {
80
125
case tea.KeyMsg :
81
126
switch msg .Type {
82
- case tea .KeyEnter :
83
- if m .askingForFlags {
127
+
128
+ // Set focus to next input
129
+ case tea .KeyShiftTab , tea .KeyEnter , tea .KeyUp , tea .KeyDown :
130
+ s := msg .String ()
131
+
132
+ // Did the user press enter while the submit button was focused?
133
+ // If so, exit.
134
+ if s == "enter" && m .focusInputIndex == len (m .inputs ) {
84
135
// Execute git commit command with flags
85
- commitCmd := exec .Command ("git" , "commit" , m .flagsInput .Value (), "-m" , m .commitMessageInput .Value ())
136
+ commitCmd := exec .Command ("git" , "commit" , m .inputs [ 0 ] .Value (), "-m" , m .inputs [ 1 ] .Value ())
86
137
commitCmd .Stdout = os .Stdout
87
138
commitCmd .Stderr = os .Stderr
88
139
@@ -92,85 +143,108 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
92
143
}
93
144
94
145
return m , tea .Quit
146
+ }
147
+
148
+ // Cycle indexes
149
+ if s == "up" || s == "shift+tab" {
150
+ m .focusInputIndex --
95
151
} else {
96
- m .inputLabel = "Commit flags"
97
- // Ask for commit flags
98
- flagsInput := textinput .New ()
99
- commitFlags := []string {
100
- "--message" ,
101
- "--all" ,
102
- "--patch" ,
103
- "--reuse-message" ,
104
- "--amend" ,
105
- "--signoff" ,
106
- "--no-verify" ,
107
- "--allow-empty" ,
108
- "--no-edit" ,
152
+ m .focusInputIndex ++
153
+ }
154
+
155
+ if m .focusInputIndex > len (m .inputs ) {
156
+ m .focusInputIndex = 0
157
+ } else if m .focusInputIndex < 0 {
158
+ m .focusInputIndex = len (m .inputs )
159
+ }
160
+
161
+ cmds := make ([]tea.Cmd , len (m .inputs ))
162
+ for i := 0 ; i <= len (m .inputs )- 1 ; i ++ {
163
+ if i == m .focusInputIndex {
164
+ // Set focused state
165
+ cmds [i ] = m .inputs [i ].Focus ()
166
+ m .inputs [i ].PromptStyle = focusedStyle
167
+ m .inputs [i ].TextStyle = focusedStyle
168
+ continue
109
169
}
110
- flagsInput .SetSuggestions (commitFlags )
111
- flagsInput .ShowSuggestions = true
112
- flagsInput .Focus ()
113
- m .flagsInput = flagsInput
114
- m .askingForFlags = true
115
- return m , nil
170
+ // Remove focused state
171
+ m .inputs [i ].Blur ()
172
+ m .inputs [i ].PromptStyle = noStyle
173
+ m .inputs [i ].TextStyle = noStyle
116
174
}
175
+
176
+ return m , tea .Batch (cmds ... )
117
177
case tea .KeyCtrlC , tea .KeyEsc :
118
178
return m , tea .Quit
119
179
}
120
180
}
121
181
122
- if m .askingForFlags {
123
- m .flagsInput , cmd = m .flagsInput .Update (msg )
124
- } else {
125
- m .commitMessageInput , cmd = m .commitMessageInput .Update (msg )
126
- }
182
+ cmd := m .updateInputs (msg )
127
183
128
184
return m , cmd
129
185
}
130
186
131
- func (m model ) View () string {
132
- var inputView string
133
- if m .askingForFlags {
134
- inputView = m .flagsInput .View ()
135
- } else {
136
- inputView = m .commitMessageInput .View ()
187
+ func (m * model ) updateInputs (msg tea.Msg ) tea.Cmd {
188
+ cmds := make ([]tea.Cmd , len (m .inputs ))
189
+
190
+ for i := range m .inputs {
191
+ m .inputs [i ], cmds [i ] = m .inputs [i ].Update (msg )
137
192
}
138
193
139
- currentCommitMessage := "Current commit message: " + m .commitMessageInput .Value () + " " + m .flagsInput .Value ()
194
+ return tea .Batch (cmds ... )
195
+ }
140
196
141
- stagedFiles := strings .Join (m .stagedFiles , "\n " )
197
+ func (m model ) View () string {
198
+ var b strings.Builder
142
199
143
- currentBranch := lipgloss .NewStyle ().Foreground (lipgloss .Color ("#fbd87f" )).SetString (fmt .Sprintf ("Current branch: %s" , m .branch ))
144
- inputLabel := lipgloss .NewStyle ().Foreground (lipgloss .Color ("#b5f8fe" )).SetString (m .inputLabel ).Bold (true )
145
- inputStyle := lipgloss .NewStyle ().SetString (inputView )
200
+ currentBranch := lipgloss .NewStyle ().Foreground (lipgloss .Color ("#fcbda1" )).SetString (fmt .Sprintf ("Branch: %s" , m .branch ))
146
201
147
- return fmt .Sprintf (
148
- "%s\n \n %s\n \n %s\n %s\n \n %s" ,
149
- currentBranch .Render (),
150
- currentCommitMessage ,
151
- inputLabel .Render (),
152
- inputStyle .Render (),
153
- stagedFiles ,
154
- ) + "\n "
155
- }
202
+ b .WriteString (currentBranch .Render ())
203
+ b .WriteRune ('\n' )
204
+ b .WriteRune ('\n' )
156
205
157
- // cmds
158
- func getCurrentGitBranch () (string , error ) {
159
- cmd := exec .Command ("git" , "branch" , "--show-current" )
160
- output , err := cmd .CombinedOutput ()
161
- if err != nil {
162
- return "" , fmt .Errorf ("error getting branch name: %v" , err )
206
+ currentCommitCommand := lipgloss .NewStyle ().Foreground (lipgloss .Color ("#a1e0fc" )).SetString (fmt .Sprintf ("git commit -m \" %s\" %s" , m .inputs [0 ].Value (), m .inputs [1 ].Value ()))
207
+
208
+ b .WriteString (currentCommitCommand .Render ())
209
+ b .WriteRune ('\n' )
210
+ b .WriteRune ('\n' )
211
+
212
+ inputLabels := []string {"Commit message " , "Commit flags " }
213
+
214
+ for i := range m .inputs {
215
+ if i == m .focusInputIndex {
216
+ b .WriteString (lipgloss .NewStyle ().Foreground (lipgloss .Color ("#6FD0FB" )).Bold (true ).SetString (inputLabels [i ]).Render ())
217
+ } else {
218
+ b .WriteString (inputLabels [i ])
219
+ }
220
+ b .WriteString (m .inputs [i ].View ())
221
+ b .WriteRune ('\n' )
222
+ if i < len (m .inputs )- 1 {
223
+ b .WriteRune ('\n' )
224
+ }
163
225
}
164
226
165
- return strings .TrimSpace (string (output )), nil
227
+ button := & blurredButton
228
+ if m .focusInputIndex == len (m .inputs ) {
229
+ button = & focusedButton
230
+ }
231
+ fmt .Fprintf (& b , "\n \n %s\n \n " , * button )
232
+
233
+ b .WriteString ("Staged changes: " )
234
+ b .WriteRune ('\n' )
235
+ b .WriteString (stagedFilesStyle .Render (strings .Join (m .stagedFiles , "\n " )))
236
+
237
+ b .WriteRune ('\n' )
238
+
239
+ return b .String ()
166
240
}
167
241
168
- func getStagedFiles () ( []string , error ) {
169
- cmd := exec .Command ("git" , "diff" , "--cached " , "--name-only " )
170
- output , err := cmd .CombinedOutput ()
242
+ func getStagedFiles () []string {
243
+ cmd := exec .Command ("git" , "diff" , "--name-only " , "--cached " )
244
+ out , err := cmd .Output ()
171
245
if err != nil {
172
- return nil , fmt . Errorf ( "error getting staged files: %v" , err )
246
+ return [] string {}
173
247
}
174
248
175
- return strings .Split (strings . TrimSpace ( string (output )) , "\n " ), nil
249
+ return strings .Split (string (out ) , "\n " )
176
250
}
0 commit comments