@@ -180,11 +180,6 @@ func (f *filterableGroupList[T]) inputHeight() int {
180 return lipgloss.Height(f.inputStyle.Render(f.input.View()))
180 return lipgloss.Height(f.inputStyle.Render(f.input.View()))
183−type groupMatch[T FilterableItem] struct {
188func (f *filterableGroupList[T]) clearItemState() []tea.Cmd {
183func (f *filterableGroupList[T]) clearItemState() []tea.Cmd {
189 var cmds []tea.Cmd
184 var cmds []tea.Cmd
190 for _, item := range slices.Collect(f.items.Seq()) {
185 for _, item := range slices.Collect(f.items.Seq()) {
@@ -211,25 +206,6 @@ func (f *filterableGroupList[T]) setMatchIndexes(item T, indexes []int) {
214−func (f *filterableGroupList[T]) findMatchingGroups(firstWord string) []groupMatch[T] {
215− var matchedGroups []groupMatch[T]
216− for _, g := range f.groups {
217− groupName := f.getGroupName(g)
218− matches := fuzzy.Find(firstWord, []string{groupName})
219− if len(matches) > 0 && matches[0].Score > 0 {
220− matchedGroups = append(matchedGroups, groupMatch[T]{
222− score: matches[0].Score,
226− // Sort by score (higher scores first - exact matches will have higher scores)
227− sort.SliceStable(matchedGroups, func(i, j int) bool {
228− return matchedGroups[i].score > matchedGroups[j].score
230− return matchedGroups
233func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string) []T {
209func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string) []T {
234 if query == "" {
210 if query == "" {
235 // No query, return all items with cleared match indexes
211 // No query, return all items with cleared match indexes
@@ -241,24 +217,31 @@ func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string
241 return items
217 return items
244− // Build search words
220+ name := f.getGroupName(group) + " "
245− words := make([]string, len(group.Items))
222+ names := make([]string, len(group.Items))
246 for i, item := range group.Items {
223 for i, item := range group.Items {
247− words[i] = strings.ToLower(item.FilterValue())
224+ names[i] = strings.ToLower(name + item.FilterValue())
250− // Perform fuzzy search
251− matches := fuzzy.Find(query, words)
227+ matches := fuzzy.Find(query, names)
252 sort.SliceStable(matches, func(i, j int) bool {
228 sort.SliceStable(matches, func(i, j int) bool {
253 return matches[i].Score > matches[j].Score
229 return matches[i].Score > matches[j].Score
256 if len(matches) > 0 {
232 if len(matches) > 0 {
257− // Found matches, return only those with highlights
258 var matchedItems []T
233 var matchedItems []T
259 for _, match := range matches {
234 for _, match := range matches {
260 item := group.Items[match.Index]
235 item := group.Items[match.Index]
237+ for _, idx := range match.MatchedIndexes {
238+ // adjusts removing group name highlights
239+ if idx < len(name) {
242+ idxs = append(idxs, idx-len(name))
261− f.setMatchIndexes(item, match.MatchedIndexes)
244+ f.setMatchIndexes(item, idxs)
262 matchedItems = append(matchedItems, item)
245 matchedItems = append(matchedItems, item)
264 return matchedItems
247 return matchedItems
@@ -267,20 +250,6 @@ func (f *filterableGroupList[T]) filterItemsInGroup(group Group[T], query string
267 return []T{}
250 return []T{}
270−func (f *filterableGroupList[T]) searchAllGroups(query string) []Group[T] {
271− var newGroups []Group[T]
272− for _, g := range f.groups {
273− matchedItems := f.filterItemsInGroup(g, query)
274− if len(matchedItems) > 0 {
275− newGroups = append(newGroups, Group[T]{
277− Items: matchedItems,
284func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
253func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
285 cmds := f.clearItemState()
254 cmds := f.clearItemState()
286 f.selectedItem = ""
255 f.selectedItem = ""
@@ -289,51 +258,24 @@ func (f *filterableGroupList[T]) Filter(query string) tea.Cmd {
289 return f.groupedList.SetGroups(f.groups)
258 return f.groupedList.SetGroups(f.groups)
292− lowerQuery := strings.ToLower(query)
261+ query = strings.ToLower(strings.ReplaceAll(query, " ", ""))
293− queryWords := strings.Fields(lowerQuery)
294− firstWord := queryWords[0]
296− // Find groups that match the first word
297− matchedGroups := f.findMatchingGroups(firstWord)
299− var newGroups []Group[T]
263+ var result []Group[T]
300− if len(matchedGroups) > 0 {
264+ for _, g := range f.groups {
301− // Filter within matched groups using remaining words
265+ if matches := fuzzy.Find(query, []string{f.getGroupName(g)}); len(matches) > 0 && matches[0].Score > 0 {
302− remainingQuery := ""
266+ result = append(result, g)
303− if len(queryWords) > 1 {
267+ continue
304− remainingQuery = strings.Join(queryWords[1:], " ")
307− for _, matchedGroup := range matchedGroups {
308− matchedItems := f.filterItemsInGroup(matchedGroup.group, remainingQuery)
309− if len(matchedItems) > 0 {
310− newGroups = append(newGroups, Group[T]{
311− Section: matchedGroup.group.Section,
312− Items: matchedItems,
316−
269+ matchedItems := f.filterItemsInGroup(g, query)
317− // add any matching items from other groups
270+ if len(matchedItems) > 0 {
318− allGroups := f.searchAllGroups(lowerQuery)
271+ result = append(result, Group[T]{
319− for _, g := range allGroups {
272+ Section: g.Section,
320− exists := false
273+ Items: matchedItems,
321− for _, existing := range newGroups {
322− if existing.Section.ID() == g.Section.ID() {
328− newGroups = append(newGroups, g)
332− // No group matches, search all groups
333− newGroups = f.searchAllGroups(lowerQuery)
336− cmds = append(cmds, f.groupedList.SetGroups(newGroups))
278+ cmds = append(cmds, f.groupedList.SetGroups(result))
337 return tea.Batch(cmds...)
279 return tea.Batch(cmds...)