Releases: charmbracelet/huh
v0.7.0
Less bugs, more feats
This is a quality-of-life release which fixes a handful of behavioral and rendering issues, and adds a few of useful features.
go get github.com/charmbracelet/huh@latest
Accessible mode: now more accessible
We made several updates to accessible mode, and it should now work better with
screen readers.
It will also now respect WithInput
and WithOutput
.
Focused? Hovered Filtered?
Useful when integrating with an existing Bubble Tea app, you can now get the
currently focused field, as well as the which option the cursor is pointing at, which we’re calling "hover":
f := huh.NewForm(
huh.NewGroup(
huh.NewSelect[string]().
Options(huh.NewOptions(
"Banana",
"Apple",
"Orange",
)...).
Title("Favorite fruit?"),
huh.NewMultiSelect[string]().
Options(huh.NewOptions(
"Pudim",
"Sagu",
"Chocolate",
)...).
Title("Favorite dessert?"),
),
)
_ = f.Run()
field := f.GetFocusedField()
switch field := field.(type) {
case *huh.Select[string]:
fmt.Println(field.Hovered())
case *huh.MultiSelect[string]:
fmt.Println(field.Hovered())
}
You can also use GetFiltering
to check if the user is currently filtering.
Spinning, but not out of control
Spinner
was revamped and now properly handles context cancellations,
interrupts, and more. You can also use the new ActionWithError
to set an action that might error.
Other improvements
FilePicker
got a couple of improvements: you can now set theCursor
, and the UI has received a bit of extra polish.Group
now properly renders theirTitle
andDescription
.Text
can now be configured to not allow opening the external editor.Select
andMultiSelect
now properly handle multi-line options, as well as automatically wrap long options so they are properly rendered.Title
andDescription
of all components now properly wrap, fixing many rendering issues.- Both
Column
andGrid
layouts received bug fixes and improvements. - Interrupt signals (
SIGINT
) are now properly handled.
Changelog
- feat(accessibility): prompt improvements by @caarlos0 in #620
- feat(input): Skip validation when going to previous field by @Sculas in #285
- feat(spinner): improve context, action, output, tests by @caarlos0 in #292
- feat: Add Cursor() methods to Select and MultiSelect fields by @kralicky in #407
- feat: add
WithButtonAlignment
function for button positioning by @bashbunni in #427 - feat: add cursor option to filepicker by @radar07 in #400
- feat: expose focused field by @jonas-grgt in #503
- feat: group titles and descriptions by @caarlos0 in #548
- feat: method to check filtering state of select fields by @jon4hz in #524
- feat: option to disable external editor in Text field by @rahji in #516
- fix rendering of dynamic fields in groups by @zhammer in #505
- fix(accessibility): parseBool defaults to no by @jaredallard in #442
- fix(accessibility): stop scanning on EOF from stdin by @aybabtme in #440
- fix(filepicker): auto-height by @caarlos0 in #464
- fix(filepicker): height, navigation by @caarlos0 in #593
- fix(filepicker): set padding and width to avoid cluttering by @caarlos0 in #465
- fix(select,multiselect): multi line items, scrolling by @caarlos0 in #569
- fix: accessible mode IO, default values, and more by @caarlos0 in #614
- fix: form and group styles by @caarlos0 in #567
- fix: height and width calculation improvements, wrapping by @caarlos0 in #573
- fix: ignore next input bug on Windows by @awoodbeck in #520
- fix: improve timeout by @caarlos0 in #600
- fix: properly handle interrupts by @caarlos0 in #491
- fix: stop using viewport deprecated methods by @caarlos0 in #621
- fix: wrap titles and descriptions when needed by @caarlos0 in #570
- Fixed README GetString to GetInt for bubbletea form example by @hegner123 in #574
- Fixed comment to match function by @hegner123 in #580
- docs(examples): fix typo by @bashbunni in #506
- docs(examples): various small corrections by @meowgorithm in #602
- docs: add example of Dynamic Huh? inside Bubble Tea application by @maaslalani in #347
- Update README.md by @chastain in #575
- chore: basic repo files maintenance by @andreynering in #616
- chore: update github templates by @bashbunni in #603
New Contributors
- @radar07 made their first contribution in #400
- @aybabtme made their first contribution in #440
- @jaredallard made their first contribution in #442
- @awoodbeck made their first contribution in #520
- @jon4hz made their first contribution in #524
- @charmcli made their first contribution in #535
- @jonas-grgt made their first contribution in #503
- @rahji made their first contribution in #516
- @zhammer made their first contribution in #505
- @kralicky made their first contribution in #407
- @hegner123 made their first contribution in #574
- @chastain made their first contribution in #575
- @github-actions made their first contribution in #608
- @andreynering made their first contribution in #616
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.6.0
Just focus
This release features automatic window focus events support for the Input
and Text
components.
Note that if you're using Huh in a larger Bubble Tea program you’ll need WithReportFocus
to enable focus events.
p := tea.NewProgram(model{}, tea.WithReportFocus())
Happy focusing!
Changelog
New!
Full Changelog: v0.5.3...v0.6.0
v0.5.3
Crushin’ bugs
This release fixes a buncha bugs in Huh and Gum alike. Gum users, stay tuned for an update later today.
What's Changed
Fixed
- fix: group varying heights by @bashbunni in #350
- fix: group size when height is 0 by @caarlos0 in #363
- fix(filepicker): not closing on close by @caarlos0 in #323
- fix(select): wrapping selects by @caarlos0 in #324
- fix(select): select all/none by @caarlos0 in #325
- fix(multiselect): contextual 'select all' help text by @meowgorithm in #342
- fix(spinner): light color in title by @hyorigo in #346
- fix (multiselect): set filterable not working by @caarlos0 in #335
- fix(lint): golangci-lint issues by @caarlos0 in #378
Other Stuff
- chore: update codeowners by @caarlos0 in #379
- refactor: use a selector type to select items by @aymanbagabas in #328
- refactor: make item selector into a slice container by @aymanbagabas in #334
New Contributors
- @bashbunni made their first contribution in #350
- @hyorigo made their first contribution in #346
Full Changelog: v0.5.2...v0.5.3
v0.5.2
Lil’ fixes ’n’ improvements
Hi! This is a maintenance release to fix issues with dynamic forms as well as address issues with Gum upstream.
Changelog
New
- add accept and reject key bindings to confirm by @csandeep in #308
- allow Generic
T
updateOptionsMsg by @maaslalani in #318
Fixed
- improve distinction in field select by @csandeep in #304
- fix invalid order of event handling in input/text fields by @Sculas in #284
New Contributors
- @csandeep made their first contribution in #304
- @MaximilianSoerenPollak made their first contribution in #313
Full Changelog: v0.5.1...v0.5.2
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.5.0
This big news in this release is that forms in Huh can now be dynamic. Read on for more:
Dynamic Forms 🪄
huh?
forms can now react to changes in other parts of the form. Replace properties such as Options
, Title
, Description
with their dynamic counterparts: OptionsFunc
, TitleFunc
, and DescriptionFunc
to recompute properties values on changes when watched variables change.
Let’s build a simple state / province picker.
var country string
var state string
The country
select will be static, we’ll use this value to recompute the
options and title for the next input.
huh.NewSelect[string]().
Options(huh.NewOptions("United States", "Canada", "Mexico")...).
Value(&country).
Title("Country").
Define your Select
with TitleFunc
and OptionsFunc
and bind them to the
&country
value from the previous field. Whenever the user chooses a different
country, the TitleFunc
and OptionsFunc
will be recomputed.
Important
We have to pass &country
as the binding to recompute the function only when
country
changes, otherwise we will hit the API too often.
huh.NewSelect[string]().
Value(&state).
Height(8).
TitleFunc(func() string {
switch country {
case "United States":
return "State"
case "Canada":
return "Province"
default:
return "Territory"
}
}, &country).
OptionsFunc(func() []huh.Option[string] {
opts := fetchStatesForCountry(country)
return huh.NewOptions(opts...)
}, &country),
Lastly, run the form
with these inputs.
err := form.Run()
if err != nil {
log.Fatal(err)
}
Changelog
New!
- Form Layouts by @adamdottv in #274
CursorText
style by @nervo in #259WithInput
by @Delta456 in #271WithTimeout
by @Delta456 in #276- Introduce
accessor
by @nervo in #263 - Introduce
accessor
by @nervo in #263 - Set filtering state of select on init by @PJGaetan in #179
Fixed
- Aborting a form returned a timeout error by @Sculas in #287
- Resolve conflict between select and filter by @MikaelFangel in #252
- Adjust input width to char limit by @nervo in #260
New Contributors
- @MikaelFangel made their first contribution in #252
- @nervo made their first contribution in #253
- @shedyfreak made their first contribution in #230
- @Delta456 made their first contribution in #271
- @abradley2 made their first contribution in #280
- @PJGaetan made their first contribution in #179
- @Sculas made their first contribution in #287
Full Changelog: v0.4.2...v0.5.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.4.2
Your favourite files 📁
Huh? v0.4.0
(v0.4.2
) introduces the File Picker to forms ✨
Prompt users to select a file in just a few lines of code, you know the drill :)
var file string
huh.NewFilePicker().
Title("Select a file:").
Description("This will be your profile image.").
AllowedTypes([]string{".png", ".jpeg", ".webp", ".gif"}).
Value(&file)
Field Interface
Zoom
File pickers introduce a new Zoom
method to the Field
interface.
type Field interface {
// ...
Zoom() bool
// ...
}
Zoom
tells the form whether this field should be the only visible field.
Notice how the FilePicker
field zooms in when selecting a file and zooms out when the file is selected, showing only the selected file.
Skip
Notes introduce a new Skip
method to the Field
interface.
type Field interface {
// ...
Skip() bool
// ...
}
Skip indicates to the form whether or not to skip this field, i.e. don't let the user interact with it. This is useful for notes to be purely informational.
What else?
- feat: set
height
inWithHeight
by @ardnew in #122 - feat: use
EchoMode
forInput
by @caarlos0 in #131 - feat: zoom
FilePicker
by @maaslalani in #133 - feat: inline
Select
by @maaslalani in #136 - feat: ctrl+u, ctrl+d, g, and G keybindings on
Select
by @caarlos0 in #143 - feat: predefined
ValidationFunc
s by @anirudhaCodes in #140 - feat: make
PrevField
andNextField
public by @KevM in #175 - feat: accessible mode when
TERM=dumb
by @maaslalani in #188 - feat:
WithOutput
API by @maaslalani in #201 - feat: set note next button label by @abtmr in #225
Bug Fixes
- fix: overlapping style issue in render function of
field_note
by @anirudhaCodes in #112 - fix:
MultiSelect
limit in Accessible Mode by @anirudhaCodes in #125 - fix: CharLimit of
Text
field in Accessible mode by @anirudhaCodes in #126 - fix: set themes on fields by @maaslalani in #219
- fix: remove temporary file after opening external editor in
Text
field by @auvred in #154 - fix: spinner bubbles up
tea.Program
errors by @clowder in #237
New Contributors
A special thanks to everyone who made this release of Huh? possible! 🤗
- @theredditbandit made their first contribution in #102
- @anirudhaCodes made their first contribution in #112
- @rharshit82 made their first contribution in #128
- @stefanlogue made their first contribution in #99
- @auvred made their first contribution in #154
- @KevM made their first contribution in #175
- @zimeg made their first contribution in #224
- @bradyjoslin made their first contribution in #241
- @joshi4 made their first contribution in #232
- @abtmr made their first contribution in #225
- @clowder made their first contribution in #237
Full Changelog: v0.3.0...v0.4.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Slack.
v0.3.0
Scrolling, autocomplete, smaller binaries and more!
This is a big release with a tonne of new features.
- Scrollable Forms
- Scrollable Selects and Multi selects
- Autocomplete for inputs
- 7x smaller binaries
- Multi select filtering
- Lotsa' bugfixes and quality-of-life improvements
To upgrade to huh
v0.3.0:
go get -u github.com/charmbracelet/huh@latest
For details read on!
Scrollable forms
If a form is in a small terminal it will automatically resize to fit the available space and the active group will scroll to stay in view. Form heights can also be set manually with the new WithHeight
method.
Select and Multi select Scrolling
Select and Multi select fields can now be restricted to a certain height, allowing their options to be scrollable. This means you can now pack in tonnes of options.
To make a Select
or MultiSelect
scrollable simply set the height on the field or form through the Height
method.
s := huh.NewSelect()
.Title("What’s for dinner?")
.Options(/* ... */)
.Height(height)
Autocomplete
Now Input
s can offer suggestions making it easier for users to fill out inputs. These suggestions can be accepted autocomplete-style with a configurable key binding (which defaults to ctrl+e).
Simply provide a []string
to Suggestions
to enable this feature.
huh.NewInput().
Title("What's for lunch?").
Prompt("? ").
Suggestions([]string{
"Artichoke",
// ...
"Cashew Apple",
"Cashews",
"Cat Food",
"Coconut Milk",
"Cucumber",
"Curry Paste",
"Currywurst",
// ...
})
More helpful help
Forms will automatically adjust their help text to indicate to the user whether the form will continue or submit on actions. This works with hidden groups. In the below example, the user will be asked to list their allergies if they select "Yes" otherwise, the form will submit.
Way smaller binaries
Huh now produces way smaller binaries! Thanks to #94 Huh now has a 7x smaller compiled footprint.
Before, using [email protected]
:
33M ./burger
After, using [email protected]
:
4.5M ./burger
Thanks, Vitor!
Special thanks to the intrepid @vitor-mariano, one of the earliest huh
contributors, who came in hot with features, fixes, improvements and good vibes. Thank you, Vitor!
Changelog
New
- scrollable multi-select by @meowgorithm in #71
- scrollable select by @meowgorithm in #76
- feat: enable filtering on MultiSelect by @vitor-mariano in #81
- skippable Fields by @maaslalani in #116
- autocomplete suggestions on inputs by @maaslalani in #93
- scroll form Inputs by @maaslalani in #95
- next / previous dynamic help by @maaslalani in #104
- reduce binary size by @maaslalani in #94
Fixed
- fix Select and MultiSelect height when unfocused by @vitor-mariano in #80
- use CharLimit in textinput by @maaslalani in #79
- select viewport on filtering by @vitor-mariano in #84
- set default theme on inputs by @maaslalani in #92
- multiselect: set height in WithHeight by @ardnew in #118
- shift+tab would fail if first group is hidden by @caarlos0 in #103
- prevField should not select skippable fields by @maaslalani in #121
Full Changelog: v0.2.3...v0.3.0
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.
v0.2.3
Better Defaults!
Huh? v0.2.3
fixes some bugs for more consistent behaviour across inputs 🐞
Text
inputs now update values on each keystroke rather than onBlur
for consistency.Select
andMultiSelect
read their default values from the initial values set by theValue
variable if provided.
A special thanks to @vitor-mariano for all his contributions to this release 🤗
Defaults Example
You can now have preselected options by declaring them in the Value
variable:
var toppings = []string{"Lettuce", "Tomatoes"}
var options = huh.NewOptions("Lettuce", "Tomatoes", "Charm Sauce", "Cheese", "Vegan Cheese")
huh.NewMultiSelect[string]().Title("Toppings").Options(options...).Value(&toppings).Run()
In the above example, Lettuce
and Tomatoes
will be preselected by default.
What's Changed
Text
value updates by @vitor-mariano in #64- Prefill
Select
andMultiSelect
inputs with default values by @vitor-mariano in #62
Full Changelog: v0.2.2...v0.2.3
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or on Discord.
v0.2.2
Better Wrapping 🎁
Huh?
v0.2.2 improves wrapping of the Input
and Text
fields.
It applies width set on the form to the inputs to perform wrapping based on the specified or terminal width.
- Fixed in #60
New Contributors
- @ddddddO made their first contribution in #56
- @vitor-mariano made their first contribution in #57
Full Changelog: v0.2.1...v0.2.2
Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.
v0.2.1
New Theme! 😸
huh?
forms can now use Catppuccin themes. (Thanks to the wonderful @sgoudham ✨)
Simply add the following to your huh.Form
s:
.WithTheme(huh.ThemeCatppuccin())

Thoughts? Questions? We love hearing from you. Feel free to reach out on Twitter, The Fediverse, or Discord.