Detailed changes
  
  
    
    @@ -12,9 +12,8 @@ require (
 	github.com/bmatcuk/doublestar/v4 v4.9.0
 	github.com/charlievieth/fastwalk v1.0.11
 	github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5
-	github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250717140350-bb75e8f6b6ac
-	github.com/charmbracelet/catwalk v0.3.1
 	github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69
+	github.com/charmbracelet/catwalk v0.3.1
 	github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674
 	github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
 	github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250716211347-10c048e36112
  
  
  
    
    @@ -70,12 +70,10 @@ github.com/charlievieth/fastwalk v1.0.11 h1:5sLT/q9+d9xMdpKExawLppqvXFZCVKf6JHnr
 github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5 h1:GTcMIfDQJKyNKS+xVt7GkNIwz+tBuQtIuiP50WpzNgs=
 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
-github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250717140350-bb75e8f6b6ac h1:murtkvFYxZ/73vk4Z/tpE4biB+WDZcFmmBp8je/yV6M=
-github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250717140350-bb75e8f6b6ac/go.mod h1:m240IQxo1/eDQ7klblSzOCAUyc3LddHcV3Rc/YEGAgw=
-github.com/charmbracelet/catwalk v0.3.1 h1:MkGWspcMyE659zDkqS+9wsaCMTKRFEDBFY2A2sap6+U=
-github.com/charmbracelet/catwalk v0.3.1/go.mod h1:gUUCqqZ8bk4D7ZzGTu3I77k7cC2x4exRuJBN1H2u2pc=
 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69 h1:nXLMl4ows2qogDXhuEtDNgFNXQiU+PJer+UEBsQZuns=
 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69/go.mod h1:XIQ1qQfRph6Z5o2EikCydjumo0oDInQySRHuPATzbZc=
+github.com/charmbracelet/catwalk v0.3.1 h1:MkGWspcMyE659zDkqS+9wsaCMTKRFEDBFY2A2sap6+U=
+github.com/charmbracelet/catwalk v0.3.1/go.mod h1:gUUCqqZ8bk4D7ZzGTu3I77k7cC2x4exRuJBN1H2u2pc=
 github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
 github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
 github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674 h1:+Cz+VfxD5DO+JT1LlswXWhre0HYLj6l2HW8HVGfMuC0=
  
  
  
    
    @@ -122,25 +122,23 @@ func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
 			c.list = d.(listModel)
 			return c, cmd
 		case key.Matches(msg, c.keyMap.UpInsert):
-			selectedItemInx := c.list.SelectedIndex() - 1
-			items := c.list.Items()
-			if selectedItemInx == list.NoSelection || selectedItemInx < 0 {
-				return c, nil // No item selected, do nothing
+			s := c.list.SelectedItem()
+			if s == nil {
+				return c, nil
 			}
-			selectedItem := items[selectedItemInx].(CompletionItem).Value()
-			c.list.SetSelected(selectedItemInx)
+			selectedItem := *s
+			c.list.SetSelected(selectedItem.ID())
 			return c, util.CmdHandler(SelectCompletionMsg{
 				Value:  selectedItem,
 				Insert: true,
 			})
 		case key.Matches(msg, c.keyMap.DownInsert):
-			selectedItemInx := c.list.SelectedIndex() + 1
-			items := c.list.Items()
-			if selectedItemInx == list.NoSelection || selectedItemInx >= len(items) {
-				return c, nil // No item selected, do nothing
+			s := c.list.SelectedItem()
+			if s == nil {
+				return c, nil
 			}
-			selectedItem := items[selectedItemInx].(CompletionItem).Value()
-			c.list.SetSelected(selectedItemInx)
+			selectedItem := *s
+			c.list.SetSelected(selectedItem.ID())
 			return c, util.CmdHandler(SelectCompletionMsg{
 				Value:  selectedItem,
 				Insert: true,
  
  
  
    
    @@ -157,7 +157,7 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
 				Section: section,
 			}
 			for _, model := range configProvider.Models {
-				item := list.NewCompletionItem(model.Model, ModelOption{
+				item := list.NewCompletionItem(model.Name, ModelOption{
 					Provider: configProvider,
 					Model:    model,
 				},
@@ -195,14 +195,14 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
 		}
 
 		section := list.NewItemSection(name)
-		if _, ok := cfg.Providers[string(provider.ID)]; ok {
+		if _, ok := cfg.Providers.Get(string(provider.ID)); ok {
 			section.SetInfo(configured)
 		}
 		group := list.Group[list.CompletionItem[ModelOption]]{
 			Section: section,
 		}
 		for _, model := range provider.Models {
-			item := list.NewCompletionItem(model.Model, ModelOption{
+			item := list.NewCompletionItem(model.Name, ModelOption{
 				Provider: provider,
 				Model:    model,
 			},
  
  
  
    
    @@ -2,6 +2,7 @@ package list
 
 import (
 	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/crush/internal/csync"
 	"github.com/charmbracelet/crush/internal/tui/components/core/layout"
 	"github.com/charmbracelet/crush/internal/tui/util"
 )
@@ -37,8 +38,8 @@ func NewGroupedList[T Item](groups []Group[T], opts ...ListOption) GroupedList[T
 			keyMap:    DefaultKeyMap(),
 			focused:   true,
 		},
-		indexMap:      make(map[string]int),
-		renderedItems: map[string]renderedItem{},
+		indexMap:      csync.NewMap[string, int](),
+		renderedItems: csync.NewMap[string, renderedItem](),
 	}
 	for _, opt := range opts {
 		opt(list.confOptions)
  
  
  
    
    @@ -6,6 +6,7 @@ import (
 
 	"github.com/charmbracelet/bubbles/v2/key"
 	tea "github.com/charmbracelet/bubbletea/v2"
+	"github.com/charmbracelet/crush/internal/csync"
 	"github.com/charmbracelet/crush/internal/tui/components/anim"
 	"github.com/charmbracelet/crush/internal/tui/components/core/layout"
 	"github.com/charmbracelet/crush/internal/tui/styles"
@@ -84,10 +85,10 @@ type list[T Item] struct {
 
 	offset int
 
-	indexMap map[string]int
+	indexMap *csync.Map[string, int]
 	items    []T
 
-	renderedItems map[string]renderedItem
+	renderedItems *csync.Map[string, renderedItem]
 
 	rendered string
 
@@ -164,8 +165,8 @@ func New[T Item](items []T, opts ...ListOption) List[T] {
 			focused:   true,
 		},
 		items:         items,
-		indexMap:      make(map[string]int),
-		renderedItems: map[string]renderedItem{},
+		indexMap:      csync.NewMap[string, int](),
+		renderedItems: csync.NewMap[string, renderedItem](),
 	}
 	for _, opt := range opts {
 		opt(list.confOptions)
@@ -175,7 +176,7 @@ func New[T Item](items []T, opts ...ListOption) List[T] {
 		if i, ok := any(item).(Indexable); ok {
 			i.SetIndex(inx)
 		}
-		list.indexMap[item.ID()] = inx
+		list.indexMap.Set(item.ID(), inx)
 	}
 	return list
 }
@@ -267,13 +268,13 @@ func (l *list[T]) viewPosition() (int, int) {
 func (l *list[T]) recalculateItemPositions() {
 	currentContentHeight := 0
 	for _, item := range l.items {
-		rItem, ok := l.renderedItems[item.ID()]
+		rItem, ok := l.renderedItems.Get(item.ID())
 		if !ok {
 			continue
 		}
 		rItem.start = currentContentHeight
 		rItem.end = currentContentHeight + rItem.height - 1
-		l.renderedItems[item.ID()] = rItem
+		l.renderedItems.Set(item.ID(), rItem)
 		currentContentHeight = rItem.end + 1 + l.gap
 	}
 }
@@ -337,7 +338,7 @@ func (l *list[T]) setDefaultSelected() {
 }
 
 func (l *list[T]) scrollToSelection() {
-	rItem, ok := l.renderedItems[l.selectedItem]
+	rItem, ok := l.renderedItems.Get(l.selectedItem)
 	if !ok {
 		l.selectedItem = ""
 		l.setDefaultSelected()
@@ -395,7 +396,7 @@ func (l *list[T]) scrollToSelection() {
 }
 
 func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
-	rItem, ok := l.renderedItems[l.selectedItem]
+	rItem, ok := l.renderedItems.Get(l.selectedItem)
 	if !ok {
 		return nil
 	}
@@ -414,13 +415,16 @@ func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
 	if itemMiddle < start {
 		// select the first item in the viewport
 		// the item is most likely an item coming after this item
-		inx := l.indexMap[rItem.id]
+		inx, ok := l.indexMap.Get(rItem.id)
+		if !ok {
+			return nil
+		}
 		for {
 			inx = l.firstSelectableItemBelow(inx)
 			if inx == ItemNotFound {
 				return nil
 			}
-			item, ok := l.renderedItems[l.items[inx].ID()]
+			item, ok := l.renderedItems.Get(l.items[inx].ID())
 			if !ok {
 				continue
 			}
@@ -439,13 +443,16 @@ func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
 	} else if itemMiddle > end {
 		// select the first item in the viewport
 		// the item is most likely an item coming after this item
-		inx := l.indexMap[rItem.id]
+		inx, ok := l.indexMap.Get(rItem.id)
+		if !ok {
+			return nil
+		}
 		for {
 			inx = l.firstSelectableItemAbove(inx)
 			if inx == ItemNotFound {
 				return nil
 			}
-			item, ok := l.renderedItems[l.items[inx].ID()]
+			item, ok := l.renderedItems.Get(l.items[inx].ID())
 			if !ok {
 				continue
 			}
@@ -512,10 +519,10 @@ func (l *list[T]) focusSelectedItem() tea.Cmd {
 		if f, ok := any(item).(layout.Focusable); ok {
 			if item.ID() == l.selectedItem && !f.IsFocused() {
 				cmds = append(cmds, f.Focus())
-				delete(l.renderedItems, item.ID())
+				l.renderedItems.Del(item.ID())
 			} else if item.ID() != l.selectedItem && f.IsFocused() {
 				cmds = append(cmds, f.Blur())
-				delete(l.renderedItems, item.ID())
+				l.renderedItems.Del(item.ID())
 			}
 		}
 	}
@@ -531,7 +538,7 @@ func (l *list[T]) blurSelectedItem() tea.Cmd {
 		if f, ok := any(item).(layout.Focusable); ok {
 			if item.ID() == l.selectedItem && f.IsFocused() {
 				cmds = append(cmds, f.Blur())
-				delete(l.renderedItems, item.ID())
+				l.renderedItems.Del(item.ID())
 			}
 		}
 	}
@@ -555,13 +562,13 @@ func (l *list[T]) renderIterator(startInx int, limitHeight bool) int {
 
 		item := l.items[inx]
 		var rItem renderedItem
-		if cache, ok := l.renderedItems[item.ID()]; ok {
+		if cache, ok := l.renderedItems.Get(item.ID()); ok {
 			rItem = cache
 		} else {
 			rItem = l.renderItem(item)
 			rItem.start = currentContentHeight
 			rItem.end = currentContentHeight + rItem.height - 1
-			l.renderedItems[item.ID()] = rItem
+			l.renderedItems.Set(item.ID(), rItem)
 		}
 		gap := l.gap + 1
 		if inx == len(l.items)-1 {
@@ -596,9 +603,9 @@ func (l *list[T]) AppendItem(item T) tea.Cmd {
 	}
 
 	l.items = append(l.items, item)
-	l.indexMap = make(map[string]int)
+	l.indexMap = csync.NewMap[string, int]()
 	for inx, item := range l.items {
-		l.indexMap[item.ID()] = inx
+		l.indexMap.Set(item.ID(), inx)
 	}
 	if l.width > 0 && l.height > 0 {
 		cmd = item.SetSize(l.width, l.height)
@@ -617,12 +624,14 @@ func (l *list[T]) AppendItem(item T) tea.Cmd {
 				cmds = append(cmds, cmd)
 			}
 		} else {
-			newItem := l.renderedItems[item.ID()]
-			newLines := newItem.height
-			if len(l.items) > 1 {
-				newLines += l.gap
+			newItem, ok := l.renderedItems.Get(item.ID())
+			if ok {
+				newLines := newItem.height
+				if len(l.items) > 1 {
+					newLines += l.gap
+				}
+				l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
 			}
-			l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
 		}
 	}
 	return tea.Sequence(cmds...)
@@ -636,11 +645,14 @@ func (l *list[T]) Blur() tea.Cmd {
 
 // DeleteItem implements List.
 func (l *list[T]) DeleteItem(id string) tea.Cmd {
-	inx := l.indexMap[id]
+	inx, ok := l.indexMap.Get(id)
+	if !ok {
+		return nil
+	}
 	l.items = slices.Delete(l.items, inx, inx+1)
-	delete(l.renderedItems, id)
+	l.renderedItems.Del(id)
 	for inx, item := range l.items {
-		l.indexMap[item.ID()] = inx
+		l.indexMap.Set(item.ID(), inx)
 	}
 
 	if l.selectedItem == id {
@@ -753,9 +765,9 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
 		item.Init(),
 	}
 	l.items = append([]T{item}, l.items...)
-	l.indexMap = make(map[string]int)
+	l.indexMap = csync.NewMap[string, int]()
 	for inx, item := range l.items {
-		l.indexMap[item.ID()] = inx
+		l.indexMap.Set(item.ID(), inx)
 	}
 	if l.width > 0 && l.height > 0 {
 		cmds = append(cmds, item.SetSize(l.width, l.height))
@@ -768,12 +780,14 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
 				cmds = append(cmds, cmd)
 			}
 		} else {
-			newItem := l.renderedItems[item.ID()]
-			newLines := newItem.height
-			if len(l.items) > 1 {
-				newLines += l.gap
+			newItem, ok := l.renderedItems.Get(item.ID())
+			if ok {
+				newLines := newItem.height
+				if len(l.items) > 1 {
+					newLines += l.gap
+				}
+				l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
 			}
-			l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
 		}
 	}
 	return tea.Batch(cmds...)
@@ -781,7 +795,7 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
 
 // SelectItemAbove implements List.
 func (l *list[T]) SelectItemAbove() tea.Cmd {
-	inx, ok := l.indexMap[l.selectedItem]
+	inx, ok := l.indexMap.Get(l.selectedItem)
 	if !ok {
 		return nil
 	}
@@ -815,7 +829,7 @@ func (l *list[T]) SelectItemAbove() tea.Cmd {
 
 // SelectItemBelow implements List.
 func (l *list[T]) SelectItemBelow() tea.Cmd {
-	inx, ok := l.indexMap[l.selectedItem]
+	inx, ok := l.indexMap.Get(l.selectedItem)
 	if !ok {
 		return nil
 	}
@@ -833,7 +847,7 @@ func (l *list[T]) SelectItemBelow() tea.Cmd {
 
 // SelectedItem implements List.
 func (l *list[T]) SelectedItem() *T {
-	inx, ok := l.indexMap[l.selectedItem]
+	inx, ok := l.indexMap.Get(l.selectedItem)
 	if !ok {
 		return nil
 	}
@@ -869,10 +883,10 @@ func (l *list[T]) reset(selectedItem string) tea.Cmd {
 	l.rendered = ""
 	l.offset = 0
 	l.selectedItem = selectedItem
-	l.indexMap = make(map[string]int)
-	l.renderedItems = make(map[string]renderedItem)
+	l.indexMap = csync.NewMap[string, int]()
+	l.renderedItems = csync.NewMap[string, renderedItem]()
 	for inx, item := range l.items {
-		l.indexMap[item.ID()] = inx
+		l.indexMap.Set(item.ID(), inx)
 		if l.width > 0 && l.height > 0 {
 			cmds = append(cmds, item.SetSize(l.width, l.height))
 		}
@@ -896,22 +910,22 @@ func (l *list[T]) SetSize(width int, height int) tea.Cmd {
 // UpdateItem implements List.
 func (l *list[T]) UpdateItem(id string, item T) tea.Cmd {
 	var cmds []tea.Cmd
-	if inx, ok := l.indexMap[id]; ok {
+	if inx, ok := l.indexMap.Get(id); ok {
 		l.items[inx] = item
-		oldItem := l.renderedItems[id]
+		oldItem, hasOldItem := l.renderedItems.Get(id)
 		oldPosition := l.offset
 		if l.direction == DirectionBackward {
 			oldPosition = (lipgloss.Height(l.rendered) - 1) - l.offset
 		}
 
-		delete(l.renderedItems, id)
+		l.renderedItems.Del(id)
 		cmd := l.render()
 
 		// need to check for nil because of sequence not handling nil
 		if cmd != nil {
 			cmds = append(cmds, cmd)
 		}
-		if l.direction == DirectionBackward {
+		if hasOldItem && l.direction == DirectionBackward {
 			// if we are the last item and there is no offset
 			// make sure to go to the bottom
 			if inx == len(l.items)-1 && l.offset == 0 {
@@ -921,14 +935,18 @@ func (l *list[T]) UpdateItem(id string, item T) tea.Cmd {
 				}
 				// if the item is at least partially below the viewport
 			} else if oldPosition < oldItem.end {
-				newItem := l.renderedItems[item.ID()]
+				newItem, ok := l.renderedItems.Get(item.ID())
+				if ok {
+					newLines := newItem.height - oldItem.height
+					l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
+				}
+			}
+		} else if hasOldItem && l.offset > oldItem.start {
+			newItem, ok := l.renderedItems.Get(item.ID())
+			if ok {
 				newLines := newItem.height - oldItem.height
 				l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
 			}
-		} else if l.offset > oldItem.start {
-			newItem := l.renderedItems[item.ID()]
-			newLines := newItem.height - oldItem.height
-			l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
 		}
 	}
 	return tea.Sequence(cmds...)
  
  
  
    
    @@ -29,17 +29,19 @@ func TestList(t *testing.T) {
 		// should select the last item
 		assert.Equal(t, items[0].ID(), l.selectedItem)
 		assert.Equal(t, 0, l.offset)
-		require.Len(t, l.indexMap, 5)
+		require.Equal(t, 5, l.indexMap.Len())
 		require.Len(t, l.items, 5)
-		require.Len(t, l.renderedItems, 5)
+		require.Equal(t, 5, l.renderedItems.Len())
 		assert.Equal(t, 5, lipgloss.Height(l.rendered))
 		assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
 		start, end := l.viewPosition()
 		assert.Equal(t, 0, start)
 		assert.Equal(t, 4, end)
 		for i := range 5 {
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
+			item, ok := l.renderedItems.Get(items[i].ID())
+			require.True(t, ok)
+			assert.Equal(t, i, item.start)
+			assert.Equal(t, i, item.end)
 		}
 
 		golden.RequireEqual(t, []byte(l.View()))
@@ -57,17 +59,19 @@ func TestList(t *testing.T) {
 		// should select the last item
 		assert.Equal(t, items[4].ID(), l.selectedItem)
 		assert.Equal(t, 0, l.offset)
-		require.Len(t, l.indexMap, 5)
+		require.Equal(t, 5, l.indexMap.Len())
 		require.Len(t, l.items, 5)
-		require.Len(t, l.renderedItems, 5)
+		require.Equal(t, 5, l.renderedItems.Len())
 		assert.Equal(t, 5, lipgloss.Height(l.rendered))
 		assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
 		start, end := l.viewPosition()
 		assert.Equal(t, 0, start)
 		assert.Equal(t, 4, end)
 		for i := range 5 {
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
+			item, ok := l.renderedItems.Get(items[i].ID())
+			require.True(t, ok)
+			assert.Equal(t, i, item.start)
+			assert.Equal(t, i, item.end)
 		}
 
 		golden.RequireEqual(t, []byte(l.View()))
@@ -86,17 +90,19 @@ func TestList(t *testing.T) {
 		// should select the last item
 		assert.Equal(t, items[0].ID(), l.selectedItem)
 		assert.Equal(t, 0, l.offset)
-		require.Len(t, l.indexMap, 30)
+		require.Equal(t, 30, l.indexMap.Len())
 		require.Len(t, l.items, 30)
-		require.Len(t, l.renderedItems, 30)
+		require.Equal(t, 30, l.renderedItems.Len())
 		assert.Equal(t, 30, lipgloss.Height(l.rendered))
 		assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
 		start, end := l.viewPosition()
 		assert.Equal(t, 0, start)
 		assert.Equal(t, 9, end)
 		for i := range 30 {
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
+			item, ok := l.renderedItems.Get(items[i].ID())
+			require.True(t, ok)
+			assert.Equal(t, i, item.start)
+			assert.Equal(t, i, item.end)
 		}
 
 		golden.RequireEqual(t, []byte(l.View()))
@@ -114,17 +120,19 @@ func TestList(t *testing.T) {
 		// should select the last item
 		assert.Equal(t, items[29].ID(), l.selectedItem)
 		assert.Equal(t, 0, l.offset)
-		require.Len(t, l.indexMap, 30)
+		require.Equal(t, 30, l.indexMap.Len())
 		require.Len(t, l.items, 30)
-		require.Len(t, l.renderedItems, 30)
+		require.Equal(t, 30, l.renderedItems.Len())
 		assert.Equal(t, 30, lipgloss.Height(l.rendered))
 		assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
 		start, end := l.viewPosition()
 		assert.Equal(t, 20, start)
 		assert.Equal(t, 29, end)
 		for i := range 30 {
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
-			assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
+			item, ok := l.renderedItems.Get(items[i].ID())
+			require.True(t, ok)
+			assert.Equal(t, i, item.start)
+			assert.Equal(t, i, item.end)
 		}
 
 		golden.RequireEqual(t, []byte(l.View()))
@@ -145,9 +153,9 @@ func TestList(t *testing.T) {
 		// should select the last item
 		assert.Equal(t, items[0].ID(), l.selectedItem)
 		assert.Equal(t, 0, l.offset)
-		require.Len(t, l.indexMap, 30)
+		require.Equal(t, 30, l.indexMap.Len())
 		require.Len(t, l.items, 30)
-		require.Len(t, l.renderedItems, 30)
+		require.Equal(t, 30, l.renderedItems.Len())
 		expectedLines := 0
 		for i := range 30 {
 			expectedLines += (i + 1) * 1
@@ -159,7 +167,8 @@ func TestList(t *testing.T) {
 		assert.Equal(t, 9, end)
 		currentPosition := 0
 		for i := range 30 {
-			rItem := l.renderedItems[items[i].ID()]
+			rItem, ok := l.renderedItems.Get(items[i].ID())
+			require.True(t, ok)
 			assert.Equal(t, currentPosition, rItem.start)
 			assert.Equal(t, currentPosition+i, rItem.end)
 			currentPosition += i + 1
@@ -182,9 +191,9 @@ func TestList(t *testing.T) {
 		// should select the last item
 		assert.Equal(t, items[29].ID(), l.selectedItem)
 		assert.Equal(t, 0, l.offset)
-		require.Len(t, l.indexMap, 30)
+		require.Equal(t, 30, l.indexMap.Len())
 		require.Len(t, l.items, 30)
-		require.Len(t, l.renderedItems, 30)
+		require.Equal(t, 30, l.renderedItems.Len())
 		expectedLines := 0
 		for i := range 30 {
 			expectedLines += (i + 1) * 1
@@ -196,7 +205,8 @@ func TestList(t *testing.T) {
 		assert.Equal(t, expectedLines-1, end)
 		currentPosition := 0
 		for i := range 30 {
-			rItem := l.renderedItems[items[i].ID()]
+			rItem, ok := l.renderedItems.Get(items[i].ID())
+			require.True(t, ok)
 			assert.Equal(t, currentPosition, rItem.start)
 			assert.Equal(t, currentPosition+i, rItem.end)
 			currentPosition += i + 1
  
  
  
    
    @@ -1,6 +1,10 @@
 [38;2;223;219;221m[38;2;104;255;214m> [m[38;2;96;95;107mT[m[38;2;96;95;107mype to filter[m[38;2;96;95;107m                                                                                       [m[m
-│Item 0                                                                                                
-Item 1                                                                                                 
-Item 2                                                                                                 
-Item 3                                                                                                 
-Item 4                                                                                                 
+[38;2;223;219;221m│Item 0                                                                                             [m   
+[38;2;223;219;221mItem 1                                                                                              [m   
+[38;2;223;219;221mItem 2                                                                                              [m   
+[38;2;223;219;221mItem 3                                                                                              [m   
+[38;2;223;219;221mItem 4                                                                                              [m   
+                                                                                                       
+                                                                                                       
+                                                                                                       
+                                                                                                       
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
-│Item 10
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
+[38;2;223;219;221m│Item 10[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 0
-Item 1
-Item 2
-Item 3
-Item 4
-Item 5
-Item 6
-Item 7
-Item 8
-Item 9
+[38;2;223;219;221m│Item 0[m   
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+[38;2;223;219;221mItem 5[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 7[m    
+[38;2;223;219;221mItem 8[m    
+[38;2;223;219;221mItem 9[m    
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 0
-Item 1
-Item 1
-Item 2
-Item 2
-Item 2
-Item 3
-Item 3
-Item 3
-Item 3
+[38;2;223;219;221m│Item 0[m   
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 3[m    
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 20
-Item 21
-Item 22
-Item 23
-Item 24
-Item 25
-Item 26
-Item 27
-Item 28
-│Item 29
+[38;2;223;219;221mItem 20[m   
+[38;2;223;219;221mItem 21[m   
+[38;2;223;219;221mItem 22[m   
+[38;2;223;219;221mItem 23[m   
+[38;2;223;219;221mItem 24[m   
+[38;2;223;219;221mItem 25[m   
+[38;2;223;219;221mItem 26[m   
+[38;2;223;219;221mItem 27[m   
+[38;2;223;219;221mItem 28[m   
+[38;2;223;219;221m│Item 29[m  
  
  
  
    
    @@ -1,5 +1,20 @@
-│Item 0
-Item 1
-Item 2
-Item 3
-Item 4
+[38;2;223;219;221m│Item 0[m   
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
  
  
  
    
    @@ -1,5 +1,20 @@
-Item 0
-Item 1
-Item 2
-Item 3
-│Item 4
+[38;2;223;219;221mItem 0[m    
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221m│Item 4[m   
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
+          
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 6
-Item 6
-Item 6
-│Item 7
-│Item 7
-│Item 7
-│Item 7
-│Item 7
-│Item 7
-│Item 7
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221m│Item 7[m   
+[38;2;223;219;221m│Item 7[m   
+[38;2;223;219;221m│Item 7[m   
+[38;2;223;219;221m│Item 7[m   
+[38;2;223;219;221m│Item 7[m   
+[38;2;223;219;221m│Item 7[m   
+[38;2;223;219;221m│Item 7[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 0
-Item 1
-Item 1
-Item 2
-Item 2
-Item 2
-│Item 3
-│Item 3
-│Item 3
-│Item 3
+[38;2;223;219;221mItem 0[m    
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221m│Item 3[m   
+[38;2;223;219;221m│Item 3[m   
+[38;2;223;219;221m│Item 3[m   
+[38;2;223;219;221m│Item 3[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 28
-│Item 28
-│Item 28
-│Item 28
-│Item 28
-Item 29
-Item 29
-Item 29
-Item 29
-Item 29
+[38;2;223;219;221m│Item 28[m  
+[38;2;223;219;221m│Item 28[m  
+[38;2;223;219;221m│Item 28[m  
+[38;2;223;219;221m│Item 28[m  
+[38;2;223;219;221m│Item 28[m  
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
-│Item 29
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221m│Item 29[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 29
-Item 29
-Item 29
-Item 29
-Item 29
-Item 29
-Item 29
-Item 29
-Item 29
-│Testing  
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221mItem 29[m   
+[38;2;223;219;221m│Testing  [m
  
  
  
    
    @@ -1,10 +1,10 @@
-│Testing  
-Item 0
-Item 1
-Item 1
-Item 2
-Item 2
-Item 2
-Item 3
-Item 3
-Item 3
+[38;2;223;219;221m│Testing  [m
+[38;2;223;219;221mItem 0[m    
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 3[m    
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 2
-Item 3
-Item 4
-Item 5
-Item 6
-Item 7
-Item 8
-Item 9
-Item 10
-Item 11
+[38;2;223;219;221m│Item 2[m   
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+[38;2;223;219;221mItem 5[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 7[m    
+[38;2;223;219;221mItem 8[m    
+[38;2;223;219;221mItem 9[m    
+[38;2;223;219;221mItem 10[m   
+[38;2;223;219;221mItem 11[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 18
-Item 19
-Item 20
-Item 21
-Item 22
-Item 23
-Item 24
-Item 25
-Item 26
-│Item 27
+[38;2;223;219;221mItem 18[m   
+[38;2;223;219;221mItem 19[m   
+[38;2;223;219;221mItem 20[m   
+[38;2;223;219;221mItem 21[m   
+[38;2;223;219;221mItem 22[m   
+[38;2;223;219;221mItem 23[m   
+[38;2;223;219;221mItem 24[m   
+[38;2;223;219;221mItem 25[m   
+[38;2;223;219;221mItem 26[m   
+[38;2;223;219;221m│Item 27[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 2
-Item 3
-Item 4
-Item 5
-Item 6
-Item 7
-Item 8
-Item 9
-Item 10
-Item 11
+[38;2;223;219;221m│Item 2[m   
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+[38;2;223;219;221mItem 5[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 7[m    
+[38;2;223;219;221mItem 8[m    
+[38;2;223;219;221mItem 9[m    
+[38;2;223;219;221mItem 10[m   
+[38;2;223;219;221mItem 11[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 18
-Item 19
-Item 20
-Item 21
-Item 22
-Item 23
-Item 24
-Item 25
-Item 26
-│Item 27
+[38;2;223;219;221mItem 18[m   
+[38;2;223;219;221mItem 19[m   
+[38;2;223;219;221mItem 20[m   
+[38;2;223;219;221mItem 21[m   
+[38;2;223;219;221mItem 22[m   
+[38;2;223;219;221mItem 23[m   
+[38;2;223;219;221mItem 24[m   
+[38;2;223;219;221mItem 25[m   
+[38;2;223;219;221mItem 26[m   
+[38;2;223;219;221m│Item 27[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 0
-Item 1
-Item 2
-Item 3
-Item 4
-Item 5
-Item 6
-Item 7
-Item 8
-Item 9
+[38;2;223;219;221m│Item 0[m   
+[38;2;223;219;221mItem 1[m    
+[38;2;223;219;221mItem 2[m    
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+[38;2;223;219;221mItem 5[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 7[m    
+[38;2;223;219;221mItem 8[m    
+[38;2;223;219;221mItem 9[m    
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 18
-Item 19
-Item 20
-Item 21
-Item 22
-Item 23
-Item 24
-Item 25
-Item 26
-│Item 27
+[38;2;223;219;221mItem 18[m   
+[38;2;223;219;221mItem 19[m   
+[38;2;223;219;221mItem 20[m   
+[38;2;223;219;221mItem 21[m   
+[38;2;223;219;221mItem 22[m   
+[38;2;223;219;221mItem 23[m   
+[38;2;223;219;221mItem 24[m   
+[38;2;223;219;221mItem 25[m   
+[38;2;223;219;221mItem 26[m   
+[38;2;223;219;221m│Item 27[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 2
-Item 3
-Item 4
-Item 5
-Item 6
-Item 7
-Item 8
-Item 9
-Item 10
-Item 11
+[38;2;223;219;221m│Item 2[m   
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+[38;2;223;219;221mItem 5[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 7[m    
+[38;2;223;219;221mItem 8[m    
+[38;2;223;219;221mItem 9[m    
+[38;2;223;219;221mItem 10[m   
+[38;2;223;219;221mItem 11[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 21
-Item 22
-Item 23
-Item 24
-Item 25
-Item 26
-Item 27
-Item 28
-│Item 29
-Item 30
+[38;2;223;219;221mItem 21[m   
+[38;2;223;219;221mItem 22[m   
+[38;2;223;219;221mItem 23[m   
+[38;2;223;219;221mItem 24[m   
+[38;2;223;219;221mItem 25[m   
+[38;2;223;219;221mItem 26[m   
+[38;2;223;219;221mItem 27[m   
+[38;2;223;219;221mItem 28[m   
+[38;2;223;219;221m│Item 29[m  
+[38;2;223;219;221mItem 30[m   
  
  
  
    
    @@ -1,10 +1,10 @@
-Item 18
-Item 19
-Item 20
-Item 21
-Item 22
-Item 23
-Item 24
-Item 25
-Item 26
-│Item 27
+[38;2;223;219;221mItem 18[m   
+[38;2;223;219;221mItem 19[m   
+[38;2;223;219;221mItem 20[m   
+[38;2;223;219;221mItem 21[m   
+[38;2;223;219;221mItem 22[m   
+[38;2;223;219;221mItem 23[m   
+[38;2;223;219;221mItem 24[m   
+[38;2;223;219;221mItem 25[m   
+[38;2;223;219;221mItem 26[m   
+[38;2;223;219;221m│Item 27[m  
  
  
  
    
    @@ -1,10 +1,10 @@
-│Item 2
-Item 3
-Item 4
-Item 5
-Item 6
-Item 7
-Item 8
-Item 9
-Item 10
-Item 11
+[38;2;223;219;221m│Item 2[m   
+[38;2;223;219;221mItem 3[m    
+[38;2;223;219;221mItem 4[m    
+[38;2;223;219;221mItem 5[m    
+[38;2;223;219;221mItem 6[m    
+[38;2;223;219;221mItem 7[m    
+[38;2;223;219;221mItem 8[m    
+[38;2;223;219;221mItem 9[m    
+[38;2;223;219;221mItem 10[m   
+[38;2;223;219;221mItem 11[m