// picker is a runnable demonstration, not a deployment tool. package main import ( tea "charm.land/bubbletea/v2" "flag" "fmt" "github.com/charmbracelet/x/term" "os" ) var servers = []item{ {"dev-eu", "development", "eu · ready"}, {"stage-eu", "staging", "eu · ready"}, {"prod-eu", "production", "eu · healthy"}, {"prod-jp", "東京 production", "jp · healthy"}, {"queue-eu", "queue worker", "eu · busy"}, } func run(args []string) (int, error) { flags := flag.NewFlagSet("picker", flag.ContinueOnError) id := flags.String("id", "", "select an exact ID without opening a terminal") list := flags.Bool("list", false, "print IDs and labels without opening a terminal") if err := flags.Parse(args); err != nil { if err == flag.ErrHelp { return 0, nil } return 2, err } if flags.NArg() != 0 { return 2, fmt.Errorf("unexpected arguments") } if *list && *id != "" { return 2, fmt.Errorf("choose either --list or --id") } if *list { for _, row := range servers { if _, err := fmt.Fprintf(os.Stdout, "%s\t%s\n", row.ID, row.Label); err != nil { return 2, err } } return 0, nil } if *id != "" { for _, row := range servers { if row.ID == *id { return finish(os.Stdout, outcome{Accepted: true, ID: row.ID}) } } return 2, fmt.Errorf("unknown ID %q", *id) } // stdout may be a pipe. Only stdin and the UI's stderr must be terminals. // Do not open a hidden /dev/tty in unattended jobs. if !term.IsTerminal(os.Stdin.Fd()) || !term.IsTerminal(os.Stderr.Fd()) { return 2, fmt.Errorf("interactive mode needs terminal stdin and stderr; use --list or --id") } final, err := tea.NewProgram(newModel(servers), tea.WithInput(os.Stdin), tea.WithOutput(os.Stderr)).Run() if err != nil { return 2, fmt.Errorf("picker: %w", err) } m, ok := final.(*model) if !ok { return 2, fmt.Errorf("unexpected final model %T", final) } return finish(os.Stdout, m.result) } func main() { code, err := run(os.Args[1:]) if err != nil { fmt.Fprintln(os.Stderr, err) } os.Exit(code) // run's defers and Bubble Tea cleanup have already completed. }