func (m *Model) updateKeybindings() {
	switch m.filterState {
	case Filtering:
		m.KeyMap.CursorUp.SetEnabled(false)
		m.KeyMap.CursorDown.SetEnabled(false)
		m.KeyMap.NextPage.SetEnabled(false)
		m.KeyMap.PrevPage.SetEnabled(false)
		m.KeyMap.GoToStart.SetEnabled(false)
		m.KeyMap.GoToEnd.SetEnabled(false)
		m.KeyMap.Filter.SetEnabled(false)
		m.KeyMap.ClearFilter.SetEnabled(false)
		m.KeyMap.CancelWhileFiltering.SetEnabled(true)
		m.KeyMap.AcceptWhileFiltering.SetEnabled(m.FilterInput.Value() != "")
		m.KeyMap.Quit.SetEnabled(true)
		m.KeyMap.ShowFullHelp.SetEnabled(false)
		m.KeyMap.CloseFullHelp.SetEnabled(false)
	default:
		hasItems := m.items != nil
		m.KeyMap.CursorUp.SetEnabled(hasItems)
		m.KeyMap.CursorDown.SetEnabled(hasItems)
		hasPages := m.Paginator.TotalPages > 1
		m.KeyMap.NextPage.SetEnabled(hasPages)
		m.KeyMap.PrevPage.SetEnabled(hasPages)
		m.KeyMap.GoToStart.SetEnabled(hasItems)
		m.KeyMap.GoToEnd.SetEnabled(hasItems)
		m.KeyMap.Filter.SetEnabled(m.filteringEnabled && hasItems)
		m.KeyMap.ClearFilter.SetEnabled(m.filterState == FilterApplied)
		m.KeyMap.CancelWhileFiltering.SetEnabled(false)
		m.KeyMap.AcceptWhileFiltering.SetEnabled(false)
		m.KeyMap.Quit.SetEnabled(true)
		if m.Help.ShowAll {
			m.KeyMap.ShowFullHelp.SetEnabled(true)
			m.KeyMap.CloseFullHelp.SetEnabled(true)
		} else {
			minHelp := countEnabledBindings(m.FullHelp()) > 1
			m.KeyMap.ShowFullHelp.SetEnabled(minHelp)
			m.KeyMap.CloseFullHelp.SetEnabled(minHelp)
		}
	}
}
Set keybindings according to the filter state.
package list
import (
	"fmt"
	"io"
	"sort"
	"strings"
	"time"
	"github.com/charmbracelet/bubbles/help"
	"github.com/charmbracelet/bubbles/key"
	"github.com/charmbracelet/bubbles/paginator"
	"github.com/charmbracelet/bubbles/spinner"
	"github.com/charmbracelet/bubbles/textinput"
	tea "github.com/charmbracelet/bubbletea"
	"github.com/charmbracelet/lipgloss"
	"github.com/muesli/reflow/ansi"
	"github.com/muesli/reflow/truncate"
	"github.com/sahilm/fuzzy"
)
type Item interface {
	FilterValue() string
}
type ItemDelegate interface {
	Render(w io.Writer, m Model, index int, item Item)
	Height() int
	Spacing() int
	Update(msg tea.Msg, m *Model) tea.Cmd
}
type filteredItem struct {
	item    Item   
	matches []int  
}
type filteredItems []filteredItem
func (f filteredItems) items() []Item {
	agg := make([]Item, len(f))
	for i, v := range f {
		agg[i] = v.item
	}
	return agg
}
func (f filteredItems) matches() [][]int {
	agg := make([][]int, len(f))
	for i, v := range f {
		agg[i] = v.matches
	}
	return agg
}
type filterMatchesMsg []filteredItem
type statusMessageTimeoutMsg struct{}
type FilterState int
const (
	Unfiltered    FilterState = iota  
	Filtering                         
	FilterApplied                     
)
func (f FilterState) String() string {
	return [...]string{
		"unfiltered",
		"filtering",
		"filter applied",
	}[f]
}
type Model struct {
	showTitle        bool
	showFilter       bool
	showStatusBar    bool
	showPagination   bool
	showHelp         bool
	filteringEnabled bool
	Title  string
	Styles Styles
	KeyMap KeyMap
	AdditionalShortHelpKeys func() []key.Binding
	AdditionalFullHelpKeys  func() []key.Binding
	spinner     spinner.Model
	showSpinner bool
	width       int
	height      int
	Paginator   paginator.Model
	cursor      int
	Help        help.Model
	FilterInput textinput.Model
	filterState FilterState
	StatusMessageLifetime time.Duration
	statusMessage      string
	statusMessageTimer *time.Timer
	items []Item
	filteredItems filteredItems
	delegate ItemDelegate
}
func NewModel(items []Item, delegate ItemDelegate, width, height int) Model {
	styles := DefaultStyles()
	sp := spinner.NewModel()
	sp.Spinner = spinner.Line
	sp.Style = styles.Spinner
	filterInput := textinput.NewModel()
	filterInput.Prompt = "Filter: "
	filterInput.PromptStyle = styles.FilterPrompt
	filterInput.CursorStyle = styles.FilterCursor
	filterInput.CharLimit = 64
	filterInput.Focus()
	p := paginator.NewModel()
	p.Type = paginator.Dots
	p.ActiveDot = styles.ActivePaginationDot.String()
	p.InactiveDot = styles.InactivePaginationDot.String()
	m := Model{
		showTitle:             true,
		showFilter:            true,
		showStatusBar:         true,
		showPagination:        true,
		showHelp:              true,
		filteringEnabled:      true,
		KeyMap:                DefaultKeyMap(),
		Styles:                styles,
		Title:                 "List",
		FilterInput:           filterInput,
		StatusMessageLifetime: time.Second,
		width:     width,
		height:    height,
		delegate:  delegate,
		items:     items,
		Paginator: p,
		spinner:   sp,
		Help:      help.NewModel(),
	}
	m.updatePagination()
	m.updateKeybindings()
	return m
}
func (m *Model) SetFilteringEnabled(v bool) {
	m.filteringEnabled = v
	if !v {
		m.resetFiltering()
	}
	m.updateKeybindings()
}
func (m Model) FilteringEnabled() bool {
	return m.filteringEnabled
}
func (m *Model) SetShowTitle(v bool) {
	m.showTitle = v
	m.updatePagination()
}
func (m Model) ShowTitle() bool {
	return m.showTitle
}
func (m *Model) SetShowFilter(v bool) {
	m.showFilter = v
	m.updatePagination()
}
func (m Model) ShowFilter() bool {
	return m.showFilter
}
func (m *Model) SetShowStatusBar(v bool) {
	m.showStatusBar = v
	m.updatePagination()
}
func (m Model) ShowStatusBar() bool {
	return m.showStatusBar
}
func (m *Model) SetShowPagination(v bool) {
	m.showPagination = v
	m.updatePagination()
}
func (m *Model) ShowPagination() bool {
	return m.showPagination
}
func (m *Model) SetShowHelp(v bool) {
	m.showHelp = v
	m.updatePagination()
}
func (m Model) ShowHelp() bool {
	return m.showHelp
}
func (m Model) Items() []Item {
	return m.items
}
func (m *Model) SetItems(i []Item) tea.Cmd {
	var cmd tea.Cmd
	m.items = i
	if m.filterState != Unfiltered {
		m.filteredItems = nil
		cmd = filterItems(*m)
	}
	m.updatePagination()
	return cmd
}
func (m *Model) Select(index int) {
	m.Paginator.Page = index / m.Paginator.PerPage
	m.cursor = index % m.Paginator.PerPage
}
func (m *Model) ResetSelected() {
	m.Select(0)
}
func (m *Model) ResetFilter() {
	m.resetFiltering()
}
func (m *Model) SetItem(index int, item Item) tea.Cmd {
	var cmd tea.Cmd
	m.items[index] = item
	if m.filterState != Unfiltered {
		cmd = filterItems(*m)
	}
	m.updatePagination()
	return cmd
}
func (m *Model) InsertItem(index int, item Item) tea.Cmd {
	var cmd tea.Cmd
	m.items = insertItemIntoSlice(m.items, item, index)
	if m.filterState != Unfiltered {
		cmd = filterItems(*m)
	}
	m.updatePagination()
	return cmd
}
func (m *Model) RemoveItem(index int) {
	m.items = removeItemFromSlice(m.items, index)
	if m.filterState != Unfiltered {
		m.filteredItems = removeFilterMatchFromSlice(m.filteredItems, index)
		if len(m.filteredItems) == 0 {
			m.resetFiltering()
		}
	}
	m.updatePagination()
}
func (m *Model) SetDelegate(d ItemDelegate) {
	m.delegate = d
	m.updatePagination()
}
func (m Model) VisibleItems() []Item {
	if m.filterState != Unfiltered {
		return m.filteredItems.items()
	}
	return m.items
}
func (m Model) SelectedItem() Item {
	i := m.Index()
	items := m.VisibleItems()
	if i < 0 || len(items) == 0 || len(items) <= i {
		return nil
	}
	return items[i]
}
func (m Model) MatchesForItem(index int) []int {
	if m.filteredItems == nil || index >= len(m.filteredItems) {
		return nil
	}
	return m.filteredItems[index].matches
}
func (m Model) Index() int {
	return m.Paginator.Page*m.Paginator.PerPage + m.cursor
}
func (m Model) Cursor() int {
	return m.cursor
}
func (m *Model) CursorUp() {
	m.cursor--
	if m.cursor < 0 && m.Paginator.Page == 0 {
		m.cursor = 0
		return
	}
	if m.cursor >= 0 {
		return
	}
	m.Paginator.PrevPage()
	m.cursor = m.Paginator.ItemsOnPage(len(m.VisibleItems())) - 1
}
func (m *Model) CursorDown() {
	itemsOnPage := m.Paginator.ItemsOnPage(len(m.VisibleItems()))
	m.cursor++
	if m.cursor < itemsOnPage {
		return
	}
	if !m.Paginator.OnLastPage() {
		m.Paginator.NextPage()
		m.cursor = 0
		return
	}
	if m.cursor > itemsOnPage {
		m.cursor = 0
		return
	}
	m.cursor = itemsOnPage - 1
}
func (m Model) PrevPage() {
	m.Paginator.PrevPage()
}
func (m Model) NextPage() {
	m.Paginator.NextPage()
}
func (m Model) FilterState() FilterState {
	return m.filterState
}
func (m Model) FilterValue() string {
	return m.FilterInput.Value()
}
func (m Model) SettingFilter() bool {
	return m.filterState == Filtering
}
func (m Model) Width() int {
	return m.width
}
func (m Model) Height() int {
	return m.height
}
func (m *Model) SetSpinner(spinner spinner.Spinner) {
	m.spinner.Spinner = spinner
}
func (m *Model) ToggleSpinner() tea.Cmd {
	if !m.showSpinner {
		return m.StartSpinner()
	}
	m.StopSpinner()
	return nil
}
func (m *Model) StartSpinner() tea.Cmd {
	m.showSpinner = true
	return spinner.Tick
}
func (m *Model) StopSpinner() {
	m.showSpinner = false
}
func (m *Model) DisableQuitKeybindings() {
	m.KeyMap.Quit.SetEnabled(false)
	m.KeyMap.ForceQuit.SetEnabled(false)
}
func (m *Model) NewStatusMessage(s string) tea.Cmd {
	m.statusMessage = s
	if m.statusMessageTimer != nil {
		m.statusMessageTimer.Stop()
	}
	m.statusMessageTimer = time.NewTimer(m.StatusMessageLifetime)
	return func() tea.Msg {
		<-m.statusMessageTimer.C
		return statusMessageTimeoutMsg{}
	}
}
func (m *Model) SetSize(width, height int) {
	m.setSize(width, height)
}
func (m *Model) SetWidth(v int) {
	m.setSize(v, m.height)
}
func (m *Model) SetHeight(v int) {
	m.setSize(m.width, v)
}
func (m *Model) setSize(width, height int) {
	promptWidth := lipgloss.Width(m.Styles.Title.Render(m.FilterInput.Prompt))
	m.width = width
	m.height = height
	m.Help.Width = width
	m.FilterInput.Width = width - promptWidth - lipgloss.Width(m.spinnerView())
	m.updatePagination()
}
func (m *Model) resetFiltering() {
	if m.filterState == Unfiltered {
		return
	}
	m.filterState = Unfiltered
	m.FilterInput.Reset()
	m.filteredItems = nil
	m.updatePagination()
	m.updateKeybindings()
}
func (m Model) itemsAsFilterItems() filteredItems {
	fi := make([]filteredItem, len(m.items))
	for i, item := range m.items {
		fi[i] = filteredItem{
			item: item,
		}
	}
	return filteredItems(fi)
}