mirror of
https://github.com/charmbracelet/gum
synced 2026-03-14 13:45:45 +01:00
refactor: removing huh as a dep (#742)
* Revert "feat: huh gum write (#525)" This reverts commit4d5d53169e. Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * Revert "Use Huh for Gum Confirm (#522)" This reverts commitf7572e387e. Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * revert: Use Huh for Gum Choose (#521) Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * revert: feat: huh for gum input (#524) * revert: feat: huh file picker (#523) * feat: remove huh * fix: timeouts * fix: lint issues * fix(choose): quit on ctrl+q ported over63a3e8c8ce* fix: ctrl+a to reverse selection Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com> * fix: better handle spin exit codes * fix(file): bind --[no-]permissions and --[no-]size * feat(confirm): show help * fix(confirm): fix help style * fix(file): help * fix(input): --no-show-help doesn't work * fix(input): help * fix(file): keymap improvement * fix(write): focus * feat(write): ctrl+e, keymaps, help * feat(choose): help * feat(filter): help * refactor: keymaps * fix(choose): only show 'toggle all' if there's no limit * fix(choose): don't show toggle if the choices are limited to 1 * fix(filter): match choose header color * fix(filter): add space above help * fix(filter): factor help into the height setting * chore(choose,filter): use verb for navigation label in help * fix(filter): hide toggle help if limit is 1 * fix(file): factor help into height setting (#746) * fix: lint issues * fix(file): handle ctrl+c * fix: remove full help * fix: lint --------- 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
d74e9ea531
commit
e30fc5ecdf
25 changed files with 1374 additions and 292 deletions
|
|
@ -3,11 +3,13 @@ package file
|
|||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/charmbracelet/bubbles/filepicker"
|
||||
"github.com/charmbracelet/bubbles/help"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/gum/internal/exit"
|
||||
"github.com/charmbracelet/huh"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
// Run is the interface to picking a file.
|
||||
|
|
@ -25,42 +27,50 @@ func (o Options) Run() error {
|
|||
return fmt.Errorf("file not found: %w", err)
|
||||
}
|
||||
|
||||
theme := huh.ThemeCharm()
|
||||
theme.Focused.Base = lipgloss.NewStyle()
|
||||
theme.Focused.File = o.FileStyle.ToLipgloss()
|
||||
theme.Focused.Directory = o.DirectoryStyle.ToLipgloss()
|
||||
theme.Focused.SelectedOption = o.SelectedStyle.ToLipgloss()
|
||||
|
||||
keymap := huh.NewDefaultKeyMap()
|
||||
keymap.FilePicker.Open.SetEnabled(false)
|
||||
|
||||
// XXX: These should be file selected specific.
|
||||
theme.Focused.TextInput.Placeholder = o.PermissionsStyle.ToLipgloss()
|
||||
theme.Focused.TextInput.Prompt = o.CursorStyle.ToLipgloss()
|
||||
|
||||
err = huh.NewForm(
|
||||
huh.NewGroup(
|
||||
huh.NewFilePicker().
|
||||
Picking(true).
|
||||
CurrentDirectory(path).
|
||||
Cursor(o.Cursor).
|
||||
DirAllowed(o.Directory).
|
||||
FileAllowed(o.File).
|
||||
Height(o.Height).
|
||||
ShowHidden(o.All).
|
||||
ShowSize(o.Size).
|
||||
ShowPermissions(o.Permissions).
|
||||
Value(&path),
|
||||
),
|
||||
).
|
||||
WithTimeout(o.Timeout).
|
||||
WithShowHelp(o.ShowHelp).
|
||||
WithKeyMap(keymap).
|
||||
WithTheme(theme).
|
||||
Run()
|
||||
if err != nil {
|
||||
return exit.Handle(err, o.Timeout)
|
||||
fp := filepicker.New()
|
||||
fp.CurrentDirectory = path
|
||||
fp.Path = path
|
||||
fp.Height = o.Height
|
||||
fp.AutoHeight = o.Height == 0
|
||||
fp.Cursor = o.Cursor
|
||||
fp.DirAllowed = o.Directory
|
||||
fp.FileAllowed = o.File
|
||||
fp.ShowPermissions = o.Permissions
|
||||
fp.ShowSize = o.Size
|
||||
fp.ShowHidden = o.All
|
||||
fp.Styles = filepicker.DefaultStyles()
|
||||
fp.Styles.Cursor = o.CursorStyle.ToLipgloss()
|
||||
fp.Styles.Symlink = o.SymlinkStyle.ToLipgloss()
|
||||
fp.Styles.Directory = o.DirectoryStyle.ToLipgloss()
|
||||
fp.Styles.File = o.FileStyle.ToLipgloss()
|
||||
fp.Styles.Permission = o.PermissionsStyle.ToLipgloss()
|
||||
fp.Styles.Selected = o.SelectedStyle.ToLipgloss()
|
||||
fp.Styles.FileSize = o.FileSizeStyle.ToLipgloss()
|
||||
m := model{
|
||||
filepicker: fp,
|
||||
timeout: o.Timeout,
|
||||
hasTimeout: o.Timeout > 0,
|
||||
aborted: false,
|
||||
showHelp: o.ShowHelp,
|
||||
help: help.New(),
|
||||
keymap: defaultKeymap(),
|
||||
}
|
||||
fmt.Println(path)
|
||||
|
||||
tm, err := tea.NewProgram(&m, tea.WithOutput(os.Stderr)).Run()
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to pick selection: %w", err)
|
||||
}
|
||||
m = tm.(model)
|
||||
if m.aborted {
|
||||
return exit.ErrAborted
|
||||
}
|
||||
if m.timedOut {
|
||||
return exit.ErrTimeout
|
||||
}
|
||||
if m.selectedPath == "" {
|
||||
return errors.New("no file selected")
|
||||
}
|
||||
|
||||
fmt.Println(m.selectedPath)
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
125
file/file.go
Normal file
125
file/file.go
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
// Package file provides an interface to pick a file from a folder (tree).
|
||||
// The user is provided a file manager-like interface to navigate, to
|
||||
// select a file.
|
||||
//
|
||||
// Let's pick a file from the current directory:
|
||||
//
|
||||
// $ gum file
|
||||
// $ gum file .
|
||||
//
|
||||
// Let's pick a file from the home directory:
|
||||
//
|
||||
// $ gum file $HOME
|
||||
package file
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/charmbracelet/bubbles/filepicker"
|
||||
"github.com/charmbracelet/bubbles/help"
|
||||
"github.com/charmbracelet/bubbles/key"
|
||||
tea "github.com/charmbracelet/bubbletea"
|
||||
"github.com/charmbracelet/gum/timeout"
|
||||
"github.com/charmbracelet/lipgloss"
|
||||
)
|
||||
|
||||
type keymap filepicker.KeyMap
|
||||
|
||||
var keyQuit = key.NewBinding(
|
||||
key.WithKeys("esc", "q"),
|
||||
key.WithHelp("esc", "close"),
|
||||
)
|
||||
|
||||
var keyAbort = key.NewBinding(
|
||||
key.WithKeys("ctrl+c"),
|
||||
key.WithHelp("ctrl+c", "abort"),
|
||||
)
|
||||
|
||||
func defaultKeymap() keymap {
|
||||
km := filepicker.DefaultKeyMap()
|
||||
km.Down.SetHelp("↓", "down")
|
||||
km.Up.SetHelp("↑", "up")
|
||||
return keymap(km)
|
||||
}
|
||||
|
||||
// FullHelp implements help.KeyMap.
|
||||
func (k keymap) FullHelp() [][]key.Binding { return nil }
|
||||
|
||||
// ShortHelp implements help.KeyMap.
|
||||
func (k keymap) ShortHelp() []key.Binding {
|
||||
return []key.Binding{
|
||||
k.Up,
|
||||
k.Down,
|
||||
keyQuit,
|
||||
k.Select,
|
||||
}
|
||||
}
|
||||
|
||||
type model struct {
|
||||
filepicker filepicker.Model
|
||||
selectedPath string
|
||||
aborted bool
|
||||
timedOut bool
|
||||
quitting bool
|
||||
timeout time.Duration
|
||||
hasTimeout bool
|
||||
showHelp bool
|
||||
help help.Model
|
||||
keymap keymap
|
||||
}
|
||||
|
||||
func (m model) Init() tea.Cmd {
|
||||
return tea.Batch(
|
||||
timeout.Init(m.timeout, nil),
|
||||
m.filepicker.Init(),
|
||||
)
|
||||
}
|
||||
|
||||
func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
switch msg := msg.(type) {
|
||||
case tea.WindowSizeMsg:
|
||||
if m.showHelp {
|
||||
m.filepicker.Height -= lipgloss.Height(m.helpView())
|
||||
}
|
||||
case tea.KeyMsg:
|
||||
switch {
|
||||
case key.Matches(msg, keyAbort):
|
||||
m.aborted = true
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
case key.Matches(msg, keyQuit):
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
case timeout.TickTimeoutMsg:
|
||||
if msg.TimeoutValue <= 0 {
|
||||
m.quitting = true
|
||||
m.timedOut = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
m.timeout = msg.TimeoutValue
|
||||
return m, timeout.Tick(msg.TimeoutValue, msg.Data)
|
||||
}
|
||||
var cmd tea.Cmd
|
||||
m.filepicker, cmd = m.filepicker.Update(msg)
|
||||
if didSelect, path := m.filepicker.DidSelectFile(msg); didSelect {
|
||||
m.selectedPath = path
|
||||
m.quitting = true
|
||||
return m, tea.Quit
|
||||
}
|
||||
return m, cmd
|
||||
}
|
||||
|
||||
func (m model) View() string {
|
||||
if m.quitting {
|
||||
return ""
|
||||
}
|
||||
if !m.showHelp {
|
||||
return m.filepicker.View()
|
||||
}
|
||||
return m.filepicker.View() + m.helpView()
|
||||
}
|
||||
|
||||
func (m model) helpView() string {
|
||||
return "\n" + m.help.View(m.keymap)
|
||||
}
|
||||
|
|
@ -19,7 +19,7 @@ type Options struct {
|
|||
Directory bool `help:"Allow directories selection" default:"false" env:"GUM_FILE_DIRECTORY"`
|
||||
ShowHelp bool `help:"Show help key binds" negatable:"" default:"true" env:"GUM_FILE_SHOW_HELP"`
|
||||
|
||||
Height int `help:"Maximum number of files to display" default:"0" env:"GUM_FILE_HEIGHT"`
|
||||
Height int `help:"Maximum number of files to display" default:"10" env:"GUM_FILE_HEIGHT"`
|
||||
CursorStyle style.Styles `embed:"" prefix:"cursor." help:"The cursor style" set:"defaultForeground=212" envprefix:"GUM_FILE_CURSOR_"`
|
||||
SymlinkStyle style.Styles `embed:"" prefix:"symlink." help:"The style to use for symlinks" set:"defaultForeground=36" envprefix:"GUM_FILE_SYMLINK_"`
|
||||
DirectoryStyle style.Styles `embed:"" prefix:"directory." help:"The style to use for directories" set:"defaultForeground=99" envprefix:"GUM_FILE_DIRECTORY_"`
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue