diff --git a/pkg/cli/add_interactive_auth.go b/pkg/cli/add_interactive_auth.go index f282e1f8f5..6bcaa2b0e0 100644 --- a/pkg/cli/add_interactive_auth.go +++ b/pkg/cli/add_interactive_auth.go @@ -56,7 +56,7 @@ func (c *AddInteractiveConfig) checkGitRepository() error { return nil }), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(c.Ctx); err != nil { return fmt.Errorf("failed to get repository info: %w", err) diff --git a/pkg/cli/add_interactive_engine.go b/pkg/cli/add_interactive_engine.go index 3bfa44057f..5207c6e5b6 100644 --- a/pkg/cli/add_interactive_engine.go +++ b/pkg/cli/add_interactive_engine.go @@ -124,7 +124,7 @@ func (c *AddInteractiveConfig) selectAIEngineAndKey() error { Options(engineOptions...). Value(&selectedEngine), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(c.Ctx); err != nil { return fmt.Errorf("failed to select coding agent: %w", err) diff --git a/pkg/cli/add_interactive_git.go b/pkg/cli/add_interactive_git.go index 8e8cc3478f..2e459f4d04 100644 --- a/pkg/cli/add_interactive_git.go +++ b/pkg/cli/add_interactive_git.go @@ -97,7 +97,7 @@ func (c *AddInteractiveConfig) createWorkflowPRAndConfigureSecret(ctx context.Co Options(options...). Value(&chosen), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if selectErr := selectForm.Run(); selectErr != nil { return fmt.Errorf("failed to get user input: %w", selectErr) @@ -130,7 +130,7 @@ func (c *AddInteractiveConfig) createWorkflowPRAndConfigureSecret(ctx context.Co Description("Add a prefix if required, for example: feat: or fix:"). Value(&newTitle), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if titleErr := titleForm.Run(); titleErr != nil { return fmt.Errorf("failed to get user input: %w", titleErr) } diff --git a/pkg/cli/add_interactive_orchestrator.go b/pkg/cli/add_interactive_orchestrator.go index 4d56433b6b..5911e37b3b 100644 --- a/pkg/cli/add_interactive_orchestrator.go +++ b/pkg/cli/add_interactive_orchestrator.go @@ -231,7 +231,7 @@ func (c *AddInteractiveConfig) confirmChanges(workflowFiles, initFiles []string, Negative("No, cancel"). Value(&confirmed), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(c.Ctx); err != nil { return fmt.Errorf("confirmation failed: %w", err) diff --git a/pkg/cli/add_interactive_schedule.go b/pkg/cli/add_interactive_schedule.go index f2c5ddd871..3f8103900d 100644 --- a/pkg/cli/add_interactive_schedule.go +++ b/pkg/cli/add_interactive_schedule.go @@ -228,7 +228,7 @@ func (c *AddInteractiveConfig) selectScheduleFrequency() error { Options(options...). Value(&selected), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(c.Ctx); err != nil { return fmt.Errorf("failed to select schedule frequency: %w", err) diff --git a/pkg/cli/add_interactive_workflow.go b/pkg/cli/add_interactive_workflow.go index 113d8cd372..ab9b1c7e04 100644 --- a/pkg/cli/add_interactive_workflow.go +++ b/pkg/cli/add_interactive_workflow.go @@ -110,7 +110,7 @@ func (c *AddInteractiveConfig) checkStatusAndOfferRun(ctx context.Context) error Negative("No, I'll run later"). Value(&runNow), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(ctx); err != nil { return nil // Not critical, just skip diff --git a/pkg/cli/engine_secrets.go b/pkg/cli/engine_secrets.go index 47d8d1a5d3..a447a557f8 100644 --- a/pkg/cli/engine_secrets.go +++ b/pkg/cli/engine_secrets.go @@ -319,7 +319,7 @@ func promptForCopilotPATUnified(req SecretRequirement, config EngineSecretConfig return stringutil.ValidateCopilotPAT(s) }), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(config.ctx()); err != nil { return fmt.Errorf("failed to get Copilot token: %w", err) @@ -367,7 +367,7 @@ func promptForSystemTokenUnified(req SecretRequirement, config EngineSecretConfi return nil }), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(config.ctx()); err != nil { return fmt.Errorf("failed to get %s token: %w", req.Name, err) @@ -420,7 +420,7 @@ func promptForGenericAPIKeyUnified(req SecretRequirement, config EngineSecretCon return nil }), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(config.ctx()); err != nil { return fmt.Errorf("failed to get %s API key: %w", label, err) diff --git a/pkg/cli/interactive.go b/pkg/cli/interactive.go index 56c4dd4bc5..cd64f370ac 100644 --- a/pkg/cli/interactive.go +++ b/pkg/cli/interactive.go @@ -101,7 +101,7 @@ func (b *InteractiveWorkflowBuilder) promptForWorkflowName() error { Value(&b.WorkflowName). Validate(ValidateWorkflowName), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) return form.RunWithContext(b.ctx) } @@ -224,7 +224,7 @@ func (b *InteractiveWorkflowBuilder) promptForConfiguration() error { ). Title("Instructions"). Description("Describe what you want this workflow to accomplish"), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(b.ctx); err != nil { return err @@ -269,7 +269,7 @@ func (b *InteractiveWorkflowBuilder) generateWorkflow(force bool) error { Negative("No, cancel"). Value(&overwrite), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := confirmForm.Run(); err != nil { return fmt.Errorf("confirmation failed: %w", err) diff --git a/pkg/cli/run_interactive.go b/pkg/cli/run_interactive.go index 9f3d77a498..2ce10d7c85 100644 --- a/pkg/cli/run_interactive.go +++ b/pkg/cli/run_interactive.go @@ -190,7 +190,7 @@ func selectWorkflow(ctx context.Context, workflows []WorkflowOption) (*WorkflowO Height(15). Value(&selected), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(ctx); err != nil { return nil, fmt.Errorf("workflow selection cancelled or failed: %w", err) @@ -314,7 +314,7 @@ func collectInputsWithMap(ctx context.Context, inputs map[string]*workflow.Input } // Show the form - form := huh.NewForm(formGroups...).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + form := huh.NewForm(formGroups...).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(ctx); err != nil { return nil, fmt.Errorf("input collection cancelled: %w", err) } @@ -351,7 +351,7 @@ func confirmExecution(ctx context.Context, wf *WorkflowOption, inputs []string) Negative("No, cancel"). Value(&confirm), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(console.IsAccessibleMode()) if err := form.RunWithContext(ctx); err != nil { runInteractiveLog.Printf("Confirmation failed: %v", err) diff --git a/pkg/console/confirm.go b/pkg/console/confirm.go index 898bb82484..014b5870ac 100644 --- a/pkg/console/confirm.go +++ b/pkg/console/confirm.go @@ -20,7 +20,7 @@ func ConfirmAction(title, affirmative, negative string) (bool, error) { Negative(negative). Value(&confirmed), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(IsAccessibleMode()) if err := confirmForm.Run(); err != nil { return false, err diff --git a/pkg/console/input.go b/pkg/console/input.go index dbc6ab651a..b2116582fc 100644 --- a/pkg/console/input.go +++ b/pkg/console/input.go @@ -35,7 +35,7 @@ func PromptSecretInput(title, description string) (string, error) { }). Value(&value), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(IsAccessibleMode()) if err := form.Run(); err != nil { return "", err diff --git a/pkg/console/list.go b/pkg/console/list.go index 85c52a2dc8..b09498c5ea 100644 --- a/pkg/console/list.go +++ b/pkg/console/list.go @@ -51,7 +51,7 @@ func ShowInteractiveList(title string, items []ListItem) (string, error) { Options(opts...). Value(&selected), ), - ).WithTheme(styles.HuhTheme()).WithAccessible(IsAccessibleMode()) + ).WithTheme(styles.HuhTheme).WithAccessible(IsAccessibleMode()) if err := form.Run(); err != nil { listLog.Printf("Error running list form: %v", err) diff --git a/pkg/styles/huh_theme.go b/pkg/styles/huh_theme.go index bd88b05fad..0dd8ad153e 100644 --- a/pkg/styles/huh_theme.go +++ b/pkg/styles/huh_theme.go @@ -7,70 +7,68 @@ import ( "charm.land/lipgloss/v2" ) -// HuhTheme returns a huh.ThemeFunc that maps the pkg/styles Dracula-inspired +// HuhTheme is a huh.ThemeFunc that maps the pkg/styles Dracula-inspired // color palette to huh form fields, giving interactive forms the same visual // identity as the rest of the CLI output. -func HuhTheme() huh.ThemeFunc { - return func(isDark bool) *huh.Styles { - t := huh.ThemeBase(isDark) - lightDark := lipgloss.LightDark(isDark) +var HuhTheme huh.ThemeFunc = func(isDark bool) *huh.Styles { + t := huh.ThemeBase(isDark) + lightDark := lipgloss.LightDark(isDark) - // Map the pkg/styles palette using lipgloss v2's LightDark helper. - var ( - primary = lightDark(lipgloss.Color(hexColorPurpleLight), lipgloss.Color(hexColorPurpleDark)) - success = lightDark(lipgloss.Color(hexColorSuccessLight), lipgloss.Color(hexColorSuccessDark)) - errorColor = lightDark(lipgloss.Color(hexColorErrorLight), lipgloss.Color(hexColorErrorDark)) - warning = lightDark(lipgloss.Color(hexColorWarningLight), lipgloss.Color(hexColorWarningDark)) - comment = lightDark(lipgloss.Color(hexColorCommentLight), lipgloss.Color(hexColorCommentDark)) - fg = lightDark(lipgloss.Color(hexColorForegroundLight), lipgloss.Color(hexColorForegroundDark)) - bg = lightDark(lipgloss.Color(hexColorBackgroundLight), lipgloss.Color(hexColorBackgroundDark)) - border = lightDark(lipgloss.Color(hexColorBorderLight), lipgloss.Color(hexColorBorderDark)) - ) + // Map the pkg/styles palette using lipgloss v2's LightDark helper. + var ( + primary = lightDark(lipgloss.Color(hexColorPurpleLight), lipgloss.Color(hexColorPurpleDark)) + success = lightDark(lipgloss.Color(hexColorSuccessLight), lipgloss.Color(hexColorSuccessDark)) + errorColor = lightDark(lipgloss.Color(hexColorErrorLight), lipgloss.Color(hexColorErrorDark)) + warning = lightDark(lipgloss.Color(hexColorWarningLight), lipgloss.Color(hexColorWarningDark)) + comment = lightDark(lipgloss.Color(hexColorCommentLight), lipgloss.Color(hexColorCommentDark)) + fg = lightDark(lipgloss.Color(hexColorForegroundLight), lipgloss.Color(hexColorForegroundDark)) + bg = lightDark(lipgloss.Color(hexColorBackgroundLight), lipgloss.Color(hexColorBackgroundDark)) + border = lightDark(lipgloss.Color(hexColorBorderLight), lipgloss.Color(hexColorBorderDark)) + ) - // Focused field styles - t.Focused.Base = t.Focused.Base.BorderForeground(border) - t.Focused.Card = t.Focused.Base - t.Focused.Title = t.Focused.Title.Foreground(primary).Bold(true) - t.Focused.NoteTitle = t.Focused.NoteTitle.Foreground(primary).Bold(true).MarginBottom(1) - t.Focused.Directory = t.Focused.Directory.Foreground(primary) - t.Focused.Description = t.Focused.Description.Foreground(comment) - t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(errorColor) - t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(errorColor) + // Focused field styles + t.Focused.Base = t.Focused.Base.BorderForeground(border) + t.Focused.Card = t.Focused.Base + t.Focused.Title = t.Focused.Title.Foreground(primary).Bold(true) + t.Focused.NoteTitle = t.Focused.NoteTitle.Foreground(primary).Bold(true).MarginBottom(1) + t.Focused.Directory = t.Focused.Directory.Foreground(primary) + t.Focused.Description = t.Focused.Description.Foreground(comment) + t.Focused.ErrorIndicator = t.Focused.ErrorIndicator.Foreground(errorColor) + t.Focused.ErrorMessage = t.Focused.ErrorMessage.Foreground(errorColor) - // Select / navigation indicators - t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(warning) - t.Focused.NextIndicator = t.Focused.NextIndicator.Foreground(warning) - t.Focused.PrevIndicator = t.Focused.PrevIndicator.Foreground(warning) + // Select / navigation indicators + t.Focused.SelectSelector = t.Focused.SelectSelector.Foreground(warning) + t.Focused.NextIndicator = t.Focused.NextIndicator.Foreground(warning) + t.Focused.PrevIndicator = t.Focused.PrevIndicator.Foreground(warning) - // List option styles - t.Focused.Option = t.Focused.Option.Foreground(fg) - t.Focused.MultiSelectSelector = t.Focused.MultiSelectSelector.Foreground(warning) - t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(success) - t.Focused.SelectedPrefix = t.Focused.SelectedPrefix.Foreground(success) - t.Focused.UnselectedOption = t.Focused.UnselectedOption.Foreground(fg) - t.Focused.UnselectedPrefix = t.Focused.UnselectedPrefix.Foreground(comment) + // List option styles + t.Focused.Option = t.Focused.Option.Foreground(fg) + t.Focused.MultiSelectSelector = t.Focused.MultiSelectSelector.Foreground(warning) + t.Focused.SelectedOption = t.Focused.SelectedOption.Foreground(success) + t.Focused.SelectedPrefix = t.Focused.SelectedPrefix.Foreground(success) + t.Focused.UnselectedOption = t.Focused.UnselectedOption.Foreground(fg) + t.Focused.UnselectedPrefix = t.Focused.UnselectedPrefix.Foreground(comment) - // Button styles - t.Focused.FocusedButton = t.Focused.FocusedButton.Foreground(bg).Background(primary).Bold(true) - t.Focused.BlurredButton = t.Focused.BlurredButton.Foreground(fg).Background(bg) - t.Focused.Next = t.Focused.FocusedButton + // Button styles + t.Focused.FocusedButton = t.Focused.FocusedButton.Foreground(bg).Background(primary).Bold(true) + t.Focused.BlurredButton = t.Focused.BlurredButton.Foreground(fg).Background(bg) + t.Focused.Next = t.Focused.FocusedButton - // Text input styles - t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(warning) - t.Focused.TextInput.Placeholder = t.Focused.TextInput.Placeholder.Foreground(comment) - t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(primary) + // Text input styles + t.Focused.TextInput.Cursor = t.Focused.TextInput.Cursor.Foreground(warning) + t.Focused.TextInput.Placeholder = t.Focused.TextInput.Placeholder.Foreground(comment) + t.Focused.TextInput.Prompt = t.Focused.TextInput.Prompt.Foreground(primary) - // Blurred styles mirror focused but hide the border - t.Blurred = t.Focused - t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder()) - t.Blurred.Card = t.Blurred.Base - t.Blurred.NextIndicator = lipgloss.NewStyle() - t.Blurred.PrevIndicator = lipgloss.NewStyle() + // Blurred styles mirror focused but hide the border + t.Blurred = t.Focused + t.Blurred.Base = t.Focused.Base.BorderStyle(lipgloss.HiddenBorder()) + t.Blurred.Card = t.Blurred.Base + t.Blurred.NextIndicator = lipgloss.NewStyle() + t.Blurred.PrevIndicator = lipgloss.NewStyle() - // Group header styles - t.Group.Title = t.Focused.Title - t.Group.Description = t.Focused.Description + // Group header styles + t.Group.Title = t.Focused.Title + t.Group.Description = t.Focused.Description - return t - } + return t }