feat: gum confirm

This commit is contained in:
Maas Lalani 2022-07-26 15:43:26 -04:00
parent 483da45749
commit 108a680385
No known key found for this signature in database
GPG key ID: 5A6ED5CBF1A0A000
7 changed files with 163 additions and 15 deletions

30
confirm/command.go Normal file
View file

@ -0,0 +1,30 @@
package confirm
import (
"os"
tea "github.com/charmbracelet/bubbletea"
)
// Run provides a shell script interface for prompting a user to confirm an
// action with an affirmative or negative answer.
func (o Options) Run() error {
m, err := tea.NewProgram(model{
affirmative: o.Affirmative,
negative: o.Negative,
vertical: o.Vertical,
selected: 0,
prompt: o.Prompt,
selectedStyle: o.SelectedStyle.ToLipgloss(),
unselectedStyle: o.UnselectedStyle.ToLipgloss(),
promptStyle: o.PromptStyle.ToLipgloss(),
}, tea.WithOutput(os.Stderr)).StartReturningModel()
if err != nil {
return err
}
os.Exit(m.(model).selected)
return nil
}

84
confirm/confirm.go Normal file
View file

@ -0,0 +1,84 @@
// Package confirm provides an interface to ask a user to confirm an action.
// The user is provided with an interface to choose an affirmative or negative
// answer, which is then reflected in the exit code for use in scripting.
//
// If the user selects the affirmative answer, the program exits with 0. If the
// user selects the negative answer, the program exits with 1.
//
// I.e. confirm if the user wants to delete a file
//
// $ gum confirm "Are you sure?" && rm file.txt
//
package confirm
import (
tea "github.com/charmbracelet/bubbletea"
"github.com/charmbracelet/lipgloss"
)
type model struct {
prompt string
affirmative string
negative string
vertical bool
quitting bool
selected int
// styles
promptStyle lipgloss.Style
selectedStyle lipgloss.Style
unselectedStyle lipgloss.Style
}
func (m model) Init() tea.Cmd { return nil }
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
switch msg := msg.(type) {
case tea.WindowSizeMsg:
return m, nil
case tea.KeyMsg:
switch msg.String() {
case "left", "h", "ctrl+p":
m.selected = m.selected - 1
if m.selected < 0 {
m.selected = 1
}
case "right", "l", "ctrl+n":
m.selected = m.selected + 1
if m.selected > 1 {
m.selected = 0
}
case "enter":
m.quitting = true
return m, tea.Quit
case "ctrl+c", "q":
m.selected = 1
m.quitting = true
return m, tea.Quit
}
}
return m, nil
}
func (m model) View() string {
if m.quitting {
return ""
}
joinFunc := lipgloss.JoinHorizontal
if m.vertical {
joinFunc = lipgloss.JoinVertical
}
var aff, neg string
if m.selected == 0 {
aff = m.selectedStyle.Render(m.affirmative)
neg = m.unselectedStyle.Render(m.negative)
} else {
aff = m.unselectedStyle.Render(m.affirmative)
neg = m.selectedStyle.Render(m.negative)
}
return lipgloss.JoinVertical(lipgloss.Center, m.promptStyle.Render(m.prompt), joinFunc(lipgloss.Left, aff, neg))
}

14
confirm/options.go Normal file
View file

@ -0,0 +1,14 @@
package confirm
import "github.com/charmbracelet/gum/style"
// Options is the customization options for the confirm command.
type Options struct {
Affirmative string `help:"The title of the affirmative action" default:"Yes"`
Negative string `help:"The title of the negative action" default:"No"`
Prompt string `arg:"" help:"Prompt to display." default:"Are you sure?"`
Vertical bool `help:"Whether to display the options vertically" default:"false"`
PromptStyle style.Styles `embed:"" prefix:"prompt." help:"The style of the prompt"`
UnselectedStyle style.Styles `embed:"" prefix:"unselected." help:"The style of the unselected action" set:"defaultBackground=0" set:"defaultPadding=0 3" set:"defaultMargin=1 1"`
SelectedStyle style.Styles `embed:"" prefix:"selected." help:"The style of the selected action" set:"defaultBackground=8" set:"defaultForeground=212" set:"defaultPadding=0 3" set:"defaultMargin=1 1"`
}

15
gum.go
View file

@ -3,6 +3,7 @@ package main
import (
"github.com/charmbracelet/gum/choose"
"github.com/charmbracelet/gum/completion"
"github.com/charmbracelet/gum/confirm"
"github.com/charmbracelet/gum/filter"
"github.com/charmbracelet/gum/format"
"github.com/charmbracelet/gum/input"
@ -34,6 +35,20 @@ type Gum struct {
//
Choose choose.Options `cmd:"" help:"Choose an option from a list of choices"`
// Confirm provides an interface to ask a user to confirm an action.
// The user is provided with an interface to choose an affirmative or
// negative answer, which is then reflected in the exit code for use in
// scripting.
//
// If the user selects the affirmative answer, the program exits with 0.
// If the user selects the negative answer, the program exits with 1.
//
// I.e. confirm if the user wants to delete a file
//
// $ gum confirm "Are you sure?" && rm file.txt
//
Confirm confirm.Options `cmd:"" help:"Ask a user to confirm an action"`
// Filter provides a fuzzy searching text input to allow filtering a list of
// options to select one option.
//

View file

@ -25,6 +25,9 @@ func main() {
kong.Vars{
"defaultBackground": "",
"defaultForeground": "",
"defaultMargin": "0 0",
"defaultPadding": "0 0",
"defaultUnderline": "false",
},
)
ctx.Run()

View file

@ -22,5 +22,6 @@ func (s Styles) ToLipgloss() lipgloss.Style {
Bold(s.Bold).
Faint(s.Faint).
Italic(s.Italic).
Strikethrough(s.Strikethrough)
Strikethrough(s.Strikethrough).
Underline(s.Underline)
}

View file

@ -18,24 +18,25 @@ type Options struct {
// components, through embedding and prefixing.
type Styles struct {
// Colors
Background string `help:"Background color of the ${name=element}" default:"${defaultBackground}" group:"Style Flags"`
Foreground string `help:"color of the ${name=element}" default:"${defaultForeground}" group:"Style Flags"`
Background string `help:"Background Color" default:"${defaultBackground}" group:"Style Flags"`
Foreground string `help:"Foreground Color" default:"${defaultForeground}" group:"Style Flags"`
// Border
Border string `help:"Border style to apply" enum:"none,hidden,normal,rounded,thick,double" default:"none" group:"Style Flags"`
BorderBackground string `help:"Border background color" group:"Style Flags"`
BorderForeground string `help:"Border foreground color" group:"Style Flags"`
Border string `help:"Border Style" enum:"none,hidden,normal,rounded,thick,double" default:"none" group:"Style Flags"`
BorderBackground string `help:"Border Background Color" group:"Style Flags"`
BorderForeground string `help:"Border Foreground Color" group:"Style Flags"`
// Layout
Align string `help:"Text alignment" enum:"left,center,right,bottom,middle,top" default:"left" group:"Style Flags"`
Height int `help:"Height of output" group:"Style Flags"`
Width int `help:"Width of output" group:"Style Flags"`
Margin string `help:"Margin to apply around the text." default:"0 0" group:"Style Flags"`
Padding string `help:"Padding to apply around the text." default:"0 0"`
Align string `help:"Text Alignment" enum:"left,center,right,bottom,middle,top" default:"left" group:"Style Flags"`
Height int `help:"Text height" group:"Style Flags"`
Width int `help:"Text width" group:"Style Flags"`
Margin string `help:"Text margin" default:"${defaultMargin}" group:"Style Flags"`
Padding string `help:"Text padding" default:"${defaultPadding}" group:"Style Flags"`
// Format
Bold bool `help:"Apply bold formatting" group:"Style Flags"`
Faint bool `help:"Apply faint formatting" group:"Style Flags"`
Italic bool `help:"Apply italic formatting" group:"Style Flags"`
Strikethrough bool `help:"Apply strikethrough formatting" group:"Style Flags"`
Bold bool `help:"Bold text" group:"Style Flags"`
Faint bool `help:"Faint text" group:"Style Flags"`
Italic bool `help:"Italicize text" group:"Style Flags"`
Strikethrough bool `help:"Strikethrough text" group:"Style Flags"`
Underline bool `help:"Underline text" default:"${defaultUnderline}" group:"Style Flags"`
}