diff --git a/.claude/settings.json b/.claude/settings.json index 7cb515c4..29ea2bbd 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -1,10 +1,6 @@ { "permissions": { "allow": [ - "Bash(go build:*)", - "Bash(go mod tidy:*)", - "Bash(go test:*)", - "Bash(gofmt:*)", "Bash(gh issue view:*)", "Bash(gh label list:*)", "Bash(gh pr checks:*)", @@ -15,20 +11,25 @@ "Bash(gh pr view:*)", "Bash(gh search code:*)", "Bash(git diff:*)", + "Bash(git fetch:*)", "Bash(git grep:*)", "Bash(git log:*)", "Bash(git status:*)", + "Bash(go build:*)", "Bash(go mod graph:*)", "Bash(go mod tidy:*)", + "Bash(go mod tidy:*)", + "Bash(go test:*)", + "Bash(gofmt:*)", "Bash(grep:*)", "Bash(ls:*)", - "Bash(make build:*)", "Bash(make build-ci:*)", + "Bash(make build:*)", "Bash(make lint:*)", "Bash(make test:*)", "Bash(tree:*)", - "WebFetch(domain:github.com)", - "WebFetch(domain:docs.slack.dev)" + "WebFetch(domain:docs.slack.dev)", + "WebFetch(domain:github.com)" ] }, "enabledPlugins": { diff --git a/internal/iostreams/charm.go b/internal/iostreams/forms.go similarity index 72% rename from internal/iostreams/charm.go rename to internal/iostreams/forms.go index 1a74ecf1..d5e43f6c 100644 --- a/internal/iostreams/charm.go +++ b/internal/iostreams/forms.go @@ -14,8 +14,9 @@ package iostreams -// Charm-based prompt implementations using the huh library. -// These are used when the "huh" experiment is enabled. +// Interactive form-based prompt implementations with Charm's Huh package. +// +// Reference: https://github.com/charmbracelet/huh?tab=readme-ov-file#huh import ( "context" @@ -28,7 +29,7 @@ import ( "github.com/slackapi/slack-cli/internal/style" ) -// newForm wraps a field in a huh form, applying the Slack theme when the lipgloss experiment is enabled. +// newForm wraps a field in an interactive form with optional Slack theming. func newForm(io *IOStreams, field huh.Field) *huh.Form { form := huh.NewForm(huh.NewGroup(field)) if io != nil && io.config.WithExperimentOn(experiment.Lipgloss) { @@ -37,7 +38,7 @@ func newForm(io *IOStreams, field huh.Field) *huh.Form { return form } -// buildInputForm constructs a huh form for text input prompts. +// buildInputForm constructs an interactive form for text input prompts. func buildInputForm(io *IOStreams, message string, cfg InputPromptConfig, input *string) *huh.Form { field := huh.NewInput(). Title(message). @@ -50,8 +51,8 @@ func buildInputForm(io *IOStreams, message string, cfg InputPromptConfig, input return newForm(io, field) } -// charmInputPrompt prompts for text input using a charm huh form -func charmInputPrompt(io *IOStreams, _ context.Context, message string, cfg InputPromptConfig) (string, error) { +// inputForm interactively prompts for text input. +func inputForm(io *IOStreams, _ context.Context, message string, cfg InputPromptConfig) (string, error) { var input string err := buildInputForm(io, message, cfg, &input).Run() if errors.Is(err, huh.ErrUserAborted) { @@ -62,7 +63,7 @@ func charmInputPrompt(io *IOStreams, _ context.Context, message string, cfg Inpu return input, nil } -// buildConfirmForm constructs a huh form for yes/no confirmation prompts. +// buildConfirmForm constructs an interactive form for yes/no confirmation prompts. func buildConfirmForm(io *IOStreams, message string, choice *bool) *huh.Form { field := huh.NewConfirm(). Title(message). @@ -70,8 +71,8 @@ func buildConfirmForm(io *IOStreams, message string, choice *bool) *huh.Form { return newForm(io, field) } -// charmConfirmPrompt prompts for a yes/no confirmation using a charm huh form -func charmConfirmPrompt(io *IOStreams, _ context.Context, message string, defaultValue bool) (bool, error) { +// confirmForm interactively prompts for a yes/no confirmation. +func confirmForm(io *IOStreams, _ context.Context, message string, defaultValue bool) (bool, error) { var choice = defaultValue err := buildConfirmForm(io, message, &choice).Run() if errors.Is(err, huh.ErrUserAborted) { @@ -82,7 +83,7 @@ func charmConfirmPrompt(io *IOStreams, _ context.Context, message string, defaul return choice, nil } -// buildSelectForm constructs a huh form for single-selection prompts. +// buildSelectForm constructs an interactive form for single-selection prompts. func buildSelectForm(io *IOStreams, msg string, options []string, cfg SelectPromptConfig, selected *string) *huh.Form { var opts []huh.Option[string] for _, opt := range options { @@ -104,8 +105,8 @@ func buildSelectForm(io *IOStreams, msg string, options []string, cfg SelectProm return newForm(io, field) } -// charmSelectPrompt prompts the user to select one option using a charm huh form -func charmSelectPrompt(io *IOStreams, _ context.Context, msg string, options []string, cfg SelectPromptConfig) (SelectPromptResponse, error) { +// selectForm interactively prompts the user to select one option. +func selectForm(io *IOStreams, _ context.Context, msg string, options []string, cfg SelectPromptConfig) (SelectPromptResponse, error) { var selected string err := buildSelectForm(io, msg, options, cfg, &selected).Run() if errors.Is(err, huh.ErrUserAborted) { @@ -118,7 +119,7 @@ func charmSelectPrompt(io *IOStreams, _ context.Context, msg string, options []s return SelectPromptResponse{Prompt: true, Index: index, Option: selected}, nil } -// buildPasswordForm constructs a huh form for password (hidden input) prompts. +// buildPasswordForm constructs an interactive form for password (hidden input) prompts. func buildPasswordForm(io *IOStreams, message string, cfg PasswordPromptConfig, input *string) *huh.Form { field := huh.NewInput(). Title(message). @@ -131,8 +132,8 @@ func buildPasswordForm(io *IOStreams, message string, cfg PasswordPromptConfig, return newForm(io, field) } -// charmPasswordPrompt prompts for a password (hidden input) using a charm huh form -func charmPasswordPrompt(io *IOStreams, _ context.Context, message string, cfg PasswordPromptConfig) (PasswordPromptResponse, error) { +// passwordForm interactively prompts for a password with hidden input. +func passwordForm(io *IOStreams, _ context.Context, message string, cfg PasswordPromptConfig) (PasswordPromptResponse, error) { var input string err := buildPasswordForm(io, message, cfg, &input).Run() if errors.Is(err, huh.ErrUserAborted) { @@ -143,7 +144,7 @@ func charmPasswordPrompt(io *IOStreams, _ context.Context, message string, cfg P return PasswordPromptResponse{Prompt: true, Value: input}, nil } -// buildMultiSelectForm constructs a huh form for multiple-selection prompts. +// buildMultiSelectForm constructs an interactive form for multiple-selection prompts. func buildMultiSelectForm(io *IOStreams, message string, options []string, selected *[]string) *huh.Form { var opts []huh.Option[string] for _, opt := range options { @@ -158,8 +159,8 @@ func buildMultiSelectForm(io *IOStreams, message string, options []string, selec return newForm(io, field) } -// charmMultiSelectPrompt prompts the user to select multiple options using a charm huh form -func charmMultiSelectPrompt(io *IOStreams, _ context.Context, message string, options []string) ([]string, error) { +// multiSelectForm interactively prompts the user to select multiple options. +func multiSelectForm(io *IOStreams, _ context.Context, message string, options []string) ([]string, error) { var selected []string err := buildMultiSelectForm(io, message, options, &selected).Run() if errors.Is(err, huh.ErrUserAborted) { diff --git a/internal/iostreams/charm_test.go b/internal/iostreams/forms_test.go similarity index 97% rename from internal/iostreams/charm_test.go rename to internal/iostreams/forms_test.go index a54fb9f7..f0ba9144 100644 --- a/internal/iostreams/charm_test.go +++ b/internal/iostreams/forms_test.go @@ -33,7 +33,7 @@ func key(r rune) tea.KeyPressMsg { return tea.KeyPressMsg{Code: r, Text: string(r)} } -func TestCharmInput(t *testing.T) { +func TestInputForm(t *testing.T) { t.Run("renders the title", func(t *testing.T) { var input string f := buildInputForm(nil, "Enter your name", InputPromptConfig{}, &input) @@ -93,7 +93,7 @@ func TestCharmInput(t *testing.T) { }) } -func TestCharmConfirm(t *testing.T) { +func TestConfirmForm(t *testing.T) { t.Run("renders the title and buttons", func(t *testing.T) { choice := false f := buildConfirmForm(nil, "Are you sure?", &choice) @@ -128,7 +128,7 @@ func TestCharmConfirm(t *testing.T) { }) } -func TestCharmSelect(t *testing.T) { +func TestSelectForm(t *testing.T) { t.Run("renders the title and options", func(t *testing.T) { var selected string options := []string{"Foo", "Bar", "Baz"} @@ -210,7 +210,7 @@ func TestCharmSelect(t *testing.T) { }) } -func TestCharmPassword(t *testing.T) { +func TestPasswordForm(t *testing.T) { t.Run("renders the title", func(t *testing.T) { var input string f := buildPasswordForm(nil, "Enter password", PasswordPromptConfig{}, &input) @@ -259,7 +259,7 @@ func TestCharmPassword(t *testing.T) { }) } -func TestCharmMultiSelect(t *testing.T) { +func TestMultiSelectForm(t *testing.T) { t.Run("renders the title and options", func(t *testing.T) { var selected []string options := []string{"Foo", "Bar", "Baz"} @@ -305,7 +305,7 @@ func TestCharmMultiSelect(t *testing.T) { }) } -func TestCharmFormsUseSlackTheme(t *testing.T) { +func TestFormsUseSlackTheme(t *testing.T) { fsMock := slackdeps.NewFsMock() osMock := slackdeps.NewOsMock() osMock.AddDefaultMocks() @@ -364,7 +364,7 @@ func TestCharmFormsUseSlackTheme(t *testing.T) { }) } -func TestCharmFormsWithoutLipgloss(t *testing.T) { +func TestFormsWithoutLipgloss(t *testing.T) { t.Run("multi-select uses default prefix without lipgloss", func(t *testing.T) { var selected []string f := buildMultiSelectForm(nil, "Pick", []string{"A", "B"}, &selected) diff --git a/internal/iostreams/prompts.go b/internal/iostreams/prompts.go index 9c5a2f0a..0a758e53 100644 --- a/internal/iostreams/prompts.go +++ b/internal/iostreams/prompts.go @@ -14,7 +14,7 @@ package iostreams -// Prompt type definitions shared between survey and charm implementations. +// Prompts handle flag values and interactive forms for gathering user input. import ( "context" @@ -194,7 +194,7 @@ func errInteractivityFlags(cfg PromptConfig) error { // the message func (io *IOStreams) ConfirmPrompt(ctx context.Context, message string, defaultValue bool) (bool, error) { if io.config.WithExperimentOn(experiment.Huh) { - return charmConfirmPrompt(io, ctx, message, defaultValue) + return confirmForm(io, ctx, message, defaultValue) } return surveyConfirmPrompt(io, ctx, message, defaultValue) } @@ -203,7 +203,7 @@ func (io *IOStreams) ConfirmPrompt(ctx context.Context, message string, defaultV // optionally be made required func (io *IOStreams) InputPrompt(ctx context.Context, message string, cfg InputPromptConfig) (string, error) { if io.config.WithExperimentOn(experiment.Huh) { - return charmInputPrompt(io, ctx, message, cfg) + return inputForm(io, ctx, message, cfg) } return surveyInputPrompt(io, ctx, message, cfg) } @@ -212,7 +212,7 @@ func (io *IOStreams) InputPrompt(ctx context.Context, message string, cfg InputP // returns the selected values func (io *IOStreams) MultiSelectPrompt(ctx context.Context, message string, options []string) ([]string, error) { if io.config.WithExperimentOn(experiment.Huh) { - return charmMultiSelectPrompt(io, ctx, message, options) + return multiSelectForm(io, ctx, message, options) } return surveyMultiSelectPrompt(io, ctx, message, options) } @@ -230,7 +230,7 @@ func (io *IOStreams) PasswordPrompt(ctx context.Context, message string, cfg Pas } if io.config.WithExperimentOn(experiment.Huh) { - return charmPasswordPrompt(io, ctx, message, cfg) + return passwordForm(io, ctx, message, cfg) } return surveyPasswordPrompt(io, ctx, message, cfg) } @@ -258,7 +258,7 @@ func (io *IOStreams) SelectPrompt(ctx context.Context, msg string, options []str } if io.config.WithExperimentOn(experiment.Huh) { - return charmSelectPrompt(io, ctx, msg, options, cfg) + return selectForm(io, ctx, msg, options, cfg) } return surveySelectPrompt(io, ctx, msg, options, cfg) }