Closed ryan-timothy-albert closed 8 months ago
I think this is super reasonable! What do you think @meowgorithm?
Yep, I think that makes sense, but @ryan-timothy-albert is your use case to modify an existing theme or is this for completely custom themes? Asking as you can create copies of themes with their constructor (i.e. huh.ThemeCharm()
) and then alter them from there.
t := huh.ThemeCharm()
t.Base = t.Base.Padding(4, 8) // add loads of padding to the form
Creating completely custom themes off of huh.ThemeBase. For some reason when I didn't copy it I was trouble having getting some of my custom lipgloss styling to appear.
https://github.com/charmbracelet/huh/pull/124 Here is a PR if it's something that makes sense
The PR makes sense, but could you also post some sample code here showing how you're trying to do it currently? We generally try and avoid increasing API surface area unless totally necessary.
The PR makes sense, but could you also post some sample code here showing how you're trying to do it currently? We generally try and avoid increasing API surface area unless totally necessary.
var formTheme *huh.Theme
func init() {
t := copyBaseTheme(huh.ThemeBase())
f := &t.Focused
f.Base = f.Base.BorderForeground(styles.Focused.GetForeground())
f.Title.Foreground(styles.Focused.GetForeground()).Bold(true)
f.Description.Foreground(styles.Dimmed.GetForeground()).Italic(true).Inline(false)
f.ErrorIndicator.Foreground(styles.Colors.Red)
f.ErrorMessage.Foreground(styles.Colors.Red)
f.SelectSelector.Foreground(styles.Focused.GetForeground())
f.MultiSelectSelector.Foreground(styles.Focused.GetForeground())
f.SelectedOption.Foreground(styles.Focused.GetForeground())
f.FocusedButton.Background(styles.Colors.Green)
f.BlurredButton.Background(styles.Dimmed.GetForeground())
f.Next = f.FocusedButton.Copy()
f.TextInput.Cursor.Foreground(styles.Focused.GetForeground())
f.TextInput.Placeholder.Foreground(styles.Dimmed.GetForeground()).Italic(true)
f.TextInput.Prompt.Foreground(styles.Focused.GetForeground())
f.TextInput.Text.Foreground(styles.Focused.GetForeground())
b := &t.Blurred
b.Description.Italic(true)
b.TextInput.Placeholder.Italic(true)
b.SelectedOption.Foreground(styles.FocusedDimmed.GetForeground())
b.SelectSelector.Foreground(styles.FocusedDimmed.GetForeground())
formTheme = &t
}
// What I've implemented is a direct duplicate of huh theme.copy()
func copyBaseTheme(original *huh.Theme) huh.Theme {
return huh.Theme{
Form: original.Form.Copy(),
Group: original.Group.Copy(),
FieldSeparator: original.FieldSeparator.Copy(),
Blurred: huh.FieldStyles{
Base: original.Blurred.Base.Copy(),
Title: original.Blurred.Title.Copy(),
Description: original.Blurred.Description.Copy(),
ErrorIndicator: original.Blurred.ErrorIndicator.Copy(),
ErrorMessage: original.Blurred.ErrorMessage.Copy(),
SelectSelector: original.Blurred.SelectSelector.Copy(),
Option: original.Blurred.Option.Copy(),
MultiSelectSelector: original.Blurred.MultiSelectSelector.Copy(),
SelectedOption: original.Blurred.SelectedOption.Copy(),
SelectedPrefix: original.Blurred.SelectedPrefix.Copy(),
UnselectedOption: original.Blurred.UnselectedOption.Copy(),
UnselectedPrefix: original.Blurred.UnselectedPrefix.Copy(),
FocusedButton: original.Blurred.FocusedButton.Copy(),
BlurredButton: original.Blurred.BlurredButton.Copy(),
TextInput: huh.TextInputStyles{
Cursor: original.Blurred.TextInput.Cursor.Copy(),
Placeholder: original.Blurred.TextInput.Placeholder.Copy(),
Prompt: original.Blurred.TextInput.Prompt.Copy(),
Text: original.Blurred.TextInput.Text.Copy(),
},
Card: original.Blurred.Card.Copy(),
Next: original.Blurred.Next.Copy(),
},
Focused: huh.FieldStyles{
Base: original.Focused.Base.Copy(),
Title: original.Focused.Title.Copy(),
Description: original.Focused.Description.Copy(),
ErrorIndicator: original.Focused.ErrorIndicator.Copy(),
ErrorMessage: original.Focused.ErrorMessage.Copy(),
SelectSelector: original.Focused.SelectSelector.Copy(),
Option: original.Focused.Option.Copy(),
MultiSelectSelector: original.Focused.MultiSelectSelector.Copy(),
SelectedOption: original.Focused.SelectedOption.Copy(),
SelectedPrefix: original.Focused.SelectedPrefix.Copy(),
UnselectedOption: original.Focused.UnselectedOption.Copy(),
UnselectedPrefix: original.Focused.UnselectedPrefix.Copy(),
FocusedButton: original.Focused.FocusedButton.Copy(),
BlurredButton: original.Focused.BlurredButton.Copy(),
TextInput: huh.TextInputStyles{
Cursor: original.Focused.TextInput.Cursor.Copy(),
Placeholder: original.Focused.TextInput.Placeholder.Copy(),
Prompt: original.Focused.TextInput.Prompt.Copy(),
Text: original.Focused.TextInput.Text.Copy(),
},
Card: original.Focused.Card.Copy(),
Next: original.Focused.Next.Copy(),
},
Help: help.Styles{
Ellipsis: original.Help.Ellipsis.Copy(),
ShortKey: original.Help.ShortKey.Copy(),
ShortDesc: original.Help.ShortDesc.Copy(),
ShortSeparator: original.Help.ShortSeparator.Copy(),
FullKey: original.Help.FullKey.Copy(),
FullDesc: original.Help.FullDesc.Copy(),
FullSeparator: original.Help.FullSeparator.Copy(),
},
}
}
This is helpful; thank you. So huh.ThemeBase()
does in fact return a copy of the base style despite the fact that it's returning a pointer reference. Themes actually don't exist as global variables (they're created on the fly in their constructor) so can safely mutate the Theme
you get after calling huh.ThemeBase()
.
Unless I'm missing something?
This is helpful; thank you. So
huh.ThemeBase()
does in fact return a copy of the base style despite the fact that it's returning a pointer reference. Themes actually don't exist as global variables (they're created on the fly in their constructor) so can safely mutate theTheme
you get after callinghuh.ThemeBase()
.Unless I'm missing something?
I thought that would be the case but when I replace t := huh.ThemeBase()
for t := copyBaseTheme(huh.ThemeBase())
some of my color stylings no longer get correctly applied to my form. I was a bit confused about this, not sure if you see anything obviously wrong.
Interesting. This could be a bug internally. Would you mind sharing some of the form code you're using against this so we can have a look on our end?
Interesting. This could be a bug internally. Would you mind sharing some of the form code you're using against this so we can have a look on our end?
type Model struct {
title string
description string
form *huh.Form // huh.Form is just a tea.Model
}
func NewForm(form *huh.Form, args ...string) Model {
model := Model{
form: form.WithTheme(formTheme),
}
if len(args) > 0 {
model.title = args[0]
if len(args) > 1 {
model.description = args[1]
}
}
return model
}
func (m Model) Init() tea.Cmd {
return m.form.Init()
}
func (m Model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.KeyMsg:
switch msg.String() {
case "esc", "ctrl+c", "q":
return m, tea.Quit
}
}
var cmds []tea.Cmd
// Process the form
form, cmd := m.form.Update(msg)
if f, ok := form.(*huh.Form); ok {
m.form = f
cmds = append(cmds, cmd)
}
// Quit when the form is done.
if m.form.State == huh.StateCompleted {
cmds = append(cmds, tea.Quit)
}
return m, tea.Batch(cmds...)
}
func (m Model) View() string {
if m.form.State == huh.StateCompleted {
return ""
}
titleStyle := lipgloss.NewStyle().Foreground(styles.Focused.GetForeground()).Bold(true)
descriptionStyle := lipgloss.NewStyle().Foreground(styles.Dimmed.GetForeground()).Italic(true)
if m.title != "" {
header := titleStyle.Render(m.title)
if m.description != "" {
header += "\n" + descriptionStyle.Render(m.description)
}
return header + "\n\n" + m.form.View()
}
return m.form.View()
}
This is the wrapper we apply to a form
Didn't want to create a new issue but had a quick question on customizing the styling of a multi select.
I would love to get rid of the []
for unselected options and [*]
for selected options. Ideally I would just have nothing here because highlighting shows what's selected.
@meowgorithm do you know how I can apply this styling change.
Something similar to the ReadMe demo would work too. I've had a bit of trouble replicating that style when it comes to my multi select.
I was actually able to solve the problem by reverse engineering the charm theme
When customizing themes for a form I frequently want to make a copy from a base theme. The theme copy function is currently not exported.
To get around this I have to write my own function that deep copies every field of theme. Basically exactly what theme.copy does.
Are you open to exporting theme.copy? I would be happy to put up a PR.