package termui import ( "bufio" "fmt" "os" "strings" "syscall" "unsafe" "hub.gaomia.site/titor/YunShu/pkg/style" ) const ( stdInputHandle = ^uintptr(9) stdOutputHandle = ^uintptr(10) enableProcessed = 0x0001 enableLineInput = 0x0002 enableEchoInput = 0x0004 enableVtProcessing = 0x0004 ) var ( kernel32 = syscall.NewLazyDLL("kernel32.dll") procGetStdHandle = kernel32.NewProc("GetStdHandle") procGetConsoleMode = kernel32.NewProc("GetConsoleMode") procSetConsoleMode = kernel32.NewProc("SetConsoleMode") ) func ensureLineMode() { h, _, _ := procGetStdHandle.Call(stdInputHandle) if h == 0 || h == uintptr(syscall.InvalidHandle) { return } var mode uint32 ret, _, _ := procGetConsoleMode.Call(h, uintptr(unsafe.Pointer(&mode))) if ret == 0 { return } need := uint32(enableProcessed | enableLineInput | enableEchoInput) if mode&need != need { procSetConsoleMode.Call(h, uintptr(need)) } } type InputConfig struct { Label string Help string Default string Required bool Validator Validator } type InputOption func(*InputConfig) func WithDefault(v string) InputOption { return func(c *InputConfig) { c.Default = v } } func WithHelp(v string) InputOption { return func(c *InputConfig) { c.Help = v } } func WithRequired(v bool) InputOption { return func(c *InputConfig) { c.Required = v } } func WithValidator(v Validator) InputOption { return func(c *InputConfig) { c.Validator = v } } func applyOpts(opts []InputOption) InputConfig { c := InputConfig{} for _, o := range opts { o(&c) } return c } func printLabel(label string, required bool) { s := style.New().Bold().Render(label) if required { s += "(必填)" } fmt.Println(s) } func ReadLine() string { ensureLineMode() r := bufio.NewReader(os.Stdin) s, err := r.ReadString('\n') if err != nil { return "" } return strings.TrimRight(s, "\r\n") } func TextInput(label string, opts ...InputOption) string { cfg := applyOpts(opts) printLabel(label, cfg.Required) for { fmt.Print(style.Cyan.Render("? ")) line := ReadLine() if line == "" { line = cfg.Default } var err error if cfg.Required && line == "" { err = fmt.Errorf("不能为空") } else if cfg.Validator != nil { err = cfg.Validator(line) } if err != nil { fmt.Println(style.Red.Render("⚠ " + err.Error())) continue } fmt.Println(style.Green.Render("✔ " + line)) return line } } func PasswordInput(label string, opts ...InputOption) string { cfg := applyOpts(opts) printLabel(label, cfg.Required) for { fmt.Print(style.Cyan.Render("? ")) line := ReadLine() fmt.Print("\033[A\r\033[K") if line == "" { line = cfg.Default } var err error if cfg.Required && line == "" { err = fmt.Errorf("不能为空") } else if cfg.Validator != nil { err = cfg.Validator(line) } if err != nil { fmt.Println(style.Red.Render("⚠ " + err.Error())) continue } masked := strings.Repeat("*", len(line)) fmt.Println(style.Green.Render("✔ " + masked)) return line } } func Confirm(label string, defaultValue bool) bool { hint := "Y/n" if !defaultValue { hint = "y/N" } fmt.Print(style.Cyan.Render("?"), label, "(", hint, ")") line := ReadLine() line = strings.ToLower(line) fmt.Print("\033[A\r\033[K") ok := defaultValue if line == "y" || line == "yes" { ok = true } else if line == "n" || line == "no" { ok = false } else if line != "" { ok = false } if ok { fmt.Println(style.Green.Render("✔ " + label)) } else { fmt.Println(style.Red.Render("✘ " + label)) } return ok }