package main import ( "errors" "fmt" "os" "strconv" "github.com/charmbracelet/huh" "golang.org/x/term" ) // pangolinTheme is the custom theme using brand colors var pangolinTheme = ThemePangolin() // isAccessibleMode checks if we should use accessible mode (simple prompts) // This is true for: non-TTY, TERM=dumb, or ACCESSIBLE env var set func isAccessibleMode() bool { // Check if stdin is not a terminal (piped input, CI, etc.) if !term.IsTerminal(int(os.Stdin.Fd())) { return true } // Check for dumb terminal if os.Getenv("TERM") == "dumb" { return true } // Check for explicit accessible mode request if os.Getenv("ACCESSIBLE") != "" { return true } return false } // handleAbort checks if the error is a user abort (Ctrl+C) and exits if so func handleAbort(err error) { if err != nil && errors.Is(err, huh.ErrUserAborted) { fmt.Println("\nInstallation cancelled.") os.Exit(0) } } // runField runs a single field with the Pangolin theme, handling accessible mode func runField(field huh.Field) error { if isAccessibleMode() { return field.RunAccessible(os.Stdout, os.Stdin) } form := huh.NewForm(huh.NewGroup(field)).WithTheme(pangolinTheme) return form.Run() } func readString(prompt string, defaultValue string) string { var value string title := prompt if defaultValue != "" { title = fmt.Sprintf("%s (default: %s)", prompt, defaultValue) } input := huh.NewInput(). Title(title). Value(&value) // If no default value, this field is required if defaultValue == "" { input = input.Validate(func(s string) error { if s == "" { return fmt.Errorf("this field is required") } return nil }) } err := runField(input) handleAbort(err) if value == "" { value = defaultValue } // Print the answer so it remains visible in terminal history (skip in accessible mode as it already shows) if !isAccessibleMode() { fmt.Printf("%s: %s\n", prompt, value) } return value } func readStringNoDefault(prompt string) string { var value string for { input := huh.NewInput(). Title(prompt). Value(&value). Validate(func(s string) error { if s == "" { return fmt.Errorf("this field is required") } return nil }) err := runField(input) handleAbort(err) if value != "" { // Print the answer so it remains visible in terminal history if !isAccessibleMode() { fmt.Printf("%s: %s\n", prompt, value) } return value } } } func readPassword(prompt string) string { var value string for { input := huh.NewInput(). Title(prompt). Value(&value). EchoMode(huh.EchoModePassword). Validate(func(s string) error { if s == "" { return fmt.Errorf("password is required") } return nil }) err := runField(input) handleAbort(err) if value != "" { // Print confirmation without revealing the password if !isAccessibleMode() { fmt.Printf("%s: %s\n", prompt, "********") } return value } } } func readBool(prompt string, defaultValue bool) bool { var value = defaultValue confirm := huh.NewConfirm(). Title(prompt). Value(&value). Affirmative("Yes"). Negative("No") err := runField(confirm) handleAbort(err) // Print the answer so it remains visible in terminal history if !isAccessibleMode() { answer := "No" if value { answer = "Yes" } fmt.Printf("%s: %s\n", prompt, answer) } return value } func readBoolNoDefault(prompt string) bool { var value bool confirm := huh.NewConfirm(). Title(prompt). Value(&value). Affirmative("Yes"). Negative("No") err := runField(confirm) handleAbort(err) // Print the answer so it remains visible in terminal history if !isAccessibleMode() { answer := "No" if value { answer = "Yes" } fmt.Printf("%s: %s\n", prompt, answer) } return value } func readInt(prompt string, defaultValue int) int { var value string title := fmt.Sprintf("%s (default: %d)", prompt, defaultValue) input := huh.NewInput(). Title(title). Value(&value). Validate(func(s string) error { if s == "" { return nil } _, err := strconv.Atoi(s) if err != nil { return fmt.Errorf("please enter a valid number") } return nil }) err := runField(input) handleAbort(err) if value == "" { // Print the answer so it remains visible in terminal history if !isAccessibleMode() { fmt.Printf("%s: %d\n", prompt, defaultValue) } return defaultValue } result, err := strconv.Atoi(value) if err != nil { if !isAccessibleMode() { fmt.Printf("%s: %d\n", prompt, defaultValue) } return defaultValue } // Print the answer so it remains visible in terminal history if !isAccessibleMode() { fmt.Printf("%s: %d\n", prompt, result) } return result }