Skip to content
Merged
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
3 changes: 2 additions & 1 deletion pkg/cli/add_interactive_auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import (

"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/styles"
)

// checkGHAuthStatus verifies the user is logged in to GitHub CLI
Expand Down Expand Up @@ -55,7 +56,7 @@ func (c *AddInteractiveConfig) checkGitRepository() error {
return nil
}),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("failed to get repository info: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/add_interactive_engine.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/workflow"
)

Expand Down Expand Up @@ -123,7 +124,7 @@ func (c *AddInteractiveConfig) selectAIEngineAndKey() error {
Options(engineOptions...).
Value(&selectedEngine),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("failed to select coding agent: %w", err)
Expand Down
5 changes: 3 additions & 2 deletions pkg/cli/add_interactive_git.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (

"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/workflow"
)

Expand Down Expand Up @@ -96,7 +97,7 @@ func (c *AddInteractiveConfig) createWorkflowPRAndConfigureSecret(ctx context.Co
Options(options...).
Value(&chosen),
),
).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)
Expand Down Expand Up @@ -129,7 +130,7 @@ func (c *AddInteractiveConfig) createWorkflowPRAndConfigureSecret(ctx context.Co
Description("Add a prefix if required, for example: feat: or fix:").
Value(&newTitle),
),
).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)
}
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/add_interactive_orchestrator.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/styles"
)

var addInteractiveLog = logger.New("cli:add_interactive")
Expand Down Expand Up @@ -228,7 +229,7 @@ func (c *AddInteractiveConfig) confirmChanges(workflowFiles, initFiles []string,
Negative("No, cancel").
Value(&confirmed),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("confirmation failed: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/add_interactive_schedule.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/parser"
"github.com/github/gh-aw/pkg/sliceutil"
"github.com/github/gh-aw/pkg/styles"
)

var scheduleWizardLog = logger.New("cli:add_interactive_schedule")
Expand Down Expand Up @@ -227,7 +228,7 @@ func (c *AddInteractiveConfig) selectScheduleFrequency() error {
Options(options...).
Value(&selected),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("failed to select schedule frequency: %w", err)
Expand Down
3 changes: 2 additions & 1 deletion pkg/cli/add_interactive_workflow.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (
"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/workflow"
)

Expand Down Expand Up @@ -109,7 +110,7 @@ func (c *AddInteractiveConfig) checkStatusAndOfferRun(ctx context.Context) error
Negative("No, I'll run later").
Value(&runNow),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return nil // Not critical, just skip
Expand Down
7 changes: 4 additions & 3 deletions pkg/cli/engine_secrets.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/github/gh-aw/pkg/repoutil"
"github.com/github/gh-aw/pkg/sliceutil"
"github.com/github/gh-aw/pkg/stringutil"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/workflow"
)

Expand Down Expand Up @@ -307,7 +308,7 @@ func promptForCopilotPATUnified(req SecretRequirement, config EngineSecretConfig
return stringutil.ValidateCopilotPAT(s)
}),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("failed to get Copilot token: %w", err)
Expand Down Expand Up @@ -355,7 +356,7 @@ func promptForSystemTokenUnified(req SecretRequirement, config EngineSecretConfi
return nil
}),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("failed to get %s token: %w", req.Name, err)
Expand Down Expand Up @@ -408,7 +409,7 @@ func promptForGenericAPIKeyUnified(req SecretRequirement, config EngineSecretCon
return nil
}),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return fmt.Errorf("failed to get %s API key: %w", label, err)
Expand Down
7 changes: 4 additions & 3 deletions pkg/cli/interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import (
"github.com/github/gh-aw/pkg/console"
"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/workflow"
)

Expand Down Expand Up @@ -98,7 +99,7 @@ func (b *InteractiveWorkflowBuilder) promptForWorkflowName() error {
Value(&b.WorkflowName).
Validate(ValidateWorkflowName),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

return form.Run()
}
Expand Down Expand Up @@ -222,7 +223,7 @@ func (b *InteractiveWorkflowBuilder) promptForConfiguration() error {
).
Title("Instructions").
Description("Describe what you want this workflow to accomplish"),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return err
Expand Down Expand Up @@ -267,7 +268,7 @@ func (b *InteractiveWorkflowBuilder) generateWorkflow(force bool) error {
Negative("No, cancel").
Value(&overwrite),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := confirmForm.Run(); err != nil {
return fmt.Errorf("confirmation failed: %w", err)
Expand Down
7 changes: 4 additions & 3 deletions pkg/cli/run_interactive.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import (
"github.com/github/gh-aw/pkg/constants"
"github.com/github/gh-aw/pkg/logger"
"github.com/github/gh-aw/pkg/sliceutil"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/tty"
"github.com/github/gh-aw/pkg/workflow"
)
Expand Down Expand Up @@ -189,7 +190,7 @@ func selectWorkflow(workflows []WorkflowOption) (*WorkflowOption, error) {
Height(15).
Value(&selected),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
return nil, fmt.Errorf("workflow selection cancelled or failed: %w", err)
Expand Down Expand Up @@ -313,7 +314,7 @@ func collectInputsWithMap(inputs map[string]*workflow.InputDefinition) ([]string
}

// Show the form
form := huh.NewForm(formGroups...).WithAccessible(console.IsAccessibleMode())
form := huh.NewForm(formGroups...).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())
if err := form.Run(); err != nil {
return nil, fmt.Errorf("input collection cancelled: %w", err)
}
Expand Down Expand Up @@ -350,7 +351,7 @@ func confirmExecution(wf *WorkflowOption, inputs []string) bool {
Negative("No, cancel").
Value(&confirm),
),
).WithAccessible(console.IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(console.IsAccessibleMode())

if err := form.Run(); err != nil {
runInteractiveLog.Printf("Confirmation failed: %v", err)
Expand Down
3 changes: 2 additions & 1 deletion pkg/console/confirm.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ package console

import (
"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/styles"
)

// ConfirmAction shows an interactive confirmation dialog using Bubble Tea (huh)
Expand All @@ -19,7 +20,7 @@ func ConfirmAction(title, affirmative, negative string) (bool, error) {
Negative(negative).
Value(&confirmed),
),
).WithAccessible(IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(IsAccessibleMode())

if err := confirmForm.Run(); err != nil {
return false, err
Expand Down
3 changes: 2 additions & 1 deletion pkg/console/input.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"errors"

"github.com/charmbracelet/huh"
"github.com/github/gh-aw/pkg/styles"
"github.com/github/gh-aw/pkg/tty"
)

Expand Down Expand Up @@ -34,7 +35,7 @@ func PromptSecretInput(title, description string) (string, error) {
}).
Value(&value),
),
).WithAccessible(IsAccessibleMode())
).WithTheme(styles.HuhTheme()).WithAccessible(IsAccessibleMode())

if err := form.Run(); err != nil {
return "", err
Expand Down
74 changes: 74 additions & 0 deletions pkg/styles/huh_theme.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
//go:build !js && !wasm

package styles

import (
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
)

// HuhTheme returns a custom huh.Theme 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.Theme {
t := huh.ThemeBase()

// Map the pkg/styles palette to lipgloss.AdaptiveColor for huh compatibility.
// huh uses github.com/charmbracelet/lipgloss, so we use that type here.
var (
primary = lipgloss.AdaptiveColor{Light: hexColorPurpleLight, Dark: hexColorPurpleDark}
success = lipgloss.AdaptiveColor{Light: hexColorSuccessLight, Dark: hexColorSuccessDark}
errorColor = lipgloss.AdaptiveColor{Light: hexColorErrorLight, Dark: hexColorErrorDark}
warning = lipgloss.AdaptiveColor{Light: hexColorWarningLight, Dark: hexColorWarningDark}
comment = lipgloss.AdaptiveColor{Light: hexColorCommentLight, Dark: hexColorCommentDark}
fg = lipgloss.AdaptiveColor{Light: hexColorForegroundLight, Dark: hexColorForegroundDark}
bg = lipgloss.AdaptiveColor{Light: hexColorBackgroundLight, Dark: hexColorBackgroundDark}
border = lipgloss.AdaptiveColor{Light: hexColorBorderLight, Dark: 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)

// 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)

// 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)

// 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

return t
}
Loading