mirror of
https://github.com/tiennm99/goclaw.git
synced 2026-06-10 06:10:46 +00:00
148 lines
3.5 KiB
Go
148 lines
3.5 KiB
Go
package cmd
|
|
|
|
import (
|
|
"github.com/charmbracelet/huh"
|
|
)
|
|
|
|
// runWithHelp wraps a huh field in a Form with help hints visible at the bottom.
|
|
func runWithHelp(fields ...huh.Field) error {
|
|
return huh.NewForm(huh.NewGroup(fields...)).WithShowHelp(true).Run()
|
|
}
|
|
|
|
// promptString prompts for a text input using huh TUI.
|
|
// If defaultVal is non-empty it is shown as placeholder; pressing Enter returns it.
|
|
func promptString(title, description, defaultVal string) (string, error) {
|
|
var value string
|
|
inp := huh.NewInput().
|
|
Title(title).
|
|
Value(&value)
|
|
|
|
if description != "" {
|
|
inp = inp.Description(description)
|
|
}
|
|
if defaultVal != "" {
|
|
inp = inp.Placeholder(defaultVal)
|
|
}
|
|
|
|
if err := runWithHelp(inp); err != nil {
|
|
return "", err
|
|
}
|
|
if value == "" {
|
|
return defaultVal, nil
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// promptPassword prompts for a password input (hidden characters) using huh TUI.
|
|
func promptPassword(title, description string) (string, error) {
|
|
var value string
|
|
inp := huh.NewInput().
|
|
Title(title).
|
|
EchoMode(huh.EchoModePassword).
|
|
Value(&value)
|
|
|
|
if description != "" {
|
|
inp = inp.Description(description)
|
|
}
|
|
|
|
if err := runWithHelp(inp); err != nil {
|
|
return "", err
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// filterThreshold: enable type-to-filter only when there are more than this many options.
|
|
const filterThreshold = 5
|
|
|
|
const scrollableThreshold = 15 // enable scrollbars when there are more than this many options
|
|
|
|
// promptSelect shows a single-select list using huh TUI.
|
|
// Returns the value of the selected option.
|
|
func promptSelect[T comparable](title string, options []SelectOption[T], defaultIdx int) (T, error) {
|
|
var value T
|
|
|
|
huhOpts := make([]huh.Option[T], len(options))
|
|
for i, opt := range options {
|
|
huhOpts[i] = huh.NewOption(opt.Label, opt.Value)
|
|
}
|
|
if defaultIdx >= 0 && defaultIdx < len(options) {
|
|
huhOpts[defaultIdx] = huhOpts[defaultIdx].Selected(true)
|
|
}
|
|
|
|
sel := huh.NewSelect[T]().
|
|
Options(huhOpts...).
|
|
Value(&value)
|
|
|
|
if len(options) > scrollableThreshold {
|
|
sel.Height(scrollableThreshold) // show scrollbar if too many options
|
|
title += " (scroll for more)"
|
|
}
|
|
sel.Title(title)
|
|
|
|
if err := runWithHelp(sel); err != nil {
|
|
var zero T
|
|
return zero, err
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// promptMultiSelect shows a multi-select list using huh TUI.
|
|
// Returns the values of all selected options.
|
|
func promptMultiSelect[T comparable](title, description string, options []SelectOption[T], preselected []T) ([]T, error) {
|
|
var values []T
|
|
|
|
// Build pre-selected set for fast lookup
|
|
preSet := make(map[T]bool, len(preselected))
|
|
for _, v := range preselected {
|
|
preSet[v] = true
|
|
}
|
|
|
|
huhOpts := make([]huh.Option[T], len(options))
|
|
for i, opt := range options {
|
|
o := huh.NewOption(opt.Label, opt.Value)
|
|
if preSet[opt.Value] {
|
|
o = o.Selected(true)
|
|
}
|
|
huhOpts[i] = o
|
|
}
|
|
|
|
ms := huh.NewMultiSelect[T]().
|
|
Title(title).
|
|
Options(huhOpts...).
|
|
Value(&values)
|
|
|
|
if description != "" {
|
|
ms = ms.Description(description)
|
|
}
|
|
if len(options) > filterThreshold {
|
|
ms = ms.Filtering(true)
|
|
}
|
|
|
|
if err := runWithHelp(ms); err != nil {
|
|
return nil, err
|
|
}
|
|
return values, nil
|
|
}
|
|
|
|
// promptConfirm asks a yes/no question using huh TUI. Returns true for yes.
|
|
func promptConfirm(title string, defaultYes bool) (bool, error) {
|
|
value := defaultYes
|
|
|
|
c := huh.NewConfirm().
|
|
Title(title).
|
|
Affirmative("Yes").
|
|
Negative("No").
|
|
Value(&value)
|
|
|
|
if err := runWithHelp(c); err != nil {
|
|
return false, err
|
|
}
|
|
return value, nil
|
|
}
|
|
|
|
// SelectOption represents a single option in a select prompt.
|
|
type SelectOption[T any] struct {
|
|
Label string
|
|
Value T
|
|
}
|