refactor: removing huh as a dep (#742)

* Revert "feat: huh gum write (#525)"

This reverts commit 4d5d53169e.

Signed-off-by: Carlos Alexandro Becker <caarlos0@users.noreply.github.com>

* Revert "Use Huh for Gum Confirm (#522)"

This reverts commit f7572e387e.

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 over 63a3e8c8ce

* 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:
Carlos Alexandro Becker 2024-12-09 13:18:35 -03:00 committed by GitHub
commit e30fc5ecdf
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
25 changed files with 1374 additions and 292 deletions

View file

@ -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
View 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)
}

View file

@ -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_"`