Files
harness-sync/internal/ui/prompts.go
T
lukaszraczylo 5f1d9c1b72 fix(deps): migrate huh to charm.land/huh/v2
The module path moved to charm.land/huh/v2. Update the import in the ui
prompts and refresh go.mod and go.sum. No API changes were needed.
2026-10-04 10:12:32 +01:00

72 lines
1.8 KiB
Go

// Package ui provides small interactive prompts and their non-interactive
// equivalents for tests.
package ui
import (
"fmt"
"charm.land/huh/v2"
)
type msOpts struct {
nonInteractive []string
interactive bool
}
// Option configures MultiSelect behaviour.
type Option func(*msOpts)
// WithNonInteractive bypasses the TUI entirely and returns the given choices.
// Test code uses this to make MultiSelect deterministic.
func WithNonInteractive(choices []string) Option {
return func(o *msOpts) {
o.nonInteractive = choices
o.interactive = false
}
}
// MultiSelect prompts the user (or returns the pre-set non-interactive value).
// Choices passed via WithNonInteractive must all appear in `choices`.
func MultiSelect(title string, choices []string, opts ...Option) ([]string, error) {
o := &msOpts{interactive: true}
for _, fn := range opts {
fn(o)
}
if !o.interactive {
valid := map[string]bool{}
for _, c := range choices {
valid[c] = true
}
for _, c := range o.nonInteractive {
if !valid[c] {
return nil, fmt.Errorf("choice %q not in %v", c, choices)
}
}
return o.nonInteractive, nil
}
// Pre-select every choice. Users deselect what they don't want; hitting
// enter without toggling thus accepts all (the common case) instead of
// silently producing an empty result.
picked := make([]string, len(choices))
copy(picked, choices)
opts2 := make([]huh.Option[string], 0, len(choices))
for _, c := range choices {
opts2 = append(opts2, huh.NewOption(c, c).Selected(true))
}
form := huh.NewForm(huh.NewGroup(
huh.NewMultiSelect[string]().
Title(title).
Description("space to toggle · enter to confirm · all pre-selected").
Options(opts2...).
Value(&picked),
))
if err := form.Run(); err != nil {
return nil, err
}
if len(picked) == 0 {
return nil, fmt.Errorf("no items selected")
}
return picked, nil
}