Skip to content

Commit 79b4471

Browse files
committed
Add type bootstrapWizardInput and related types.
Replace text fields with checkboxes for Boolean values in the bootstrap wizard. Add local variables for input lookup. Removed unnecessary copying of styles.
1 parent 3b1059d commit 79b4471

File tree

2 files changed

+167
-72
lines changed

2 files changed

+167
-72
lines changed

pkg/run/bootstrap/bootstrap_wizard.go

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -300,9 +300,7 @@ const (
300300
branchPrefix = "refs/heads/"
301301
)
302302

303-
type (
304-
GitProvider int32
305-
)
303+
type GitProvider int32
306304

307305
const (
308306
GitProviderUnknown GitProvider = 0

pkg/run/bootstrap/bootstrap_wizard_ui.go

Lines changed: 166 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package bootstrap
22

33
import (
44
"fmt"
5+
"strconv"
56
"strings"
67

78
"github.com/charmbracelet/bubbles/table"
@@ -19,7 +20,38 @@ type preWizardModel struct {
1920
msgChan chan GitProvider
2021
}
2122

22-
const flagSeparator = " - "
23+
type wizardModel struct {
24+
windowIsReady bool
25+
viewport viewport.Model
26+
inputs []*bootstrapWizardInput
27+
msgChan chan BootstrapCmdOptions
28+
cursorMode textinput.CursorMode
29+
focusIndex int
30+
errorMsg string
31+
}
32+
33+
type checkbox struct {
34+
checked bool
35+
}
36+
37+
type inputType int32
38+
39+
const (
40+
inputTypeTextInput inputType = 0
41+
inputTypeCheckbox inputType = 1
42+
)
43+
44+
type bootstrapWizardInput struct {
45+
inputType inputType
46+
prompt string
47+
textInput textinput.Model
48+
checkboxInput *checkbox
49+
}
50+
51+
const (
52+
flagSeparator = " - "
53+
buttonText = "Submit"
54+
)
2355

2456
// UI styling
2557
var (
@@ -37,8 +69,8 @@ var (
3769
helpStyle = blurredStyle.Copy()
3870
cursorModeHelpStyle = lipgloss.NewStyle().Foreground(lipgloss.Color("244"))
3971

40-
focusedButton = focusedStyle.Copy().Render("[ Submit ]")
41-
blurredButton = fmt.Sprintf("[ %s ]", blurredStyle.Render("Submit"))
72+
focusedButton = focusedStyle.Render(fmt.Sprintf("[ %s ]", buttonText))
73+
blurredButton = fmt.Sprintf("[ %s ]", blurredStyle.Render(buttonText))
4274
)
4375

4476
func makeViewport(width int, height int, content string) viewport.Model {
@@ -162,61 +194,94 @@ func (m preWizardModel) getContent() string {
162194
) + m.textInput.View()
163195
}
164196

165-
type wizardModel struct {
166-
windowIsReady bool
167-
viewport viewport.Model
168-
textInputs []textinput.Model
169-
prompts []string
170-
msgChan chan BootstrapCmdOptions
171-
cursorMode textinput.CursorMode
172-
focusIndex int
173-
errorMsg string
174-
}
175197

176-
func makeTextInput(task *BootstrapWizardTask, isFocused bool) textinput.Model {
177-
ti := textinput.New()
178-
ti.CursorStyle = cursorStyle
179-
ti.CharLimit = 100
180198

181-
ti.SetValue(task.flagValue)
182-
ti.Placeholder = task.flagDescription
199+
func makeInput(task *BootstrapWizardTask, isFocused bool) *bootstrapWizardInput {
200+
var inputType inputType
183201

184-
if task.isPassword {
185-
ti.EchoMode = textinput.EchoPassword
202+
if task.isBoolean {
203+
inputType = inputTypeCheckbox
204+
} else {
205+
inputType = inputTypeTextInput
186206
}
187207

188-
if isFocused {
189-
ti.Focus()
190-
ti.PromptStyle = focusedStyle
191-
ti.TextStyle = focusedStyle
208+
prompt := task.flagName+flagSeparator+task.flagDescription
209+
210+
ti := textinput.Model{}
211+
212+
var cb *checkbox
213+
214+
if inputType == inputTypeCheckbox {
215+
cb = &checkbox{
216+
checked: task.flagValue == "true",
217+
}
218+
} else {
219+
ti = textinput.New()
220+
ti.CursorStyle = cursorStyle
221+
ti.CharLimit = 100
222+
223+
ti.SetValue(task.flagValue)
224+
ti.Placeholder = task.flagDescription
225+
226+
if task.isPassword {
227+
ti.EchoMode = textinput.EchoPassword
228+
}
229+
230+
if isFocused {
231+
ti.Focus()
232+
ti.PromptStyle = focusedStyle
233+
ti.TextStyle = focusedStyle
234+
}
192235
}
193236

194-
return ti
237+
return &bootstrapWizardInput{
238+
inputType: inputType,
239+
prompt: prompt,
240+
textInput: ti,
241+
checkboxInput: cb,
242+
}
195243
}
196244

197-
func initialWizardModel(tasks []*BootstrapWizardTask, msgChan chan BootstrapCmdOptions) wizardModel {
198-
numInputs := len(tasks)
245+
func (input *bootstrapWizardInput) getView(isFocused bool) string {
246+
if input.inputType == inputTypeTextInput {
247+
return input.textInput.View()
248+
}
199249

200-
inputs := make([]textinput.Model, numInputs)
250+
var checkmark string
201251

202-
for i := range inputs {
203-
task := tasks[i]
252+
if input.checkboxInput.checked {
253+
checkmark = "x"
254+
255+
if isFocused {
256+
checkmark = focusedStyle.Render(checkmark)
257+
}
258+
} else {
259+
checkmark = blurredStyle.Render("_")
260+
}
204261

205-
ti := makeTextInput(task, i == 0)
262+
open := "["
263+
close := "]"
206264

207-
inputs[i] = ti
265+
if isFocused {
266+
open = focusedStyle.Render(open)
267+
close = focusedStyle.Render(close)
208268
}
209269

210-
prompts := []string{}
270+
return fmt.Sprintf("%s%s%s", open, checkmark, close)
271+
}
272+
273+
func initialWizardModel(tasks []*BootstrapWizardTask, msgChan chan BootstrapCmdOptions) wizardModel {
274+
numInputs := len(tasks)
275+
276+
inputs := make([]*bootstrapWizardInput, numInputs)
211277

212-
for _, task := range tasks {
213-
prompts = append(prompts, task.flagName+flagSeparator+task.flagDescription)
278+
for i := range inputs {
279+
inputs[i] = makeInput(tasks[i], i == 0)
214280
}
215281

216282
return wizardModel{
217-
textInputs: inputs,
283+
inputs: inputs,
218284
errorMsg: "",
219-
prompts: prompts,
220285
msgChan: msgChan,
221286
}
222287
}
@@ -241,34 +306,43 @@ func (m wizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
241306
m.cursorMode = textinput.CursorBlink
242307
}
243308

244-
cmdsTextInputs := make([]tea.Cmd, len(m.textInputs))
309+
cmdsTextInputs := make([]tea.Cmd, len(m.inputs))
245310

246-
for i := range m.textInputs {
247-
cmdsTextInputs[i] = m.textInputs[i].SetCursorMode(m.cursorMode)
311+
for i := range m.inputs {
312+
input := m.inputs[i]
313+
if input.inputType == inputTypeTextInput {
314+
cmdsTextInputs[i] = input.textInput.SetCursorMode(m.cursorMode)
315+
}
248316
}
249317

250318
cmds = append(cmds, cmdsTextInputs...)
251319
case tea.KeyTab, tea.KeyShiftTab, tea.KeyEnter:
252320
t := msg.Type
253321

254-
if t == tea.KeyEnter && m.focusIndex == len(m.textInputs) {
322+
if t == tea.KeyEnter && m.focusIndex == len(m.inputs) {
255323
options := make(BootstrapCmdOptions)
256324

257-
for i, input := range m.textInputs {
258-
prompt := m.prompts[i]
325+
for _, input := range m.inputs {
326+
prompt := input.prompt
259327

260-
value := strings.TrimSpace(input.Value())
328+
var value string
261329

262-
if value == "" {
263-
m.errorMsg = "Missing value in " + input.Placeholder
330+
if input.inputType == inputTypeTextInput {
331+
value = strings.TrimSpace(input.textInput.Value())
264332

265-
m.viewport.SetContent(m.getContent())
333+
if value == "" {
334+
m.errorMsg = "Missing value in " + input.textInput.Placeholder
266335

267-
var cmdViewport tea.Cmd
336+
m.viewport.SetContent(m.getContent())
268337

269-
m.viewport, cmdViewport = m.viewport.Update(msg)
338+
var cmdViewport tea.Cmd
270339

271-
return m, cmdViewport
340+
m.viewport, cmdViewport = m.viewport.Update(msg)
341+
342+
return m, cmdViewport
343+
}
344+
} else {
345+
value = strconv.FormatBool(input.checkboxInput.checked)
272346
}
273347

274348
options[prompt[:strings.Index(prompt, flagSeparator)]] = value
@@ -285,29 +359,43 @@ func (m wizardModel) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
285359
m.focusIndex++
286360
}
287361

288-
if m.focusIndex > len(m.textInputs) {
362+
if m.focusIndex > len(m.inputs) {
289363
m.focusIndex = 0
290364
} else if m.focusIndex < 0 {
291-
m.focusIndex = len(m.textInputs)
365+
m.focusIndex = len(m.inputs)
292366
}
293367

294-
cmdsTextInputs := make([]tea.Cmd, len(m.textInputs))
368+
cmdsTextInputs := []tea.Cmd{}
369+
370+
for i := 0; i <= len(m.inputs)-1; i++ {
371+
input := m.inputs[i]
372+
373+
if input.inputType == inputTypeCheckbox {
374+
continue
375+
}
295376

296-
for i := 0; i <= len(m.textInputs)-1; i++ {
297377
if i == m.focusIndex {
298-
cmdsTextInputs[i] = m.textInputs[i].Focus()
299-
m.textInputs[i].PromptStyle = focusedStyle
300-
m.textInputs[i].TextStyle = focusedStyle
378+
cmdsTextInputs = append(cmdsTextInputs, input.textInput.Focus())
379+
input.textInput.PromptStyle = focusedStyle
380+
input.textInput.TextStyle = focusedStyle
301381

302382
continue
303383
}
304384

305-
m.textInputs[i].Blur()
306-
m.textInputs[i].PromptStyle = noStyle
307-
m.textInputs[i].TextStyle = noStyle
385+
input.textInput.Blur()
386+
input.textInput.PromptStyle = noStyle
387+
input.textInput.TextStyle = noStyle
308388
}
309389

310390
cmds = append(cmds, cmdsTextInputs...)
391+
case tea.KeySpace:
392+
input := m.inputs[m.focusIndex]
393+
394+
if m.focusIndex == len(m.inputs) || input.inputType != inputTypeCheckbox {
395+
break
396+
}
397+
398+
input.checkboxInput.checked = !input.checkboxInput.checked
311399
}
312400
case tea.WindowSizeMsg:
313401
if !m.windowIsReady {
@@ -338,12 +426,18 @@ func (m wizardModel) View() string {
338426
}
339427

340428
func (m *wizardModel) updateInputs(msg tea.Msg) tea.Cmd {
341-
cmds := make([]tea.Cmd, len(m.textInputs))
429+
cmds := make([]tea.Cmd, len(m.inputs))
342430

343431
// Only text inputs with Focus() set will respond, so it's safe to simply
344432
// update all of them here without any further logic.
345-
for i := range m.textInputs {
346-
m.textInputs[i], cmds[i] = m.textInputs[i].Update(msg)
433+
for i := range m.inputs {
434+
input := m.inputs[i]
435+
436+
if input.inputType == inputTypeCheckbox {
437+
continue
438+
}
439+
440+
input.textInput, cmds[i] = input.textInput.Update(msg)
347441
}
348442

349443
return tea.Batch(cmds...)
@@ -354,21 +448,24 @@ func (m wizardModel) getContent() string {
354448

355449
b.WriteString("Please enter the following values" + "\n" +
356450
"(Tab and Shift+Tab to move input selection," + "\n" +
451+
"(Space to toggle the currently focused checkbox," + "\n" +
357452
"Enter to move to the next input or submit the form, " + "\n" +
358453
"up and down arrows to scroll the view, Ctrl+C twice to quit):" + "\n\n\n")
359454

360-
for i := range m.textInputs {
361-
b.WriteString(m.prompts[i])
455+
for i := range m.inputs {
456+
input := m.inputs[i]
457+
458+
b.WriteString(input.prompt)
362459
b.WriteRune('\n')
363-
b.WriteString(m.textInputs[i].View())
460+
b.WriteString(input.getView(i == m.focusIndex))
364461

365-
if i < len(m.textInputs)-1 {
462+
if i < len(m.inputs)-1 {
366463
b.WriteRune('\n')
367464
}
368465
}
369466

370467
button := &blurredButton
371-
if m.focusIndex == len(m.textInputs) {
468+
if m.focusIndex == len(m.inputs) {
372469
button = &focusedButton
373470
}
374471

0 commit comments

Comments
 (0)