Add syncing
This commit is contained in:
@ -89,6 +89,11 @@ func (a *Autocomplete) SetSuggestions(suggestions []string) {
|
||||
a.updateFilteredSuggestions()
|
||||
}
|
||||
|
||||
// HasSuggestions returns true if the autocomplete is currently showing suggestions
|
||||
func (a *Autocomplete) HasSuggestions() bool {
|
||||
return a.showSuggestions && len(a.filteredSuggestions) > 0
|
||||
}
|
||||
|
||||
// Init initializes the autocomplete
|
||||
func (a *Autocomplete) Init() tea.Cmd {
|
||||
return textinput.Blink
|
||||
|
||||
@ -36,6 +36,7 @@ type Picker struct {
|
||||
onCreate func(string) tea.Cmd
|
||||
title string
|
||||
filterByDefault bool
|
||||
defaultValue string
|
||||
baseItems []list.Item
|
||||
focused bool
|
||||
}
|
||||
@ -54,6 +55,12 @@ func WithOnCreate(onCreate func(string) tea.Cmd) PickerOption {
|
||||
}
|
||||
}
|
||||
|
||||
func WithDefaultValue(value string) PickerOption {
|
||||
return func(p *Picker) {
|
||||
p.defaultValue = value
|
||||
}
|
||||
}
|
||||
|
||||
func (p *Picker) Focus() tea.Cmd {
|
||||
p.focused = true
|
||||
return nil
|
||||
@ -88,6 +95,7 @@ func New(
|
||||
l.SetShowHelp(false)
|
||||
l.SetShowStatusBar(false)
|
||||
l.SetFilteringEnabled(true)
|
||||
l.Filter = list.UnsortedFilter // Preserve item order, don't rank by match quality
|
||||
|
||||
// Custom key for filtering (insert mode)
|
||||
l.KeyMap.Filter = key.NewBinding(
|
||||
@ -112,16 +120,24 @@ func New(
|
||||
opt(p)
|
||||
}
|
||||
|
||||
if p.filterByDefault {
|
||||
// Manually trigger filter mode on the list so it doesn't require a global key press
|
||||
var cmd tea.Cmd
|
||||
p.list, cmd = p.list.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'i'}})
|
||||
// We can ignore the command here as it's likely just for blinking, which will happen on Init anyway
|
||||
_ = cmd
|
||||
// If a default value is provided, don't start in filter mode
|
||||
if p.defaultValue != "" {
|
||||
p.filterByDefault = false
|
||||
}
|
||||
|
||||
if p.filterByDefault {
|
||||
// Manually trigger filter mode on the list so it doesn't require a global key press
|
||||
p.list, _ = p.list.Update(tea.KeyMsg{Type: tea.KeyRunes, Runes: []rune{'i'}})
|
||||
}
|
||||
|
||||
// Refresh items after entering filter mode to ensure they're visible
|
||||
p.Refresh()
|
||||
|
||||
// If a default value is provided, select the corresponding item
|
||||
if p.defaultValue != "" {
|
||||
p.SelectItemByFilterValue(p.defaultValue)
|
||||
}
|
||||
|
||||
return p
|
||||
}
|
||||
|
||||
@ -131,18 +147,22 @@ func (p *Picker) Refresh() tea.Cmd {
|
||||
}
|
||||
|
||||
func (p *Picker) updateListItems() tea.Cmd {
|
||||
items := p.baseItems
|
||||
filterVal := p.list.FilterValue()
|
||||
return p.updateListItemsWithFilter(p.list.FilterValue())
|
||||
}
|
||||
|
||||
func (p *Picker) updateListItemsWithFilter(filterVal string) tea.Cmd {
|
||||
items := make([]list.Item, 0, len(p.baseItems)+1)
|
||||
|
||||
// First add all base items
|
||||
items = append(items, p.baseItems...)
|
||||
|
||||
if p.onCreate != nil && filterVal != "" {
|
||||
// Add the creation item at the end (bottom of the list)
|
||||
newItem := creationItem{
|
||||
text: "(new) " + filterVal,
|
||||
filter: filterVal,
|
||||
}
|
||||
newItems := make([]list.Item, len(items)+1)
|
||||
copy(newItems, items)
|
||||
newItems[len(items)] = newItem
|
||||
items = newItems
|
||||
items = append(items, newItem)
|
||||
}
|
||||
|
||||
return p.list.SetItems(items)
|
||||
@ -162,7 +182,9 @@ func (p *Picker) SetSize(width, height int) {
|
||||
}
|
||||
|
||||
func (p *Picker) Init() tea.Cmd {
|
||||
return nil
|
||||
// Trigger list item update to ensure items are properly displayed,
|
||||
// especially when in filter mode with an empty filter
|
||||
return p.updateListItems()
|
||||
}
|
||||
|
||||
func (p *Picker) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
@ -171,17 +193,31 @@ func (p *Picker) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
|
||||
var cmd tea.Cmd
|
||||
var cmds []tea.Cmd
|
||||
|
||||
switch msg := msg.(type) {
|
||||
case tea.KeyMsg:
|
||||
// If filtering, let the list handle keys (including Enter to stop filtering)
|
||||
// If filtering, update items with predicted filter before list processes the key
|
||||
if p.list.FilterState() == list.Filtering {
|
||||
// if key.Matches(msg, p.common.Keymap.Ok) {
|
||||
// items := p.list.VisibleItems()
|
||||
// if len(items) == 1 {
|
||||
// return p, p.handleSelect(items[0])
|
||||
// }
|
||||
// }
|
||||
currentFilter := p.list.FilterValue()
|
||||
predictedFilter := currentFilter
|
||||
|
||||
// Predict what the filter will be after this key
|
||||
switch msg.Type {
|
||||
case tea.KeyRunes:
|
||||
predictedFilter = currentFilter + string(msg.Runes)
|
||||
case tea.KeyBackspace:
|
||||
if len(currentFilter) > 0 {
|
||||
predictedFilter = currentFilter[:len(currentFilter)-1]
|
||||
}
|
||||
}
|
||||
|
||||
// Update items with predicted filter before list processes the message
|
||||
if predictedFilter != currentFilter {
|
||||
preCmd := p.updateListItemsWithFilter(predictedFilter)
|
||||
cmds = append(cmds, preCmd)
|
||||
}
|
||||
|
||||
break // Pass to list.Update
|
||||
}
|
||||
|
||||
@ -195,15 +231,10 @@ func (p *Picker) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
|
||||
}
|
||||
}
|
||||
|
||||
prevFilter := p.list.FilterValue()
|
||||
p.list, cmd = p.list.Update(msg)
|
||||
cmds = append(cmds, cmd)
|
||||
|
||||
if p.list.FilterValue() != prevFilter {
|
||||
updateCmd := p.updateListItems()
|
||||
return p, tea.Batch(cmd, updateCmd)
|
||||
}
|
||||
|
||||
return p, cmd
|
||||
return p, tea.Batch(cmds...)
|
||||
}
|
||||
|
||||
func (p *Picker) handleSelect(item list.Item) tea.Cmd {
|
||||
|
||||
Reference in New Issue
Block a user