@@ -39,6 +39,7 @@ type ListModel interface {
3939 ResetView () // Clear rendering cache and reset scroll position
4040 Items () []util.Model // Get all items in the list
4141 SelectedIndex () int // Get the index of the currently selected item
42+ SetSelected (int ) tea.Cmd // Set the selected item by index and scroll to it
4243 Filter (string ) tea.Cmd // Filter items based on a search term
4344}
4445
@@ -133,11 +134,12 @@ type model struct {
133134 gapSize int // Number of empty lines between items
134135 padding []int // Padding around the list content
135136
136- filterable bool // Whether items can be filtered
137- filteredItems []util.Model // Filtered items based on current search
138- input textinput.Model // Input field for filtering items
139- hideFilterInput bool // Whether to hide the filter input field
140- currentSearch string // Current search term for filtering
137+ filterable bool // Whether items can be filtered
138+ filterPlaceholder string // Placeholder text for filter input
139+ filteredItems []util.Model // Filtered items based on current search
140+ input textinput.Model // Input field for filtering items
141+ hideFilterInput bool // Whether to hide the filter input field
142+ currentSearch string // Current search term for filtering
141143}
142144
143145// listOptions is a function type for configuring list options.
@@ -195,29 +197,39 @@ func WithHideFilterInput(hide bool) listOptions {
195197 }
196198}
197199
200+ // WithFilterPlaceholder sets the placeholder text for the filter input field.
201+ func WithFilterPlaceholder (placeholder string ) listOptions {
202+ return func (m * model ) {
203+ m .filterPlaceholder = placeholder
204+ }
205+ }
206+
198207// New creates a new list model with the specified options.
199208// The list starts with no items selected and requires SetItems to be called
200209// or items to be provided via WithItems option.
201210func New (opts ... listOptions ) ListModel {
202211 m := & model {
203- help : help .New (),
204- keyMap : DefaultKeyMap (),
205- allItems : []util.Model {},
206- filteredItems : []util.Model {},
207- renderState : newRenderState (),
208- gapSize : DefaultGapSize ,
209- padding : []int {},
210- selectionState : selectionState {selectedIndex : NoSelection },
212+ help : help .New (),
213+ keyMap : DefaultKeyMap (),
214+ allItems : []util.Model {},
215+ filteredItems : []util.Model {},
216+ renderState : newRenderState (),
217+ gapSize : DefaultGapSize ,
218+ padding : []int {},
219+ selectionState : selectionState {selectedIndex : NoSelection },
220+ filterPlaceholder : "Type to filter..." ,
211221 }
212222 for _ , opt := range opts {
213223 opt (m )
214224 }
215225
216226 if m .filterable && ! m .hideFilterInput {
227+ t := styles .CurrentTheme ()
217228 ti := textinput .New ()
218- ti .Placeholder = "Type to filter..."
229+ ti .Placeholder = m . filterPlaceholder
219230 ti .SetVirtualCursor (false )
220231 ti .Focus ()
232+ ti .SetStyles (t .S ().TextInput )
221233 m .input = ti
222234
223235 // disable j,k movements
@@ -616,7 +628,7 @@ func (m *model) isSectionHeader(index int) bool {
616628
617629// findFirstSelectableItem finds the first item that is not a section header.
618630func (m * model ) findFirstSelectableItem () int {
619- for i := 0 ; i < len ( m .filteredItems ); i ++ {
631+ for i := range m .filteredItems {
620632 if ! m .isSectionHeader (i ) {
621633 return i
622634 }
@@ -944,7 +956,7 @@ func (m *model) SetSize(width int, height int) tea.Cmd {
944956 m .viewState .width = width
945957 m .ResetView ()
946958 if m .filterable && ! m .hideFilterInput {
947- m .input .SetWidth (m .getItemWidth () - 3 )
959+ m .input .SetWidth (m .getItemWidth () - 5 )
948960 }
949961 return m .setAllItemsSize ()
950962}
@@ -1096,7 +1108,7 @@ func (m *model) SetItems(items []util.Model) tea.Cmd {
10961108}
10971109
10981110func (c * model ) inputStyle () lipgloss.Style {
1099- return styles .BaseStyle ()
1111+ return styles .BaseStyle (). Padding ( 0 , 1 , 1 , 1 )
11001112}
11011113
11021114// section represents a group of items under a section header.
@@ -1275,3 +1287,22 @@ func (m *model) SelectedIndex() int {
12751287 }
12761288 return m .selectionState .selectedIndex
12771289}
1290+
1291+ // SetSelected sets the selected item by index and automatically scrolls to make it visible.
1292+ // If the index is invalid or points to a section header, it finds the nearest selectable item.
1293+ func (m * model ) SetSelected (index int ) tea.Cmd {
1294+ changeNeeded := m .selectionState .selectedIndex - index
1295+ cmds := []tea.Cmd {}
1296+ if changeNeeded < 0 {
1297+ for range - changeNeeded {
1298+ cmds = append (cmds , m .selectNextItem ())
1299+ m .renderVisible ()
1300+ }
1301+ } else if changeNeeded > 0 {
1302+ for range changeNeeded {
1303+ cmds = append (cmds , m .selectPreviousItem ())
1304+ m .renderVisible ()
1305+ }
1306+ }
1307+ return tea .Batch (cmds ... )
1308+ }
0 commit comments