package main import ( "fmt" "io" "strings" "github.com/charmbracelet/bubbles/textinput" tea "github.com/charmbracelet/bubbletea" "github.com/charmbracelet/lipgloss" "github.com/charmbracelet/bubbles/list" ) type item struct { value string } func (i item) FilterValue() string { return i.value } 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, listItem list.Item) { i, ok := listItem.(item) if !ok { return } str := fmt.Sprintf("%s", i.value) fn := lipgloss.NewStyle().PaddingLeft(2).Render if index == m.Index() { fn = func(s ...string) string { return lipgloss.NewStyle().PaddingLeft(0).PaddingRight(1).Background(lipgloss.Color("#0F7FCF")).Render("> " + strings.Join(s, " ")) } } fmt.Fprint(w, fn(str)) } func (m model) Update(msg tea.Msg) (tea.Model, tea.Cmd) { var cmd tea.Cmd ignoreInput := false switch msg := msg.(type) { case tea.KeyMsg: switch msg.Type { case tea.KeyCtrlC, tea.KeyEsc: m.quitting = true; return m, tea.Quit case tea.KeyEnter: i, ok := m.list.SelectedItem().(item) if ok { m.choice = i.value } return m, tea.Quit case tea.KeyUp, tea.KeyCtrlK: m.list.CursorUp() ignoreInput = true case tea.KeyDown, tea.KeyCtrlJ: m.list.CursorDown() ignoreInput = true } // We handle errors just like any other message case errMsg: m.err = msg return m, nil case tea.WindowSizeMsg: m.textInput.Width = msg.Width m.list.SetWidth(msg.Width) return m, nil } if ignoreInput { return m, cmd } cmds := []tea.Cmd{} m.textInput, cmd = m.textInput.Update(msg) cmds = append(cmds, cmd) if !m.list.SettingFilter() { m.list, cmd = m.list.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune("/")}) cmds = append(cmds, cmd) } m.list, cmd = m.list.Update(msg) cmds = append(cmds, cmd) return m, tea.Batch(cmds...) } func (m model) View() string { if m.choice != "" { return m.choice } if m.quitting { return "" } return fmt.Sprintf("%s\n", m.list.View(), ) } type ( errMsg error ) type model struct { textInput textinput.Model list list.Model choice string quitting bool err error } func initialModel(items []list.Item) model { const defaultWidth = 20 l := list.New(items, itemDelegate{}, defaultWidth, 14) l.SetShowFilter(true) l.SetShowStatusBar(false) l.SetFilteringEnabled(true) l.SetShowHelp(false) l.SetShowTitle(false) l.SetShowPagination(false) l.Styles.TitleBar = lipgloss.NewStyle() ti := textinput.New() ti.Focus() ti.CharLimit = 4096 ti.Width = 20 l.FilterInput = ti l.Filter = Filter return model{ list: l, err: nil, } } func (m model) Init() tea.Cmd { return textinput.Blink }