117 lines
2.6 KiB
Go
117 lines
2.6 KiB
Go
package tui
|
|
|
|
import (
|
|
"fmt"
|
|
"io"
|
|
"strings"
|
|
|
|
"git.jingrow.com/charmbracelet/bubbles/list"
|
|
tea "git.jingrow.com/charmbracelet/bubbletea"
|
|
)
|
|
|
|
func PickItem(prompt string, items []string) (selectedItem string, err error) {
|
|
// Convert strings to list items
|
|
listItems := make([]list.Item, len(items))
|
|
for i, item := range items {
|
|
listItems[i] = listItem(item)
|
|
}
|
|
|
|
// Create list with filepicker-like styles
|
|
l := list.New(listItems, itemDelegate{}, 0, 0)
|
|
l.SetShowTitle(false)
|
|
l.SetShowStatusBar(false)
|
|
l.SetFilteringEnabled(true)
|
|
l.SetShowPagination(false)
|
|
l.SetShowHelp(false)
|
|
|
|
m := &model{
|
|
list: l,
|
|
prompt: prompt,
|
|
}
|
|
|
|
p := tea.NewProgram(m, tea.WithAltScreen())
|
|
if _, err := p.Run(); err != nil {
|
|
return "", err
|
|
}
|
|
|
|
return m.choice, nil
|
|
}
|
|
|
|
// Styles matching filepicker aesthetics
|
|
var (
|
|
titleStyle = BUBBLE_TEA_FILEPICKER_STYLE.File.Bold(true)
|
|
itemStyle = BUBBLE_TEA_FILEPICKER_STYLE.File
|
|
selectedItemStyle = BUBBLE_TEA_FILEPICKER_STYLE.Selected
|
|
)
|
|
|
|
type listItem string
|
|
|
|
func (i listItem) FilterValue() string { return string(i) }
|
|
|
|
type itemDelegate struct{}
|
|
|
|
func (d itemDelegate) Height() int { return 1 }
|
|
func (d itemDelegate) Spacing() int { return 0 }
|
|
func (d itemDelegate) Update(_ tea.Msg, _ *list.Model) tea.Cmd { return nil }
|
|
func (d itemDelegate) Render(w io.Writer, m list.Model, index int, li list.Item) {
|
|
item, ok := li.(listItem)
|
|
if !ok {
|
|
return
|
|
}
|
|
|
|
var str string
|
|
if index == m.Index() {
|
|
str = selectedItemStyle.Render("> " + string(item))
|
|
} else {
|
|
str = itemStyle.Render(" " + string(item))
|
|
}
|
|
|
|
fmt.Fprint(w, str)
|
|
}
|
|
|
|
type model struct {
|
|
list list.Model
|
|
choice string
|
|
prompt string
|
|
}
|
|
|
|
func (m *model) Init() tea.Cmd {
|
|
return tea.Batch(
|
|
tea.EnterAltScreen, // Start with alt screen
|
|
tea.HideCursor, // Hide cursor
|
|
tea.ClearScreen, // Clear the screen
|
|
)
|
|
}
|
|
|
|
func (m *model) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
|
switch msg := msg.(type) {
|
|
case tea.WindowSizeMsg:
|
|
m.list.SetWidth(msg.Width)
|
|
m.list.SetHeight(msg.Height - 4) // Leave space for help text
|
|
return m, nil
|
|
|
|
case tea.KeyMsg:
|
|
switch msg.String() {
|
|
case "ctrl+q":
|
|
return m, tea.Quit
|
|
case "enter":
|
|
if i, ok := m.list.SelectedItem().(listItem); ok {
|
|
m.choice = string(i)
|
|
}
|
|
return m, tea.Quit
|
|
}
|
|
}
|
|
|
|
var cmd tea.Cmd
|
|
m.list, cmd = m.list.Update(msg)
|
|
return m, cmd
|
|
}
|
|
|
|
func (m *model) View() string {
|
|
var s strings.Builder
|
|
s.WriteString("\n " + titleStyle.Render(m.prompt) + "\n\n")
|
|
s.WriteString(m.list.View())
|
|
s.WriteString("\n ↑/↓ to navigate • / to search • <enter> to select • <ctrl+q> to quit\n")
|
|
return s.String()
|
|
}
|