Files
Felis/cmd/felis/tui_theme.go

75 lines
3.1 KiB
Go

package main
import (
"github.com/charmbracelet/huh"
"github.com/charmbracelet/lipgloss"
)
// felisTheme recolors huh's base theme to the Felis palette (cyan primary,
// purple accent, green success) so the forms feel like part of the product
// rather than a generic huh prompt. We start from ThemeBase — a neutral
// skeleton with the right structure — and override only the colored bits.
func felisTheme() *huh.Theme {
t := huh.ThemeBase()
f := &t.Focused
f.Base = f.Base.BorderForeground(cPrimary)
f.Card = f.Base
f.Title = lipgloss.NewStyle().Foreground(cPrimary).Bold(true)
f.NoteTitle = f.Title
f.Description = lipgloss.NewStyle().Foreground(cDim)
f.ErrorIndicator = lipgloss.NewStyle().Foreground(cError).SetString(" ✗")
f.ErrorMessage = lipgloss.NewStyle().Foreground(cError)
f.SelectSelector = lipgloss.NewStyle().Foreground(cPrimary).SetString("▸ ")
// Unselected rows recede to dim and the active row is bright cyan + bold, so
// the list reads at a glance as a picker with one row chosen — not a wall of
// equally-lit info text. huh's single-select paints UnselectedOption (not
// Option), which ThemeBase leaves bright; overriding it is the key fix.
f.Option = lipgloss.NewStyle().Foreground(cDim)
f.UnselectedOption = lipgloss.NewStyle().Foreground(cDim)
f.SelectedOption = lipgloss.NewStyle().Foreground(cPrimary).Bold(true)
f.NextIndicator = lipgloss.NewStyle().Foreground(cAccent).MarginLeft(1).SetString("→")
f.PrevIndicator = lipgloss.NewStyle().Foreground(cAccent).MarginRight(1).SetString("←")
f.TextInput.Cursor = lipgloss.NewStyle().Foreground(cPrimary)
f.TextInput.Prompt = lipgloss.NewStyle().Foreground(cPrimary)
f.TextInput.Placeholder = lipgloss.NewStyle().Foreground(cDim)
f.TextInput.Text = lipgloss.NewStyle().Foreground(cWhite)
button := lipgloss.NewStyle().Padding(0, 2).MarginRight(1)
f.FocusedButton = button.Foreground(cWhite).Background(cPrimary).Bold(true)
f.BlurredButton = button.Foreground(cWhite).Background(cBgDark)
// Blurred mirrors focused but hides the left bar and dims the title so the
// active field is unmistakable.
t.Blurred = *f
t.Blurred.Base = f.Base.BorderStyle(lipgloss.HiddenBorder())
t.Blurred.Card = t.Blurred.Base
t.Blurred.Title = lipgloss.NewStyle().Foreground(cDim).Bold(true)
t.Blurred.NextIndicator = lipgloss.NewStyle()
t.Blurred.PrevIndicator = lipgloss.NewStyle()
// Help line at the bottom of every form.
t.Help.ShortKey = lipgloss.NewStyle().Foreground(cPrimary)
t.Help.ShortDesc = lipgloss.NewStyle().Foreground(cDim)
t.Help.ShortSeparator = lipgloss.NewStyle().Foreground(cDim)
t.Help.FullKey = t.Help.ShortKey
t.Help.FullDesc = t.Help.ShortDesc
t.Help.FullSeparator = t.Help.ShortSeparator
return t
}
// theme is built once; lipgloss styles are immutable values so sharing is safe.
var felisFormTheme = felisTheme()
// newFelisForm wires a form to the Felis theme with the conventions every wizard
// screen wants: themed, help line shown, errors shown inline.
func newFelisForm(groups ...*huh.Group) *huh.Form {
return huh.NewForm(groups...).
WithTheme(felisFormTheme).
WithShowHelp(true).
WithShowErrors(true)
}