#+title: Gummies
A Go library for embedding interactive TUI prompts in your CLI applications.
* Why it exists
[[https://github.com/charmbracelet/gum][Gum]], by Charm, is a fantastic CLI tool that brings polished TUI interactions to
shell scripts. Unfortunately, its TUI components were written to run as a
standalone executable rather than as importable library code.
Gummies extracts that core TUI functionality into a proper Go library so you can
drop interactive prompts into any Go program without shelling out to an external
process.
We've expanded the capabilities of the =Choose= widget, and will probably add new
features over time.
* What it provides
All functions write their UI to =stderr= so that =stdout= remains clean for
program output. Each function returns =ErrAborted= if the user cancels with
=ctrl+c= or =esc=.
** Choose
#+begin_src go
func Choose(options []string, limit int, instructions string, preselected ...string) ([]string, error)
#+end_src
Displays a scrollable, paginated selection list. The user navigates with the
arrow keys and submits with =enter=.
- Set =limit= to =1= for single-select (no toggle UI).
- Set =limit= to =0= for unlimited multi-select.
- Any positive =limit > 1= caps how many items can be selected.
*** Preselected items
Pass item values as trailing arguments to position the cursor on (single-select)
or pre-check (multi-select) specific items when the list opens.
#+begin_src go
// Single-select: cursor starts on "banana"
result, err := gummies.Choose(options, 1, "Pick one:", "banana")
// Multi-select: "banana" and "cherry" are pre-checked
results, err := gummies.Choose(options, 0, "Pick any:", "banana", "cherry")
#+end_src
Two rules apply:
- Every value in =preselected= must exist in =options=. Passing an unknown value
returns an error before the UI opens.
- When =limit > 0=, =len(preselected)= must not exceed =limit=. A limit of =0=
(unlimited) has no such restriction.
** Confirm
#+begin_src go
func Confirm(prompt string) (bool, error)
#+end_src
Displays a Yes / No prompt. Returns =true= for yes, =false= for no.
** Input
#+begin_src go
func Input(placeholder string, instructions string) (string, error)
func InputWithValue(placeholder string, instructions string, initialContent *string) (string, error)
#+end_src
Displays a single-line text input. =InputWithValue= pre-fills the field with
the value pointed to by =initialContent=.
** Write
#+begin_src go
func Write(placeholder string, instructions string, initialContent *string) (string, error)
#+end_src
Displays a multi-line text editor. Press =enter= for new lines and =ctrl+d= to
submit. Pass a non-nil =initialContent= to pre-fill the buffer.
* Installation
#+begin_src sh
go get github.com/masukomi/gummies
#+end_src
* Usage
#+begin_src go
import "github.com/masukomi/gummies"
// Single-select list
selected, err := gummies.Choose([]string{"apple", "banana", "cherry"}, 1, "Pick a fruit:")
if errors.Is(err, gummies.ErrAborted) {
// user cancelled
}
// Single-select with cursor starting on "banana"
selected, err = gummies.Choose([]string{"apple", "banana", "cherry"}, 1, "Pick a fruit:", "banana")
// Multi-select list (up to 3)
picks, err := gummies.Choose(options, 3, "Choose up to 3:")
// Multi-select with "banana" and "cherry" pre-checked
picks, err = gummies.Choose(options, 0, "Choose any:", "banana", "cherry")
// Yes/No confirmation
ok, err := gummies.Confirm("Continue?")
// Single-line input
name, err := gummies.Input("e.g. myapp", "Enter a name:")
// Single-line input with a pre-filled value
edited, err := gummies.InputWithValue("", "Edit the title:", &existingTitle)
// Multi-line input
body, err := gummies.Write("", "Enter a description (ctrl+d to submit):", nil)
#+end_src
* License
Gummies is distributed under the MIT license, and it should be noted that the folks over at Charm are the ones who did all the hard thinking here.