package choose import ( "errors" "fmt" "os" "strings" "github.com/charmbracelet/huh" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/x/ansi" "github.com/charmbracelet/x/term" "github.com/charmbracelet/gum/internal/stdin" ) const widthBuffer = 2 // Run provides a shell script interface for choosing between different through // options. func (o Options) Run() error { if len(o.Options) <= 0 { input, _ := stdin.Read() if input == "" { return errors.New("no options provided, see `gum choose --help`") } o.Options = strings.Split(input, "\n") } if o.SelectIfOne && len(o.Options) == 1 { fmt.Println(o.Options[0]) return nil } theme := huh.ThemeCharm() options := huh.NewOptions(o.Options...) theme.Focused.Base = lipgloss.NewStyle() theme.Focused.Title = o.HeaderStyle.ToLipgloss() theme.Focused.SelectSelector = o.CursorStyle.ToLipgloss().SetString(o.Cursor) theme.Focused.MultiSelectSelector = o.CursorStyle.ToLipgloss().SetString(o.Cursor) theme.Focused.SelectedOption = o.SelectedItemStyle.ToLipgloss() theme.Focused.UnselectedOption = o.ItemStyle.ToLipgloss() theme.Focused.SelectedPrefix = o.SelectedItemStyle.ToLipgloss().SetString(o.SelectedPrefix) theme.Focused.UnselectedPrefix = o.ItemStyle.ToLipgloss().SetString(o.UnselectedPrefix) for _, s := range o.Selected { for i, opt := range options { if s == opt.Key || s == opt.Value { options[i] = opt.Selected(true) } } } width := max(widest(o.Options)+ max(lipgloss.Width(o.SelectedPrefix)+lipgloss.Width(o.UnselectedPrefix))+ lipgloss.Width(o.Cursor)+1, lipgloss.Width(o.Header)+widthBuffer) if o.NoLimit { o.Limit = 0 } if o.Limit > 1 || o.NoLimit { var choices []string field := huh.NewMultiSelect[string](). Options(options...). Title(o.Header). Height(o.Height). Limit(o.Limit). Value(&choices) form := huh.NewForm(huh.NewGroup(field)) err := form. WithWidth(width). WithShowHelp(o.ShowHelp). WithTheme(theme). Run() if err != nil { return err } if len(choices) > 0 { s := strings.Join(choices, "\n") ansiprint(s) } return nil } var choice string err := huh.NewForm( huh.NewGroup( huh.NewSelect[string](). Options(options...). Title(o.Header). Height(o.Height). Value(&choice), ), ). WithWidth(width). WithTheme(theme). WithShowHelp(o.ShowHelp). Run() if err != nil { return err } if term.IsTerminal(os.Stdout.Fd()) { fmt.Println(choice) } else { fmt.Print(ansi.Strip(choice)) } return nil } func widest(options []string) int { var max int for _, o := range options { w := lipgloss.Width(o) if w > max { max = w } } return max } func ansiprint(s string) { if term.IsTerminal(os.Stdout.Fd()) { fmt.Println(s) } else { fmt.Print(ansi.Strip(s)) } }