Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion internal/iostreams/forms.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,9 @@ import (
// 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) {
if io != nil && io.config.NoColor {
form = form.WithTheme(style.ThemePlain())
} else if io != nil && io.config.WithExperimentOn(experiment.Lipgloss) {
form = form.WithTheme(style.ThemeSlack())
} else {
form = form.WithTheme(style.ThemeSurvey())
Expand Down
41 changes: 41 additions & 0 deletions internal/iostreams/forms_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -415,6 +415,47 @@ func TestFormsUseSlackTheme(t *testing.T) {
})
}

func TestFormsNoColor(t *testing.T) {
t.Run("forms use plain theme with no-color", func(t *testing.T) {
fsMock := slackdeps.NewFsMock()
osMock := slackdeps.NewOsMock()
osMock.AddDefaultMocks()
cfg := config.NewConfig(fsMock, osMock)
cfg.NoColor = true
io := NewIOStreams(cfg, fsMock, osMock)

var selected string
f := buildSelectForm(io, "Pick", []string{"A", "B"}, SelectPromptConfig{}, &selected)
f.Update(f.Init())

view := f.View()
// Title and option lines should have no ANSI codes
for _, line := range strings.Split(view, "\n")[:3] {
assert.Equal(t, ansi.Strip(line), line, "content line should have no ANSI codes")
}
})

t.Run("no-color takes priority over lipgloss experiment", func(t *testing.T) {
fsMock := slackdeps.NewFsMock()
osMock := slackdeps.NewOsMock()
osMock.AddDefaultMocks()
cfg := config.NewConfig(fsMock, osMock)
cfg.NoColor = true
cfg.ExperimentsFlag = []string{"lipgloss"}
cfg.LoadExperiments(context.Background(), func(_ context.Context, _ string, _ ...any) {})
io := NewIOStreams(cfg, fsMock, osMock)

var selected string
f := buildSelectForm(io, "Pick", []string{"A", "B"}, SelectPromptConfig{}, &selected)
f.Update(f.Init())

view := f.View()
for _, line := range strings.Split(view, "\n")[:3] {
assert.Equal(t, ansi.Strip(line), line, "content line should have no ANSI codes even with lipgloss experiment on")
}
})
}

func TestFormsUseSurveyTheme(t *testing.T) {
t.Run("multi-select uses survey prefix without lipgloss", func(t *testing.T) {
var selected []string
Expand Down
30 changes: 30 additions & 0 deletions internal/style/theme.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,36 @@ func Chevron() string {
return "❱"
}

// ThemePlain returns a huh Theme with no colors or formatting.
func ThemePlain() huh.Theme {
return huh.ThemeFunc(func(_ bool) *huh.Styles {
t := huh.ThemeBase(false)
t.Focused.Title = lipgloss.NewStyle()
t.Focused.Description = lipgloss.NewStyle()
t.Focused.ErrorIndicator = lipgloss.NewStyle().SetString(" *")
t.Focused.ErrorMessage = lipgloss.NewStyle()
t.Focused.SelectSelector = lipgloss.NewStyle().SetString("> ")
t.Focused.Option = lipgloss.NewStyle()
t.Focused.MultiSelectSelector = lipgloss.NewStyle().SetString("> ")
t.Focused.SelectedOption = lipgloss.NewStyle()
t.Focused.SelectedPrefix = lipgloss.NewStyle().SetString("[x] ")
t.Focused.UnselectedOption = lipgloss.NewStyle()
t.Focused.UnselectedPrefix = lipgloss.NewStyle().SetString("[ ] ")
t.Focused.FocusedButton = lipgloss.NewStyle().Padding(0, 2).MarginRight(1)
t.Focused.BlurredButton = lipgloss.NewStyle().Padding(0, 2).MarginRight(1)
Comment on lines +144 to +145
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📸 issue: I'm not sure if these are the correct options but I find the confirm default isn't highlighted now. I'll include a screenshot for other reviewers:

Image

t.Focused.TextInput.Cursor = lipgloss.NewStyle()
t.Focused.TextInput.Prompt = lipgloss.NewStyle()
t.Focused.TextInput.Placeholder = lipgloss.NewStyle()
t.Focused.TextInput.Text = lipgloss.NewStyle()
t.Focused.Base = lipgloss.NewStyle().PaddingLeft(1).BorderStyle(lipgloss.ThickBorder()).BorderLeft(true)
t.Blurred = t.Focused
t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder())
t.Blurred.SelectSelector = lipgloss.NewStyle().SetString(" ")
t.Blurred.MultiSelectSelector = lipgloss.NewStyle().SetString(" ")
return t
})
}

// ThemeSurvey returns a huh Theme that matches the legacy survey prompt styling.
// Applied when experiment.Huh is on but experiment.Lipgloss is off.
func ThemeSurvey() huh.Theme {
Expand Down
34 changes: 34 additions & 0 deletions internal/style/theme_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -116,6 +116,40 @@ func TestThemeSurvey(t *testing.T) {
}
}

func TestThemePlain(t *testing.T) {
theme := ThemePlain().Theme(false)
tests := map[string]struct {
rendered string
expected string
}{
"title renders plain text": {
rendered: theme.Focused.Title.Render("x"),
expected: "x",
},
"error message renders plain text": {
rendered: theme.Focused.ErrorMessage.Render("err"),
expected: "err",
},
"select selector renders plain >": {
rendered: theme.Focused.SelectSelector.Render(),
expected: "> ",
},
"selected prefix renders [x]": {
rendered: theme.Focused.SelectedPrefix.Render(),
expected: "[x] ",
},
"unselected prefix renders [ ]": {
rendered: theme.Focused.UnselectedPrefix.Render(),
expected: "[ ] ",
},
}
for name, tc := range tests {
t.Run(name, func(t *testing.T) {
assert.Equal(t, tc.expected, tc.rendered)
})
}
}

func TestChevron(t *testing.T) {
tests := map[string]struct {
styleEnabled bool
Expand Down
Loading