diff --git a/cmd/submit.go b/cmd/submit.go index 340d43e..2beb656 100644 --- a/cmd/submit.go +++ b/cmd/submit.go @@ -303,7 +303,7 @@ func doSubmitPRs(g *git.Git, cfg *config.Config, root *tree.Node, branches []*tr prNum, adopted, err := createPRForBranch(g, ghClient, cfg, root, b.Name, parent, trunk, remoteBranches, s) switch { case errors.Is(err, ErrPRSkipped): - fmt.Printf("Skipped PR for %s %s\n", s.Branch(b.Name), s.Muted("(user pressed ESC)")) + fmt.Printf("Skipped PR for %s %s\n", s.Branch(b.Name), s.Muted("(skipped)")) case err != nil: fmt.Printf("%s failed to create PR for %s: %v\n", s.WarningIcon(), s.Branch(b.Name), err) case adopted: diff --git a/go.mod b/go.mod index a2145ed..ea22fdd 100644 --- a/go.mod +++ b/go.mod @@ -3,6 +3,7 @@ module github.com/boneskull/gh-stack go 1.25.0 require ( + github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 github.com/charmbracelet/huh v1.0.0 github.com/cli/go-gh/v2 v2.13.0 github.com/cli/safeexec v1.0.1 @@ -15,7 +16,6 @@ require ( github.com/atotto/clipboard v0.1.4 // indirect github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect github.com/catppuccin/go v0.3.0 // indirect - github.com/charmbracelet/bubbles v0.21.1-0.20250623103423-23b8fd6302d7 // indirect github.com/charmbracelet/bubbletea v1.3.6 // indirect github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect github.com/charmbracelet/lipgloss v1.1.1-0.20250319133953-166f707985bc // indirect diff --git a/internal/prompt/prompt.go b/internal/prompt/prompt.go index ae6fd69..97cb8f4 100644 --- a/internal/prompt/prompt.go +++ b/internal/prompt/prompt.go @@ -8,6 +8,7 @@ import ( "os/exec" "strings" + "github.com/charmbracelet/bubbles/key" "github.com/charmbracelet/huh" "github.com/cli/go-gh/v2/pkg/prompter" "github.com/cli/go-gh/v2/pkg/term" @@ -51,11 +52,9 @@ func Input(prompt, defaultValue string) (string, error) { } // InputWithSkip prompts the user for a single line of input with a default value, -// allowing the user to skip by pressing ESC. Returns (value, skipped, error). +// allowing the user to skip by pressing ESC or Ctrl+C. Returns (value, skipped, error). // -// When the user presses ESC (or otherwise aborts via huh.ErrUserAborted), -// skipped is true and err is nil. Ctrl+C will typically terminate the process -// via SIGINT rather than returning here. +// When the user presses ESC or Ctrl+C, skipped is true and err is nil. // // If not in an interactive terminal, the default is returned without prompting. func InputWithSkip(title, description, defaultValue string) (string, bool, error) { @@ -65,14 +64,26 @@ func InputWithSkip(title, description, defaultValue string) (string, bool, error value := defaultValue - err := huh.NewInput(). + // Build a keymap that treats both ctrl+c and esc as "quit/skip". + // huh's default keymap only binds ctrl+c to Quit, leaving esc unhandled + // at the form level, so ESC would otherwise do nothing. + km := huh.NewDefaultKeyMap() + km.Quit = key.NewBinding( + key.WithKeys("ctrl+c", "esc"), + key.WithHelp("esc/ctrl+c", "skip"), + ) + + input := huh.NewInput(). Title(title). Description(description). - Value(&value). + Value(&value) + + err := huh.NewForm(huh.NewGroup(input)). + WithShowHelp(false). + WithKeyMap(km). Run() if errors.Is(err, huh.ErrUserAborted) { - // User pressed ESC to skip return "", true, nil } if err != nil {