mirror of
https://github.com/charmbracelet/gum
synced 2026-03-14 13:45:45 +01:00
feat: adding --padding to most commands (#960)
* feat(filter,choose): allow UI to be padded * feat: --padding everywhere Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: unrelated lint issue Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: filter Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: use ordered.Clamp Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> --------- Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> Co-authored-by: Christian Rocha <christian@rocha.is>
This commit is contained in:
parent
09940da8c0
commit
6045525ab9
28 changed files with 132 additions and 81 deletions
|
|
@ -18,6 +18,7 @@ import (
|
|||
"github.com/charmbracelet/bubbles/paginator"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/x/exp/ordered"
|
||||
)
|
||||
|
||||
func defaultKeymap() keymap {
|
||||
|
|
@ -97,6 +98,7 @@ func (k keymap) ShortHelp() []key.Binding {
|
|||
|
||||
type model struct {
|
||||
height int
|
||||
padding []int
|
||||
cursor string
|
||||
selectedPrefix string
|
||||
unselectedPrefix string
|
||||
|
|
@ -157,10 +159,10 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
m.paginator.PrevPage()
|
||||
}
|
||||
case key.Matches(msg, km.Right):
|
||||
m.index = clamp(m.index+m.height, 0, len(m.items)-1)
|
||||
m.index = ordered.Clamp(m.index+m.height, 0, len(m.items)-1)
|
||||
m.paginator.NextPage()
|
||||
case key.Matches(msg, km.Left):
|
||||
m.index = clamp(m.index-m.height, 0, len(m.items)-1)
|
||||
m.index = ordered.Clamp(m.index-m.height, 0, len(m.items)-1)
|
||||
m.paginator.PrevPage()
|
||||
case key.Matches(msg, km.End):
|
||||
m.index = len(m.items) - 1
|
||||
|
|
@ -280,15 +282,8 @@ func (m model) View() string {
|
|||
parts = append(parts, "", m.help.View(m.keymap))
|
||||
}
|
||||
|
||||
return lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||
}
|
||||
|
||||
func clamp(x, low, high int) int {
|
||||
if x < low {
|
||||
return low
|
||||
}
|
||||
if x > high {
|
||||
return high
|
||||
}
|
||||
return x
|
||||
view := lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(view)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ import (
|
|||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/internal/tty"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
|
|
@ -107,6 +108,7 @@ func (o Options) Run() error {
|
|||
|
||||
// Use the pagination model to display the current and total number of
|
||||
// pages.
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
pager := paginator.New()
|
||||
pager.SetTotalPages((len(items) + o.Height - 1) / o.Height)
|
||||
pager.PerPage = o.Height
|
||||
|
|
@ -128,6 +130,7 @@ func (o Options) Run() error {
|
|||
index: startingIndex,
|
||||
currentOrder: currentOrder,
|
||||
height: o.Height,
|
||||
padding: []int{top, right, bottom, left},
|
||||
cursor: o.Cursor,
|
||||
header: o.Header,
|
||||
selectedPrefix: o.SelectedPrefix,
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ type Options struct {
|
|||
OutputDelimiter string `help:"Option delimiter when writing to STDOUT" default:"\n" env:"GUM_CHOOSE_OUTPUT_DELIMITER"`
|
||||
LabelDelimiter string `help:"Allows to set a delimiter, so options can be set as label:value" default:"" env:"GUM_CHOOSE_LABEL_DELIMITER"`
|
||||
StripANSI bool `help:"Strip ANSI sequences when reading from STDIN" default:"true" negatable:"" env:"GUM_CHOOSE_STRIP_ANSI"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_CHOOSE_PADDING"`
|
||||
|
||||
CursorStyle style.Styles `embed:"" prefix:"cursor." set:"defaultForeground=212" envprefix:"GUM_CHOOSE_CURSOR_"`
|
||||
HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=99" envprefix:"GUM_CHOOSE_HEADER_"`
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/charmbracelet/gum/internal/exit"
|
||||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
)
|
||||
|
||||
// Run provides a shell script interface for prompting a user to confirm an
|
||||
|
|
@ -28,6 +29,7 @@ func (o Options) Run() error {
|
|||
ctx, cancel := timeout.Context(o.Timeout)
|
||||
defer cancel()
|
||||
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
m := model{
|
||||
affirmative: o.Affirmative,
|
||||
negative: o.Negative,
|
||||
|
|
@ -41,6 +43,7 @@ func (o Options) Run() error {
|
|||
selectedStyle: o.SelectedStyle.ToLipgloss(),
|
||||
unselectedStyle: o.UnselectedStyle.ToLipgloss(),
|
||||
promptStyle: o.PromptStyle.ToLipgloss(),
|
||||
padding: []int{top, right, bottom, left},
|
||||
}
|
||||
tm, err := tea.NewProgram(
|
||||
m,
|
||||
|
|
|
|||
|
|
@ -91,6 +91,7 @@ type model struct {
|
|||
promptStyle lipgloss.Style
|
||||
selectedStyle lipgloss.Style
|
||||
unselectedStyle lipgloss.Style
|
||||
padding []int
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd { return nil }
|
||||
|
|
@ -149,18 +150,19 @@ func (m model) View() string {
|
|||
neg = ""
|
||||
}
|
||||
|
||||
if m.showHelp {
|
||||
return lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
m.promptStyle.Render(m.prompt)+"\n",
|
||||
lipgloss.JoinHorizontal(lipgloss.Left, aff, neg),
|
||||
"\n"+m.help.View(m.keys),
|
||||
)
|
||||
parts := []string{
|
||||
m.promptStyle.Render(m.prompt) + "\n",
|
||||
lipgloss.JoinHorizontal(lipgloss.Left, aff, neg),
|
||||
}
|
||||
|
||||
return lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
m.promptStyle.Render(m.prompt)+"\n",
|
||||
lipgloss.JoinHorizontal(lipgloss.Left, aff, neg),
|
||||
)
|
||||
if m.showHelp {
|
||||
parts = append(parts, "", m.help.View(m.keys))
|
||||
}
|
||||
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
parts...,
|
||||
))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,4 +21,5 @@ type Options struct {
|
|||
UnselectedStyle style.Styles `embed:"" prefix:"unselected." help:"The style of the unselected action" set:"defaultBackground=235" set:"defaultForeground=254" set:"defaultPadding=0 3" set:"defaultMargin=0 1" envprefix:"GUM_CONFIRM_UNSELECTED_"`
|
||||
ShowHelp bool `help:"Show help key binds" negatable:"" default:"true" env:"GUM_CONFIRM_SHOW_HELP"`
|
||||
Timeout time.Duration `help:"Timeout until confirm returns selected value or default if provided" default:"0s" env:"GUM_CONFIRM_TIMEOUT"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_CONFIRM_PADDING"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import (
|
|||
"github.com/charmbracelet/bubbles/help"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
)
|
||||
|
||||
// Run is the interface to picking a file.
|
||||
|
|
@ -46,8 +47,10 @@ func (o Options) Run() error {
|
|||
fp.Styles.Permission = o.PermissionsStyle.ToLipgloss()
|
||||
fp.Styles.Selected = o.SelectedStyle.ToLipgloss()
|
||||
fp.Styles.FileSize = o.FileSizeStyle.ToLipgloss()
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
m := model{
|
||||
filepicker: fp,
|
||||
padding: []int{top, right, bottom, left},
|
||||
showHelp: o.ShowHelp,
|
||||
help: help.New(),
|
||||
keymap: defaultKeymap(),
|
||||
|
|
|
|||
12
file/file.go
12
file/file.go
|
|
@ -59,6 +59,7 @@ type model struct {
|
|||
selectedPath string
|
||||
quitting bool
|
||||
showHelp bool
|
||||
padding []int
|
||||
help help.Model
|
||||
keymap keymap
|
||||
}
|
||||
|
|
@ -68,9 +69,11 @@ func (m model) Init() tea.Cmd { return m.filepicker.Init() }
|
|||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
height := msg.Height - m.padding[0] - m.padding[2]
|
||||
if m.showHelp {
|
||||
m.filepicker.Height -= lipgloss.Height(m.helpView()) //nolint:staticcheck
|
||||
height -= lipgloss.Height(m.helpView())
|
||||
}
|
||||
m.filepicker.SetHeight(height)
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, keyAbort):
|
||||
|
|
@ -103,7 +106,12 @@ func (m model) View() string {
|
|||
if m.showHelp {
|
||||
parts = append(parts, m.helpView())
|
||||
}
|
||||
return lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
parts...,
|
||||
))
|
||||
}
|
||||
|
||||
func (m model) helpView() string {
|
||||
|
|
|
|||
|
|
@ -30,4 +30,5 @@ type Options struct {
|
|||
SelectedStyle style.Styles `embed:"" prefix:"selected." help:"The style to use for the selected item" set:"defaultBold=true" set:"defaultForeground=212" envprefix:"GUM_FILE_SELECTED_"` //nolint:staticcheck
|
||||
FileSizeStyle style.Styles `embed:"" prefix:"file-size." help:"The style to use for file sizes" set:"defaultWidth=8" set:"defaultAlign=right" set:"defaultForeground=240" envprefix:"GUM_FILE_FILE_SIZE_"` //nolint:staticcheck
|
||||
HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=99" envprefix:"GUM_FILE_HEADER_"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_FILE_PADDING"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import (
|
|||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/internal/tty"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
"github.com/charmbracelet/x/ansi"
|
||||
"github.com/sahilm/fuzzy"
|
||||
)
|
||||
|
|
@ -94,7 +95,7 @@ func (o Options) Run() error {
|
|||
km.ToggleAndNext.SetEnabled(true)
|
||||
km.ToggleAll.SetEnabled(true)
|
||||
}
|
||||
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
m := model{
|
||||
choices: choices,
|
||||
filteringChoices: filteringChoices,
|
||||
|
|
@ -113,6 +114,7 @@ func (o Options) Run() error {
|
|||
textStyle: o.TextStyle.ToLipgloss(),
|
||||
cursorTextStyle: o.CursorTextStyle.ToLipgloss(),
|
||||
height: o.Height,
|
||||
padding: []int{top, right, bottom, left},
|
||||
selected: make(map[string]struct{}),
|
||||
limit: o.Limit,
|
||||
reverse: o.Reverse,
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import (
|
|||
"github.com/charmbracelet/bubbles/viewport"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/x/exp/ordered"
|
||||
"github.com/rivo/uniseg"
|
||||
"github.com/sahilm/fuzzy"
|
||||
)
|
||||
|
|
@ -137,6 +138,7 @@ type model struct {
|
|||
selectedPrefix string
|
||||
unselectedPrefix string
|
||||
height int
|
||||
padding []int
|
||||
quitting bool
|
||||
headerStyle lipgloss.Style
|
||||
matchStyle lipgloss.Style
|
||||
|
|
@ -230,33 +232,35 @@ func (m model) View() string {
|
|||
|
||||
m.viewport.SetContent(s.String())
|
||||
|
||||
help := ""
|
||||
if m.showHelp {
|
||||
help = m.helpView()
|
||||
}
|
||||
|
||||
// View the input and the filtered choices
|
||||
header := m.headerStyle.Render(m.header)
|
||||
if m.reverse {
|
||||
view := m.viewport.View() + "\n" + m.textinput.View()
|
||||
if m.showHelp {
|
||||
view += help
|
||||
}
|
||||
view := m.viewport.View()
|
||||
if m.header != "" {
|
||||
return lipgloss.JoinVertical(lipgloss.Left, view, header)
|
||||
view += "\n" + header
|
||||
}
|
||||
|
||||
return view
|
||||
view += "\n" + m.textinput.View()
|
||||
if m.showHelp {
|
||||
view += m.helpView()
|
||||
}
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(view)
|
||||
}
|
||||
|
||||
view := m.textinput.View() + "\n" + m.viewport.View()
|
||||
if m.showHelp {
|
||||
view += help
|
||||
view += m.helpView()
|
||||
}
|
||||
if m.header != "" {
|
||||
return lipgloss.JoinVertical(lipgloss.Left, header, view)
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(header + "\n" + view)
|
||||
}
|
||||
return view
|
||||
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(view)
|
||||
}
|
||||
|
||||
func (m model) helpView() string {
|
||||
|
|
@ -279,10 +283,11 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
if m.showHelp {
|
||||
m.viewport.Height = m.viewport.Height - lipgloss.Height(m.helpView())
|
||||
}
|
||||
m.viewport.Width = msg.Width
|
||||
m.textinput.Width = msg.Width
|
||||
m.viewport.Height = m.viewport.Height - m.padding[0] - m.padding[2]
|
||||
m.viewport.Width = msg.Width - m.padding[1] - m.padding[3]
|
||||
m.textinput.Width = msg.Width - m.padding[1] - m.padding[3]
|
||||
if m.reverse {
|
||||
m.viewport.YOffset = clamp(0, len(m.matches), len(m.matches)-m.viewport.Height)
|
||||
m.viewport.YOffset = ordered.Clamp(len(m.matches)-m.viewport.Height, 0, len(m.matches))
|
||||
}
|
||||
case tea.KeyMsg:
|
||||
km := m.keymap
|
||||
|
|
@ -374,7 +379,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
// it remains at a constant position relative to the cursor.
|
||||
if m.reverse {
|
||||
maxYOffset := max(0, len(m.matches)-m.viewport.Height)
|
||||
m.viewport.YOffset = clamp(0, maxYOffset, len(m.matches)-yOffsetFromBottom)
|
||||
m.viewport.YOffset = ordered.Clamp(len(m.matches)-yOffsetFromBottom, 0, maxYOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -388,7 +393,7 @@ func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|||
|
||||
// It's possible that filtering items have caused fewer matches. So, ensure
|
||||
// that the selected index is within the bounds of the number of matches.
|
||||
m.cursor = clamp(0, len(m.matches)-1, m.cursor)
|
||||
m.cursor = ordered.Clamp(m.cursor, 0, len(m.matches)-1)
|
||||
return m, tea.Batch(cmd, icmd)
|
||||
}
|
||||
|
||||
|
|
@ -499,16 +504,6 @@ func exactMatches(search string, choices []string) []fuzzy.Match {
|
|||
return matches
|
||||
}
|
||||
|
||||
func clamp(low, high, val int) int {
|
||||
if val < low {
|
||||
return low
|
||||
}
|
||||
if val > high {
|
||||
return high
|
||||
}
|
||||
return val
|
||||
}
|
||||
|
||||
func matchedRanges(in []int) [][2]int {
|
||||
if len(in) == 0 {
|
||||
return [][2]int{}
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@ type Options struct {
|
|||
InputDelimiter string `help:"Option delimiter when reading from STDIN" default:"\n" env:"GUM_FILTER_INPUT_DELIMITER"`
|
||||
OutputDelimiter string `help:"Option delimiter when writing to STDOUT" default:"\n" env:"GUM_FILTER_OUTPUT_DELIMITER"`
|
||||
StripANSI bool `help:"Strip ANSI sequences when reading from STDIN" default:"true" negatable:"" env:"GUM_FILTER_STRIP_ANSI"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_FILTER_PADDING"`
|
||||
|
||||
// Deprecated: use [FuzzySort]. This will be removed at some point.
|
||||
Sort bool `help:"Sort fuzzy results by their scores" default:"true" env:"GUM_FILTER_FUZZY_SORT" negatable:"" hidden:""`
|
||||
|
|
|
|||
1
go.mod
1
go.mod
|
|
@ -15,6 +15,7 @@ require (
|
|||
github.com/charmbracelet/log v0.4.2
|
||||
github.com/charmbracelet/x/ansi v0.10.1
|
||||
github.com/charmbracelet/x/editor v0.1.0
|
||||
github.com/charmbracelet/x/exp/ordered v0.1.0
|
||||
github.com/charmbracelet/x/term v0.2.1
|
||||
github.com/charmbracelet/x/xpty v0.1.2
|
||||
github.com/muesli/roff v0.1.0
|
||||
|
|
|
|||
2
go.sum
2
go.sum
|
|
@ -44,6 +44,8 @@ github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86 h1:JSt3B+U9
|
|||
github.com/charmbracelet/x/errors v0.0.0-20240508181413-e8d8b6e2de86/go.mod h1:2P0UgXMEa6TsToMSuFqKFQR+fZTO9CNGUNokkPatT/0=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91 h1:payRxjMjKgx2PaCWLZ4p3ro9y97+TVLZNaRZgJwSVDQ=
|
||||
github.com/charmbracelet/x/exp/golden v0.0.0-20241011142426-46044092ad91/go.mod h1:wDlXFlCrmJ8J+swcL/MnGUuYnqgQdW9rhSD61oNMb6U=
|
||||
github.com/charmbracelet/x/exp/ordered v0.1.0 h1:55/qLwjIh0gL0Vni+QAWk7T/qRVP6sBf+2agPBgnOFE=
|
||||
github.com/charmbracelet/x/exp/ordered v0.1.0/go.mod h1:5UHwmG+is5THxMyCJHNPCn2/ecI07aKNrW+LcResjJ8=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf h1:rLG0Yb6MQSDKdB52aGX55JT1oi0P0Kuaj7wi1bLUpnI=
|
||||
github.com/charmbracelet/x/exp/slice v0.0.0-20250327172914-2fdc97757edf/go.mod h1:B3UgsnsBZS/eX42BlaNiJkD1pPOUa+oF1IYC6Yd2CEU=
|
||||
github.com/charmbracelet/x/term v0.2.1 h1:AQeHeLZ1OqSXhrAWpYUtZyX1T3zVxfpZuEQMIQaGIAQ=
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/charmbracelet/gum/cursor"
|
||||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
)
|
||||
|
||||
// Run provides a shell script interface for the text input bubble.
|
||||
|
|
@ -43,10 +44,12 @@ func (o Options) Run() error {
|
|||
i.EchoCharacter = '•'
|
||||
}
|
||||
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
m := model{
|
||||
textinput: i,
|
||||
header: o.Header,
|
||||
headerStyle: o.HeaderStyle.ToLipgloss(),
|
||||
padding: []int{top, right, bottom, left},
|
||||
autoWidth: o.Width < 1,
|
||||
showHelp: o.ShowHelp,
|
||||
help: help.New(),
|
||||
|
|
|
|||
|
|
@ -38,6 +38,7 @@ func (k keymap) ShortHelp() []key.Binding {
|
|||
type model struct {
|
||||
autoWidth bool
|
||||
header string
|
||||
padding []int
|
||||
headerStyle lipgloss.Style
|
||||
textinput textinput.Model
|
||||
quitting bool
|
||||
|
|
@ -53,27 +54,30 @@ func (m model) View() string {
|
|||
if m.quitting {
|
||||
return ""
|
||||
}
|
||||
var parts []string
|
||||
if m.header != "" {
|
||||
header := m.headerStyle.Render(m.header)
|
||||
return lipgloss.JoinVertical(lipgloss.Left, header, m.textinput.View())
|
||||
parts = append(parts, m.headerStyle.Render(m.header))
|
||||
}
|
||||
|
||||
if !m.showHelp {
|
||||
return m.textinput.View()
|
||||
parts = append(parts, m.textinput.View())
|
||||
if m.showHelp {
|
||||
parts = append(parts, "", m.help.View(m.keymap))
|
||||
}
|
||||
return lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
m.textinput.View(),
|
||||
"",
|
||||
m.help.View(m.keymap),
|
||||
)
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(lipgloss.JoinVertical(
|
||||
lipgloss.Top,
|
||||
parts...,
|
||||
))
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
if m.autoWidth {
|
||||
m.textinput.Width = msg.Width - lipgloss.Width(m.textinput.Prompt) - 1
|
||||
m.textinput.Width = msg.Width - 1 -
|
||||
lipgloss.Width(m.textinput.Prompt) -
|
||||
m.padding[1] - m.padding[3]
|
||||
}
|
||||
case tea.KeyMsg:
|
||||
switch msg.String() {
|
||||
|
|
|
|||
|
|
@ -23,4 +23,5 @@ type Options struct {
|
|||
HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=240" envprefix:"GUM_INPUT_HEADER_"`
|
||||
Timeout time.Duration `help:"Timeout until input aborts" default:"0s" env:"GUM_INPUT_TIMEOUT"`
|
||||
StripANSI bool `help:"Strip ANSI sequences when reading from STDIN" default:"true" negatable:"" env:"GUM_INPUT_STRIP_ANSI"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_INPUT_PADDING"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ import (
|
|||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/gum/internal/exit"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
"github.com/charmbracelet/x/term"
|
||||
)
|
||||
|
||||
|
|
@ -20,6 +21,7 @@ func (o Options) Run() error {
|
|||
s := spinner.New()
|
||||
s.Style = o.SpinnerStyle.ToLipgloss()
|
||||
s.Spinner = spinnerMap[o.Spinner]
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
m := model{
|
||||
spinner: s,
|
||||
title: o.TitleStyle.ToLipgloss().Render(o.Title),
|
||||
|
|
@ -29,6 +31,7 @@ func (o Options) Run() error {
|
|||
showStderr: (o.ShowOutput || o.ShowStderr) && isErrTTY,
|
||||
showError: o.ShowError,
|
||||
isTTY: isErrTTY,
|
||||
padding: []int{top, right, bottom, left},
|
||||
}
|
||||
|
||||
ctx, cancel := timeout.Context(o.Timeout)
|
||||
|
|
|
|||
|
|
@ -20,4 +20,5 @@ type Options struct {
|
|||
TitleStyle style.Styles `embed:"" prefix:"title." envprefix:"GUM_SPIN_TITLE_"`
|
||||
Align string `help:"Alignment of spinner with regard to the title" short:"a" type:"align" enum:"left,right" default:"left" env:"GUM_SPIN_ALIGN"`
|
||||
Timeout time.Duration `help:"Timeout until spin command aborts" default:"0s" env:"GUM_SPIN_TIMEOUT"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_SPIN_PADDING"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import (
|
|||
|
||||
"github.com/charmbracelet/bubbles/spinner"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
"github.com/charmbracelet/x/term"
|
||||
"github.com/charmbracelet/x/xpty"
|
||||
)
|
||||
|
|
@ -32,6 +33,7 @@ import (
|
|||
type model struct {
|
||||
spinner spinner.Model
|
||||
title string
|
||||
padding []int
|
||||
align string
|
||||
command []string
|
||||
quitting bool
|
||||
|
|
@ -70,7 +72,7 @@ func commandStart(command []string) tea.Cmd {
|
|||
args = command[1:]
|
||||
}
|
||||
|
||||
executing = exec.Command(command[0], args...) //nolint:gosec
|
||||
executing = exec.CommandContext(context.Background(), command[0], args...) //nolint:gosec
|
||||
executing.Stdin = os.Stdin
|
||||
|
||||
isTerminal := term.IsTerminal(os.Stdout.Fd())
|
||||
|
|
@ -167,7 +169,9 @@ func (m model) View() string {
|
|||
} else {
|
||||
header = m.title + " " + m.spinner.View()
|
||||
}
|
||||
return header + "\n" + out
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(header, "", out)
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ func (s Styles) ToLipgloss() lipgloss.Style {
|
|||
Height(s.Height).
|
||||
Width(s.Width).
|
||||
Margin(parseMargin(s.Margin)).
|
||||
Padding(parsePadding(s.Padding)).
|
||||
Padding(ParsePadding(s.Padding)).
|
||||
Bold(s.Bold).
|
||||
Faint(s.Faint).
|
||||
Italic(s.Italic).
|
||||
|
|
@ -40,7 +40,7 @@ func (s StylesNotHidden) ToLipgloss() lipgloss.Style {
|
|||
Height(s.Height).
|
||||
Width(s.Width).
|
||||
Margin(parseMargin(s.Margin)).
|
||||
Padding(parsePadding(s.Padding)).
|
||||
Padding(ParsePadding(s.Padding)).
|
||||
Bold(s.Bold).
|
||||
Faint(s.Faint).
|
||||
Italic(s.Italic).
|
||||
|
|
|
|||
|
|
@ -11,9 +11,9 @@ const (
|
|||
maxTokens = 4
|
||||
)
|
||||
|
||||
// parsePadding parses 1 - 4 integers from a string and returns them in a top,
|
||||
// ParsePadding parses 1 - 4 integers from a string and returns them in a top,
|
||||
// right, bottom, left order for use in the lipgloss.Padding() method.
|
||||
func parsePadding(s string) (int, int, int, int) {
|
||||
func ParsePadding(s string) (int, int, int, int) {
|
||||
var ints [maxTokens]int
|
||||
|
||||
tokens := strings.Split(s, " ")
|
||||
|
|
@ -48,4 +48,4 @@ func parsePadding(s string) (int, int, int, int) {
|
|||
|
||||
// parseMargin is an alias for parsePadding since they involve the same logic
|
||||
// to parse integers to the same format.
|
||||
var parseMargin = parsePadding
|
||||
var parseMargin = ParsePadding
|
||||
|
|
|
|||
|
|
@ -79,6 +79,7 @@ func (o Options) Run() error {
|
|||
}
|
||||
|
||||
defaultStyles := table.DefaultStyles()
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
|
||||
styles := table.Styles{
|
||||
Cell: defaultStyles.Cell.Inherit(o.CellStyle.ToLipgloss()),
|
||||
|
|
@ -133,7 +134,7 @@ func (o Options) Run() error {
|
|||
table.WithStyles(styles),
|
||||
}
|
||||
if o.Height > 0 {
|
||||
opts = append(opts, table.WithHeight(o.Height))
|
||||
opts = append(opts, table.WithHeight(o.Height-top-bottom))
|
||||
}
|
||||
|
||||
table := table.New(opts...)
|
||||
|
|
@ -147,6 +148,7 @@ func (o Options) Run() error {
|
|||
hideCount: o.HideCount,
|
||||
help: help.New(),
|
||||
keymap: defaultKeymap(),
|
||||
padding: []int{top, right, bottom, left},
|
||||
}
|
||||
tm, err := tea.NewProgram(
|
||||
m,
|
||||
|
|
|
|||
|
|
@ -26,4 +26,5 @@ type Options struct {
|
|||
SelectedStyle style.Styles `embed:"" prefix:"selected." set:"defaultForeground=212" envprefix:"GUM_TABLE_SELECTED_"`
|
||||
ReturnColumn int `short:"r" help:"Which column number should be returned instead of whole row as string. Default=0 returns whole Row" default:"0"`
|
||||
Timeout time.Duration `help:"Timeout until choose returns selected element" default:"0s" env:"GUM_TABLE_TIMEOUT"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_TABLE_PADDING"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ import (
|
|||
"github.com/charmbracelet/bubbles/key"
|
||||
"github.com/charmbracelet/bubbles/table"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
type keymap struct {
|
||||
|
|
@ -72,6 +73,7 @@ type model struct {
|
|||
hideCount bool
|
||||
help help.Model
|
||||
keymap keymap
|
||||
padding []int
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd { return nil }
|
||||
|
|
@ -122,7 +124,9 @@ func (m model) View() string {
|
|||
if m.showHelp {
|
||||
s += "\n" + m.countView() + m.help.View(m.keymap)
|
||||
}
|
||||
return s
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(s)
|
||||
}
|
||||
|
||||
func numLen(i int) int {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import (
|
|||
"github.com/charmbracelet/gum/cursor"
|
||||
"github.com/charmbracelet/gum/internal/stdin"
|
||||
"github.com/charmbracelet/gum/internal/timeout"
|
||||
"github.com/charmbracelet/gum/style"
|
||||
)
|
||||
|
||||
// Run provides a shell script interface for the text area bubble.
|
||||
|
|
@ -30,6 +31,7 @@ func (o Options) Run() error {
|
|||
a.ShowLineNumbers = o.ShowLineNumbers
|
||||
a.CharLimit = o.CharLimit
|
||||
a.MaxHeight = o.MaxLines
|
||||
top, right, bottom, left := style.ParsePadding(o.Padding)
|
||||
|
||||
style := textarea.Style{
|
||||
Base: o.BaseStyle.ToLipgloss(),
|
||||
|
|
@ -46,8 +48,8 @@ func (o Options) Run() error {
|
|||
a.Cursor.Style = o.CursorStyle.ToLipgloss()
|
||||
a.Cursor.SetMode(cursor.Modes[o.CursorMode])
|
||||
|
||||
a.SetWidth(o.Width)
|
||||
a.SetHeight(o.Height)
|
||||
a.SetWidth(max(0, o.Width-left-right))
|
||||
a.SetHeight(max(0, o.Height-top-bottom))
|
||||
a.SetValue(o.Value)
|
||||
|
||||
m := model{
|
||||
|
|
@ -58,6 +60,7 @@ func (o Options) Run() error {
|
|||
help: help.New(),
|
||||
showHelp: o.ShowHelp,
|
||||
keymap: defaultKeymap(),
|
||||
padding: []int{top, right, bottom, left},
|
||||
}
|
||||
|
||||
m.textarea.KeyMap.InsertNewline = m.keymap.InsertNewline
|
||||
|
|
|
|||
|
|
@ -32,4 +32,5 @@ type Options struct {
|
|||
HeaderStyle style.Styles `embed:"" prefix:"header." set:"defaultForeground=240" envprefix:"GUM_WRITE_HEADER_"`
|
||||
PlaceholderStyle style.Styles `embed:"" prefix:"placeholder." set:"defaultForeground=240" envprefix:"GUM_WRITE_PLACEHOLDER_"`
|
||||
PromptStyle style.Styles `embed:"" prefix:"prompt." set:"defaultForeground=7" envprefix:"GUM_WRITE_PROMPT_"`
|
||||
Padding string `help:"Padding" default:"${defaultPadding}" group:"Style Flags" env:"GUM_WRITE_PADDING"`
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,6 +77,7 @@ type model struct {
|
|||
showHelp bool
|
||||
help help.Model
|
||||
keymap keymap
|
||||
padding []int
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd { return textarea.Blink }
|
||||
|
|
@ -96,14 +97,19 @@ func (m model) View() string {
|
|||
if m.showHelp {
|
||||
parts = append(parts, "", m.help.View(m.keymap))
|
||||
}
|
||||
return lipgloss.JoinVertical(lipgloss.Left, parts...)
|
||||
return lipgloss.NewStyle().
|
||||
Padding(m.padding...).
|
||||
Render(lipgloss.JoinVertical(
|
||||
lipgloss.Left,
|
||||
parts...,
|
||||
))
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
if m.autoWidth {
|
||||
m.textarea.SetWidth(msg.Width)
|
||||
m.textarea.SetWidth(msg.Width - m.padding[1] - m.padding[3])
|
||||
}
|
||||
case tea.FocusMsg, tea.BlurMsg:
|
||||
var cmd tea.Cmd
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue