on main help sign in
chore: rebase fix + sync map
bb668b1Kujtim Hoxha
go.mod+1−2
@@ -12,9 +12,8 @@ require (
12 github.com/bmatcuk/doublestar/v4 v4.9.0
12 github.com/bmatcuk/doublestar/v4 v4.9.0
13 github.com/charlievieth/fastwalk v1.0.11
13 github.com/charlievieth/fastwalk v1.0.11
14 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5
14 github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5
15− github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250717140350-bb75e8f6b6ac
16− github.com/charmbracelet/catwalk v0.3.1
17 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69
15 github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69
16+ github.com/charmbracelet/catwalk v0.3.1
18 github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674
17 github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674
19 github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
18 github.com/charmbracelet/glamour/v2 v2.0.0-20250516160903-6f1e2c8f9ebe
20 github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250716211347-10c048e36112
19 github.com/charmbracelet/lipgloss/v2 v2.0.0-beta.3.0.20250716211347-10c048e36112
go.sum+2−4
@@ -70,12 +70,10 @@ github.com/charlievieth/fastwalk v1.0.11 h1:5sLT/q9+d9xMdpKExawLppqvXFZCVKf6JHnr
70github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
70github.com/charlievieth/fastwalk v1.0.11/go.mod h1:yGy1zbxog41ZVMcKA/i8ojXLFsuayX5VvwhQVoj9PBI=
71github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5 h1:GTcMIfDQJKyNKS+xVt7GkNIwz+tBuQtIuiP50WpzNgs=
71github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5 h1:GTcMIfDQJKyNKS+xVt7GkNIwz+tBuQtIuiP50WpzNgs=
72github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
72github.com/charmbracelet/bubbles/v2 v2.0.0-beta.1.0.20250716191546-1e2ffbbcf5c5/go.mod h1:6HamsBKWqEC/FVHuQMHgQL+knPyvHH55HwJDHl/adMw=
73−github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250717140350-bb75e8f6b6ac h1:murtkvFYxZ/73vk4Z/tpE4biB+WDZcFmmBp8je/yV6M=
74−github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250717140350-bb75e8f6b6ac/go.mod h1:m240IQxo1/eDQ7klblSzOCAUyc3LddHcV3Rc/YEGAgw=
75−github.com/charmbracelet/catwalk v0.3.1 h1:MkGWspcMyE659zDkqS+9wsaCMTKRFEDBFY2A2sap6+U=
76−github.com/charmbracelet/catwalk v0.3.1/go.mod h1:gUUCqqZ8bk4D7ZzGTu3I77k7cC2x4exRuJBN1H2u2pc=
77github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69 h1:nXLMl4ows2qogDXhuEtDNgFNXQiU+PJer+UEBsQZuns=
73github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69 h1:nXLMl4ows2qogDXhuEtDNgFNXQiU+PJer+UEBsQZuns=
78github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69/go.mod h1:XIQ1qQfRph6Z5o2EikCydjumo0oDInQySRHuPATzbZc=
74github.com/charmbracelet/bubbletea/v2 v2.0.0-beta.4.0.20250724172607-5ba56e2bec69/go.mod h1:XIQ1qQfRph6Z5o2EikCydjumo0oDInQySRHuPATzbZc=
75+github.com/charmbracelet/catwalk v0.3.1 h1:MkGWspcMyE659zDkqS+9wsaCMTKRFEDBFY2A2sap6+U=
76+github.com/charmbracelet/catwalk v0.3.1/go.mod h1:gUUCqqZ8bk4D7ZzGTu3I77k7cC2x4exRuJBN1H2u2pc=
79github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
77github.com/charmbracelet/colorprofile v0.3.1 h1:k8dTHMd7fgw4bnFd7jXTLZrSU/CQrKnL3m+AxCzDz40=
80github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
78github.com/charmbracelet/colorprofile v0.3.1/go.mod h1:/GkGusxNs8VB/RSOh3fu0TJmQ4ICMMPApIIVn0KszZ0=
81github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674 h1:+Cz+VfxD5DO+JT1LlswXWhre0HYLj6l2HW8HVGfMuC0=
79github.com/charmbracelet/fang v0.3.1-0.20250711140230-d5ebb8c1d674 h1:+Cz+VfxD5DO+JT1LlswXWhre0HYLj6l2HW8HVGfMuC0=
internal/tui/components/completions/completions.go+10−12
@@ -122,25 +122,23 @@ func (c *completionsCmp) Update(msg tea.Msg) (tea.Model, tea.Cmd) {
122 c.list = d.(listModel)
122 c.list = d.(listModel)
123 return c, cmd
123 return c, cmd
124 case key.Matches(msg, c.keyMap.UpInsert):
124 case key.Matches(msg, c.keyMap.UpInsert):
125− selectedItemInx := c.list.SelectedIndex() - 1
125+ s := c.list.SelectedItem()
126− items := c.list.Items()
126+ if s == nil {
127− if selectedItemInx == list.NoSelection || selectedItemInx < 0 {
128− return c, nil // No item selected, do nothing
127+ return c, nil
129 }
128 }
130− selectedItem := items[selectedItemInx].(CompletionItem).Value()
129+ selectedItem := *s
131− c.list.SetSelected(selectedItemInx)
130+ c.list.SetSelected(selectedItem.ID())
132 return c, util.CmdHandler(SelectCompletionMsg{
131 return c, util.CmdHandler(SelectCompletionMsg{
133 Value: selectedItem,
132 Value: selectedItem,
134 Insert: true,
133 Insert: true,
135 })
134 })
136 case key.Matches(msg, c.keyMap.DownInsert):
135 case key.Matches(msg, c.keyMap.DownInsert):
137− selectedItemInx := c.list.SelectedIndex() + 1
136+ s := c.list.SelectedItem()
138− items := c.list.Items()
137+ if s == nil {
139− if selectedItemInx == list.NoSelection || selectedItemInx >= len(items) {
140− return c, nil // No item selected, do nothing
138+ return c, nil
141 }
139 }
142− selectedItem := items[selectedItemInx].(CompletionItem).Value()
140+ selectedItem := *s
143− c.list.SetSelected(selectedItemInx)
141+ c.list.SetSelected(selectedItem.ID())
144 return c, util.CmdHandler(SelectCompletionMsg{
142 return c, util.CmdHandler(SelectCompletionMsg{
145 Value: selectedItem,
143 Value: selectedItem,
146 Insert: true,
144 Insert: true,
internal/tui/components/dialogs/models/list.go+3−3
@@ -157,7 +157,7 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
157 Section: section,
157 Section: section,
158 }
158 }
159 for _, model := range configProvider.Models {
159 for _, model := range configProvider.Models {
160− item := list.NewCompletionItem(model.Model, ModelOption{
160+ item := list.NewCompletionItem(model.Name, ModelOption{
161 Provider: configProvider,
161 Provider: configProvider,
162 Model: model,
162 Model: model,
163 },
163 },
@@ -195,14 +195,14 @@ func (m *ModelListComponent) SetModelType(modelType int) tea.Cmd {
195 }
195 }
196
196
197 section := list.NewItemSection(name)
197 section := list.NewItemSection(name)
198− if _, ok := cfg.Providers[string(provider.ID)]; ok {
198+ if _, ok := cfg.Providers.Get(string(provider.ID)); ok {
199 section.SetInfo(configured)
199 section.SetInfo(configured)
200 }
200 }
201 group := list.Group[list.CompletionItem[ModelOption]]{
201 group := list.Group[list.CompletionItem[ModelOption]]{
202 Section: section,
202 Section: section,
203 }
203 }
204 for _, model := range provider.Models {
204 for _, model := range provider.Models {
205− item := list.NewCompletionItem(model.Model, ModelOption{
205+ item := list.NewCompletionItem(model.Name, ModelOption{
206 Provider: provider,
206 Provider: provider,
207 Model: model,
207 Model: model,
208 },
208 },
internal/tui/exp/list/grouped.go+3−2
@@ -2,6 +2,7 @@ package list
2
2
3import (
3import (
4 tea "github.com/charmbracelet/bubbletea/v2"
4 tea "github.com/charmbracelet/bubbletea/v2"
5+ "github.com/charmbracelet/crush/internal/csync"
5 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
6 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
6 "github.com/charmbracelet/crush/internal/tui/util"
7 "github.com/charmbracelet/crush/internal/tui/util"
7)
8)
@@ -37,8 +38,8 @@ func NewGroupedList[T Item](groups []Group[T], opts ...ListOption) GroupedList[T
37 keyMap: DefaultKeyMap(),
38 keyMap: DefaultKeyMap(),
38 focused: true,
39 focused: true,
39 },
40 },
40− indexMap: make(map[string]int),
41+ indexMap: csync.NewMap[string, int](),
41− renderedItems: map[string]renderedItem{},
42+ renderedItems: csync.NewMap[string, renderedItem](),
42 }
43 }
43 for _, opt := range opts {
44 for _, opt := range opts {
44 opt(list.confOptions)
45 opt(list.confOptions)
internal/tui/exp/list/list.go+68−50
@@ -6,6 +6,7 @@ import (
6
6
7 "github.com/charmbracelet/bubbles/v2/key"
7 "github.com/charmbracelet/bubbles/v2/key"
8 tea "github.com/charmbracelet/bubbletea/v2"
8 tea "github.com/charmbracelet/bubbletea/v2"
9+ "github.com/charmbracelet/crush/internal/csync"
9 "github.com/charmbracelet/crush/internal/tui/components/anim"
10 "github.com/charmbracelet/crush/internal/tui/components/anim"
10 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
11 "github.com/charmbracelet/crush/internal/tui/components/core/layout"
11 "github.com/charmbracelet/crush/internal/tui/styles"
12 "github.com/charmbracelet/crush/internal/tui/styles"
@@ -84,10 +85,10 @@ type list[T Item] struct {
84
85
85 offset int
86 offset int
86
87
87− indexMap map[string]int
88+ indexMap *csync.Map[string, int]
88 items []T
89 items []T
89
90
90− renderedItems map[string]renderedItem
91+ renderedItems *csync.Map[string, renderedItem]
91
92
92 rendered string
93 rendered string
93
94
@@ -164,8 +165,8 @@ func New[T Item](items []T, opts ...ListOption) List[T] {
164 focused: true,
165 focused: true,
165 },
166 },
166 items: items,
167 items: items,
167− indexMap: make(map[string]int),
168+ indexMap: csync.NewMap[string, int](),
168− renderedItems: map[string]renderedItem{},
169+ renderedItems: csync.NewMap[string, renderedItem](),
169 }
170 }
170 for _, opt := range opts {
171 for _, opt := range opts {
171 opt(list.confOptions)
172 opt(list.confOptions)
@@ -175,7 +176,7 @@ func New[T Item](items []T, opts ...ListOption) List[T] {
175 if i, ok := any(item).(Indexable); ok {
176 if i, ok := any(item).(Indexable); ok {
176 i.SetIndex(inx)
177 i.SetIndex(inx)
177 }
178 }
178− list.indexMap[item.ID()] = inx
179+ list.indexMap.Set(item.ID(), inx)
179 }
180 }
180 return list
181 return list
181}
182}
@@ -267,13 +268,13 @@ func (l *list[T]) viewPosition() (int, int) {
267func (l *list[T]) recalculateItemPositions() {
268func (l *list[T]) recalculateItemPositions() {
268 currentContentHeight := 0
269 currentContentHeight := 0
269 for _, item := range l.items {
270 for _, item := range l.items {
270− rItem, ok := l.renderedItems[item.ID()]
271+ rItem, ok := l.renderedItems.Get(item.ID())
271 if !ok {
272 if !ok {
272 continue
273 continue
273 }
274 }
274 rItem.start = currentContentHeight
275 rItem.start = currentContentHeight
275 rItem.end = currentContentHeight + rItem.height - 1
276 rItem.end = currentContentHeight + rItem.height - 1
276− l.renderedItems[item.ID()] = rItem
277+ l.renderedItems.Set(item.ID(), rItem)
277 currentContentHeight = rItem.end + 1 + l.gap
278 currentContentHeight = rItem.end + 1 + l.gap
278 }
279 }
279}
280}
@@ -337,7 +338,7 @@ func (l *list[T]) setDefaultSelected() {
337}
338}
338
339
339func (l *list[T]) scrollToSelection() {
340func (l *list[T]) scrollToSelection() {
340− rItem, ok := l.renderedItems[l.selectedItem]
341+ rItem, ok := l.renderedItems.Get(l.selectedItem)
341 if !ok {
342 if !ok {
342 l.selectedItem = ""
343 l.selectedItem = ""
343 l.setDefaultSelected()
344 l.setDefaultSelected()
@@ -395,7 +396,7 @@ func (l *list[T]) scrollToSelection() {
395}
396}
396
397
397func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
398func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
398− rItem, ok := l.renderedItems[l.selectedItem]
399+ rItem, ok := l.renderedItems.Get(l.selectedItem)
399 if !ok {
400 if !ok {
400 return nil
401 return nil
401 }
402 }
@@ -414,13 +415,16 @@ func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
414 if itemMiddle < start {
415 if itemMiddle < start {
415 // select the first item in the viewport
416 // select the first item in the viewport
416 // the item is most likely an item coming after this item
417 // the item is most likely an item coming after this item
417− inx := l.indexMap[rItem.id]
418+ inx, ok := l.indexMap.Get(rItem.id)
419+ if !ok {
420+ return nil
421+ }
418 for {
422 for {
419 inx = l.firstSelectableItemBelow(inx)
423 inx = l.firstSelectableItemBelow(inx)
420 if inx == ItemNotFound {
424 if inx == ItemNotFound {
421 return nil
425 return nil
422 }
426 }
423− item, ok := l.renderedItems[l.items[inx].ID()]
427+ item, ok := l.renderedItems.Get(l.items[inx].ID())
424 if !ok {
428 if !ok {
425 continue
429 continue
426 }
430 }
@@ -439,13 +443,16 @@ func (l *list[T]) changeSelectionWhenScrolling() tea.Cmd {
439 } else if itemMiddle > end {
443 } else if itemMiddle > end {
440 // select the first item in the viewport
444 // select the first item in the viewport
441 // the item is most likely an item coming after this item
445 // the item is most likely an item coming after this item
442− inx := l.indexMap[rItem.id]
446+ inx, ok := l.indexMap.Get(rItem.id)
447+ if !ok {
448+ return nil
449+ }
443 for {
450 for {
444 inx = l.firstSelectableItemAbove(inx)
451 inx = l.firstSelectableItemAbove(inx)
445 if inx == ItemNotFound {
452 if inx == ItemNotFound {
446 return nil
453 return nil
447 }
454 }
448− item, ok := l.renderedItems[l.items[inx].ID()]
455+ item, ok := l.renderedItems.Get(l.items[inx].ID())
449 if !ok {
456 if !ok {
450 continue
457 continue
451 }
458 }
@@ -512,10 +519,10 @@ func (l *list[T]) focusSelectedItem() tea.Cmd {
512 if f, ok := any(item).(layout.Focusable); ok {
519 if f, ok := any(item).(layout.Focusable); ok {
513 if item.ID() == l.selectedItem && !f.IsFocused() {
520 if item.ID() == l.selectedItem && !f.IsFocused() {
514 cmds = append(cmds, f.Focus())
521 cmds = append(cmds, f.Focus())
515− delete(l.renderedItems, item.ID())
522+ l.renderedItems.Del(item.ID())
516 } else if item.ID() != l.selectedItem && f.IsFocused() {
523 } else if item.ID() != l.selectedItem && f.IsFocused() {
517 cmds = append(cmds, f.Blur())
524 cmds = append(cmds, f.Blur())
518− delete(l.renderedItems, item.ID())
525+ l.renderedItems.Del(item.ID())
519 }
526 }
520 }
527 }
521 }
528 }
@@ -531,7 +538,7 @@ func (l *list[T]) blurSelectedItem() tea.Cmd {
531 if f, ok := any(item).(layout.Focusable); ok {
538 if f, ok := any(item).(layout.Focusable); ok {
532 if item.ID() == l.selectedItem && f.IsFocused() {
539 if item.ID() == l.selectedItem && f.IsFocused() {
533 cmds = append(cmds, f.Blur())
540 cmds = append(cmds, f.Blur())
534− delete(l.renderedItems, item.ID())
541+ l.renderedItems.Del(item.ID())
535 }
542 }
536 }
543 }
537 }
544 }
@@ -555,13 +562,13 @@ func (l *list[T]) renderIterator(startInx int, limitHeight bool) int {
555
562
556 item := l.items[inx]
563 item := l.items[inx]
557 var rItem renderedItem
564 var rItem renderedItem
558− if cache, ok := l.renderedItems[item.ID()]; ok {
565+ if cache, ok := l.renderedItems.Get(item.ID()); ok {
559 rItem = cache
566 rItem = cache
560 } else {
567 } else {
561 rItem = l.renderItem(item)
568 rItem = l.renderItem(item)
562 rItem.start = currentContentHeight
569 rItem.start = currentContentHeight
563 rItem.end = currentContentHeight + rItem.height - 1
570 rItem.end = currentContentHeight + rItem.height - 1
564− l.renderedItems[item.ID()] = rItem
571+ l.renderedItems.Set(item.ID(), rItem)
565 }
572 }
566 gap := l.gap + 1
573 gap := l.gap + 1
567 if inx == len(l.items)-1 {
574 if inx == len(l.items)-1 {
@@ -596,9 +603,9 @@ func (l *list[T]) AppendItem(item T) tea.Cmd {
596 }
603 }
597
604
598 l.items = append(l.items, item)
605 l.items = append(l.items, item)
599− l.indexMap = make(map[string]int)
606+ l.indexMap = csync.NewMap[string, int]()
600 for inx, item := range l.items {
607 for inx, item := range l.items {
601− l.indexMap[item.ID()] = inx
608+ l.indexMap.Set(item.ID(), inx)
602 }
609 }
603 if l.width > 0 && l.height > 0 {
610 if l.width > 0 && l.height > 0 {
604 cmd = item.SetSize(l.width, l.height)
611 cmd = item.SetSize(l.width, l.height)
@@ -617,12 +624,14 @@ func (l *list[T]) AppendItem(item T) tea.Cmd {
617 cmds = append(cmds, cmd)
624 cmds = append(cmds, cmd)
618 }
625 }
619 } else {
626 } else {
620− newItem := l.renderedItems[item.ID()]
627+ newItem, ok := l.renderedItems.Get(item.ID())
628+ if ok {
621− newLines := newItem.height
629+ newLines := newItem.height
622− if len(l.items) > 1 {
630+ if len(l.items) > 1 {
623− newLines += l.gap
631+ newLines += l.gap
632+ }
633+ l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
624 }
634 }
625− l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
626 }
635 }
627 }
636 }
628 return tea.Sequence(cmds...)
637 return tea.Sequence(cmds...)
@@ -636,11 +645,14 @@ func (l *list[T]) Blur() tea.Cmd {
636
645
637// DeleteItem implements List.
646// DeleteItem implements List.
638func (l *list[T]) DeleteItem(id string) tea.Cmd {
647func (l *list[T]) DeleteItem(id string) tea.Cmd {
639− inx := l.indexMap[id]
648+ inx, ok := l.indexMap.Get(id)
649+ if !ok {
650+ return nil
651+ }
640 l.items = slices.Delete(l.items, inx, inx+1)
652 l.items = slices.Delete(l.items, inx, inx+1)
641− delete(l.renderedItems, id)
653+ l.renderedItems.Del(id)
642 for inx, item := range l.items {
654 for inx, item := range l.items {
643− l.indexMap[item.ID()] = inx
655+ l.indexMap.Set(item.ID(), inx)
644 }
656 }
645
657
646 if l.selectedItem == id {
658 if l.selectedItem == id {
@@ -753,9 +765,9 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
753 item.Init(),
765 item.Init(),
754 }
766 }
755 l.items = append([]T{item}, l.items...)
767 l.items = append([]T{item}, l.items...)
756− l.indexMap = make(map[string]int)
768+ l.indexMap = csync.NewMap[string, int]()
757 for inx, item := range l.items {
769 for inx, item := range l.items {
758− l.indexMap[item.ID()] = inx
770+ l.indexMap.Set(item.ID(), inx)
759 }
771 }
760 if l.width > 0 && l.height > 0 {
772 if l.width > 0 && l.height > 0 {
761 cmds = append(cmds, item.SetSize(l.width, l.height))
773 cmds = append(cmds, item.SetSize(l.width, l.height))
@@ -768,12 +780,14 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
768 cmds = append(cmds, cmd)
780 cmds = append(cmds, cmd)
769 }
781 }
770 } else {
782 } else {
771− newItem := l.renderedItems[item.ID()]
783+ newItem, ok := l.renderedItems.Get(item.ID())
784+ if ok {
772− newLines := newItem.height
785+ newLines := newItem.height
773− if len(l.items) > 1 {
786+ if len(l.items) > 1 {
774− newLines += l.gap
787+ newLines += l.gap
788+ }
789+ l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
775 }
790 }
776− l.offset = min(lipgloss.Height(l.rendered)-1, l.offset+newLines)
777 }
791 }
778 }
792 }
779 return tea.Batch(cmds...)
793 return tea.Batch(cmds...)
@@ -781,7 +795,7 @@ func (l *list[T]) PrependItem(item T) tea.Cmd {
781
795
782// SelectItemAbove implements List.
796// SelectItemAbove implements List.
783func (l *list[T]) SelectItemAbove() tea.Cmd {
797func (l *list[T]) SelectItemAbove() tea.Cmd {
784− inx, ok := l.indexMap[l.selectedItem]
798+ inx, ok := l.indexMap.Get(l.selectedItem)
785 if !ok {
799 if !ok {
786 return nil
800 return nil
787 }
801 }
@@ -815,7 +829,7 @@ func (l *list[T]) SelectItemAbove() tea.Cmd {
815
829
816// SelectItemBelow implements List.
830// SelectItemBelow implements List.
817func (l *list[T]) SelectItemBelow() tea.Cmd {
831func (l *list[T]) SelectItemBelow() tea.Cmd {
818− inx, ok := l.indexMap[l.selectedItem]
832+ inx, ok := l.indexMap.Get(l.selectedItem)
819 if !ok {
833 if !ok {
820 return nil
834 return nil
821 }
835 }
@@ -833,7 +847,7 @@ func (l *list[T]) SelectItemBelow() tea.Cmd {
833
847
834// SelectedItem implements List.
848// SelectedItem implements List.
835func (l *list[T]) SelectedItem() *T {
849func (l *list[T]) SelectedItem() *T {
836− inx, ok := l.indexMap[l.selectedItem]
850+ inx, ok := l.indexMap.Get(l.selectedItem)
837 if !ok {
851 if !ok {
838 return nil
852 return nil
839 }
853 }
@@ -869,10 +883,10 @@ func (l *list[T]) reset(selectedItem string) tea.Cmd {
869 l.rendered = ""
883 l.rendered = ""
870 l.offset = 0
884 l.offset = 0
871 l.selectedItem = selectedItem
885 l.selectedItem = selectedItem
872− l.indexMap = make(map[string]int)
886+ l.indexMap = csync.NewMap[string, int]()
873− l.renderedItems = make(map[string]renderedItem)
887+ l.renderedItems = csync.NewMap[string, renderedItem]()
874 for inx, item := range l.items {
888 for inx, item := range l.items {
875− l.indexMap[item.ID()] = inx
889+ l.indexMap.Set(item.ID(), inx)
876 if l.width > 0 && l.height > 0 {
890 if l.width > 0 && l.height > 0 {
877 cmds = append(cmds, item.SetSize(l.width, l.height))
891 cmds = append(cmds, item.SetSize(l.width, l.height))
878 }
892 }
@@ -896,22 +910,22 @@ func (l *list[T]) SetSize(width int, height int) tea.Cmd {
896// UpdateItem implements List.
910// UpdateItem implements List.
897func (l *list[T]) UpdateItem(id string, item T) tea.Cmd {
911func (l *list[T]) UpdateItem(id string, item T) tea.Cmd {
898 var cmds []tea.Cmd
912 var cmds []tea.Cmd
899− if inx, ok := l.indexMap[id]; ok {
913+ if inx, ok := l.indexMap.Get(id); ok {
900 l.items[inx] = item
914 l.items[inx] = item
901− oldItem := l.renderedItems[id]
915+ oldItem, hasOldItem := l.renderedItems.Get(id)
902 oldPosition := l.offset
916 oldPosition := l.offset
903 if l.direction == DirectionBackward {
917 if l.direction == DirectionBackward {
904 oldPosition = (lipgloss.Height(l.rendered) - 1) - l.offset
918 oldPosition = (lipgloss.Height(l.rendered) - 1) - l.offset
905 }
919 }
906
920
907− delete(l.renderedItems, id)
921+ l.renderedItems.Del(id)
908 cmd := l.render()
922 cmd := l.render()
909
923
910 // need to check for nil because of sequence not handling nil
924 // need to check for nil because of sequence not handling nil
911 if cmd != nil {
925 if cmd != nil {
912 cmds = append(cmds, cmd)
926 cmds = append(cmds, cmd)
913 }
927 }
914− if l.direction == DirectionBackward {
928+ if hasOldItem && l.direction == DirectionBackward {
915 // if we are the last item and there is no offset
929 // if we are the last item and there is no offset
916 // make sure to go to the bottom
930 // make sure to go to the bottom
917 if inx == len(l.items)-1 && l.offset == 0 {
931 if inx == len(l.items)-1 && l.offset == 0 {
@@ -921,14 +935,18 @@ func (l *list[T]) UpdateItem(id string, item T) tea.Cmd {
921 }
935 }
922 // if the item is at least partially below the viewport
936 // if the item is at least partially below the viewport
923 } else if oldPosition < oldItem.end {
937 } else if oldPosition < oldItem.end {
924− newItem := l.renderedItems[item.ID()]
938+ newItem, ok := l.renderedItems.Get(item.ID())
939+ if ok {
940+ newLines := newItem.height - oldItem.height
941+ l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
942+ }
943+ }
944+ } else if hasOldItem && l.offset > oldItem.start {
945+ newItem, ok := l.renderedItems.Get(item.ID())
946+ if ok {
925 newLines := newItem.height - oldItem.height
947 newLines := newItem.height - oldItem.height
926 l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
948 l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
927 }
949 }
928− } else if l.offset > oldItem.start {
929− newItem := l.renderedItems[item.ID()]
930− newLines := newItem.height - oldItem.height
931− l.offset = util.Clamp(l.offset+newLines, 0, lipgloss.Height(l.rendered)-1)
932 }
950 }
933 }
951 }
934 return tea.Sequence(cmds...)
952 return tea.Sequence(cmds...)
internal/tui/exp/list/list_test.go+32−22
@@ -29,17 +29,19 @@ func TestList(t *testing.T) {
29 // should select the last item
29 // should select the last item
30 assert.Equal(t, items[0].ID(), l.selectedItem)
30 assert.Equal(t, items[0].ID(), l.selectedItem)
31 assert.Equal(t, 0, l.offset)
31 assert.Equal(t, 0, l.offset)
32− require.Len(t, l.indexMap, 5)
32+ require.Equal(t, 5, l.indexMap.Len())
33 require.Len(t, l.items, 5)
33 require.Len(t, l.items, 5)
34− require.Len(t, l.renderedItems, 5)
34+ require.Equal(t, 5, l.renderedItems.Len())
35 assert.Equal(t, 5, lipgloss.Height(l.rendered))
35 assert.Equal(t, 5, lipgloss.Height(l.rendered))
36 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
36 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
37 start, end := l.viewPosition()
37 start, end := l.viewPosition()
38 assert.Equal(t, 0, start)
38 assert.Equal(t, 0, start)
39 assert.Equal(t, 4, end)
39 assert.Equal(t, 4, end)
40 for i := range 5 {
40 for i := range 5 {
41− assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
41+ item, ok := l.renderedItems.Get(items[i].ID())
42+ require.True(t, ok)
43+ assert.Equal(t, i, item.start)
42− assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
44+ assert.Equal(t, i, item.end)
43 }
45 }
44
46
45 golden.RequireEqual(t, []byte(l.View()))
47 golden.RequireEqual(t, []byte(l.View()))
@@ -57,17 +59,19 @@ func TestList(t *testing.T) {
57 // should select the last item
59 // should select the last item
58 assert.Equal(t, items[4].ID(), l.selectedItem)
60 assert.Equal(t, items[4].ID(), l.selectedItem)
59 assert.Equal(t, 0, l.offset)
61 assert.Equal(t, 0, l.offset)
60− require.Len(t, l.indexMap, 5)
62+ require.Equal(t, 5, l.indexMap.Len())
61 require.Len(t, l.items, 5)
63 require.Len(t, l.items, 5)
62− require.Len(t, l.renderedItems, 5)
64+ require.Equal(t, 5, l.renderedItems.Len())
63 assert.Equal(t, 5, lipgloss.Height(l.rendered))
65 assert.Equal(t, 5, lipgloss.Height(l.rendered))
64 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
66 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
65 start, end := l.viewPosition()
67 start, end := l.viewPosition()
66 assert.Equal(t, 0, start)
68 assert.Equal(t, 0, start)
67 assert.Equal(t, 4, end)
69 assert.Equal(t, 4, end)
68 for i := range 5 {
70 for i := range 5 {
69− assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
71+ item, ok := l.renderedItems.Get(items[i].ID())
72+ require.True(t, ok)
73+ assert.Equal(t, i, item.start)
70− assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
74+ assert.Equal(t, i, item.end)
71 }
75 }
72
76
73 golden.RequireEqual(t, []byte(l.View()))
77 golden.RequireEqual(t, []byte(l.View()))
@@ -86,17 +90,19 @@ func TestList(t *testing.T) {
86 // should select the last item
90 // should select the last item
87 assert.Equal(t, items[0].ID(), l.selectedItem)
91 assert.Equal(t, items[0].ID(), l.selectedItem)
88 assert.Equal(t, 0, l.offset)
92 assert.Equal(t, 0, l.offset)
89− require.Len(t, l.indexMap, 30)
93+ require.Equal(t, 30, l.indexMap.Len())
90 require.Len(t, l.items, 30)
94 require.Len(t, l.items, 30)
91− require.Len(t, l.renderedItems, 30)
95+ require.Equal(t, 30, l.renderedItems.Len())
92 assert.Equal(t, 30, lipgloss.Height(l.rendered))
96 assert.Equal(t, 30, lipgloss.Height(l.rendered))
93 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
97 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
94 start, end := l.viewPosition()
98 start, end := l.viewPosition()
95 assert.Equal(t, 0, start)
99 assert.Equal(t, 0, start)
96 assert.Equal(t, 9, end)
100 assert.Equal(t, 9, end)
97 for i := range 30 {
101 for i := range 30 {
98− assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
102+ item, ok := l.renderedItems.Get(items[i].ID())
103+ require.True(t, ok)
104+ assert.Equal(t, i, item.start)
99− assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
105+ assert.Equal(t, i, item.end)
100 }
106 }
101
107
102 golden.RequireEqual(t, []byte(l.View()))
108 golden.RequireEqual(t, []byte(l.View()))
@@ -114,17 +120,19 @@ func TestList(t *testing.T) {
114 // should select the last item
120 // should select the last item
115 assert.Equal(t, items[29].ID(), l.selectedItem)
121 assert.Equal(t, items[29].ID(), l.selectedItem)
116 assert.Equal(t, 0, l.offset)
122 assert.Equal(t, 0, l.offset)
117− require.Len(t, l.indexMap, 30)
123+ require.Equal(t, 30, l.indexMap.Len())
118 require.Len(t, l.items, 30)
124 require.Len(t, l.items, 30)
119− require.Len(t, l.renderedItems, 30)
125+ require.Equal(t, 30, l.renderedItems.Len())
120 assert.Equal(t, 30, lipgloss.Height(l.rendered))
126 assert.Equal(t, 30, lipgloss.Height(l.rendered))
121 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
127 assert.NotEqual(t, "\n", string(l.rendered[len(l.rendered)-1]), "should not end in newline")
122 start, end := l.viewPosition()
128 start, end := l.viewPosition()
123 assert.Equal(t, 20, start)
129 assert.Equal(t, 20, start)
124 assert.Equal(t, 29, end)
130 assert.Equal(t, 29, end)
125 for i := range 30 {
131 for i := range 30 {
126− assert.Equal(t, i, l.renderedItems[items[i].ID()].start)
132+ item, ok := l.renderedItems.Get(items[i].ID())
133+ require.True(t, ok)
134+ assert.Equal(t, i, item.start)
127− assert.Equal(t, i, l.renderedItems[items[i].ID()].end)
135+ assert.Equal(t, i, item.end)
128 }
136 }
129
137
130 golden.RequireEqual(t, []byte(l.View()))
138 golden.RequireEqual(t, []byte(l.View()))
@@ -145,9 +153,9 @@ func TestList(t *testing.T) {
145 // should select the last item
153 // should select the last item
146 assert.Equal(t, items[0].ID(), l.selectedItem)
154 assert.Equal(t, items[0].ID(), l.selectedItem)
147 assert.Equal(t, 0, l.offset)
155 assert.Equal(t, 0, l.offset)
148− require.Len(t, l.indexMap, 30)
156+ require.Equal(t, 30, l.indexMap.Len())
149 require.Len(t, l.items, 30)
157 require.Len(t, l.items, 30)
150− require.Len(t, l.renderedItems, 30)
158+ require.Equal(t, 30, l.renderedItems.Len())
151 expectedLines := 0
159 expectedLines := 0
152 for i := range 30 {
160 for i := range 30 {
153 expectedLines += (i + 1) * 1
161 expectedLines += (i + 1) * 1
@@ -159,7 +167,8 @@ func TestList(t *testing.T) {
159 assert.Equal(t, 9, end)
167 assert.Equal(t, 9, end)
160 currentPosition := 0
168 currentPosition := 0
161 for i := range 30 {
169 for i := range 30 {
162− rItem := l.renderedItems[items[i].ID()]
170+ rItem, ok := l.renderedItems.Get(items[i].ID())
171+ require.True(t, ok)
163 assert.Equal(t, currentPosition, rItem.start)
172 assert.Equal(t, currentPosition, rItem.start)
164 assert.Equal(t, currentPosition+i, rItem.end)
173 assert.Equal(t, currentPosition+i, rItem.end)
165 currentPosition += i + 1
174 currentPosition += i + 1
@@ -182,9 +191,9 @@ func TestList(t *testing.T) {
182 // should select the last item
191 // should select the last item
183 assert.Equal(t, items[29].ID(), l.selectedItem)
192 assert.Equal(t, items[29].ID(), l.selectedItem)
184 assert.Equal(t, 0, l.offset)
193 assert.Equal(t, 0, l.offset)
185− require.Len(t, l.indexMap, 30)
194+ require.Equal(t, 30, l.indexMap.Len())
186 require.Len(t, l.items, 30)
195 require.Len(t, l.items, 30)
187− require.Len(t, l.renderedItems, 30)
196+ require.Equal(t, 30, l.renderedItems.Len())
188 expectedLines := 0
197 expectedLines := 0
189 for i := range 30 {
198 for i := range 30 {
190 expectedLines += (i + 1) * 1
199 expectedLines += (i + 1) * 1
@@ -196,7 +205,8 @@ func TestList(t *testing.T) {
196 assert.Equal(t, expectedLines-1, end)
205 assert.Equal(t, expectedLines-1, end)
197 currentPosition := 0
206 currentPosition := 0
198 for i := range 30 {
207 for i := range 30 {
199− rItem := l.renderedItems[items[i].ID()]
208+ rItem, ok := l.renderedItems.Get(items[i].ID())
209+ require.True(t, ok)
200 assert.Equal(t, currentPosition, rItem.start)
210 assert.Equal(t, currentPosition, rItem.start)
201 assert.Equal(t, currentPosition+i, rItem.end)
211 assert.Equal(t, currentPosition+i, rItem.end)
202 currentPosition += i + 1
212 currentPosition += i + 1
internal/tui/exp/list/testdata/TestFilterableList/should_create_simple_filterable_list.golden+9−5
@@ -1,6 +1,10 @@
1> Type to filter 
1> Type to filter 
2−│Item 0
2+│Item 0 
3−Item 1
3+Item 1 
4−Item 2
4+Item 2 
5−Item 3
5+Item 3 
6−Item 4
6+Item 4 
7+
8+
9+
10+
internal/tui/exp/list/testdata/TestList/should_go_to_selected_item_at_the_beginning.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 10
1+│Item 10
2−│Item 10
2+│Item 10
3−│Item 10
3+│Item 10
4−│Item 10
4+│Item 10
5−│Item 10
5+│Item 10
6−│Item 10
6+│Item 10
7−│Item 10
7+│Item 10
8−│Item 10
8+│Item 10
9−│Item 10
9+│Item 10
10−│Item 10
10+│Item 10
internal/tui/exp/list/testdata/TestList/should_go_to_selected_item_at_the_beginning_backwards.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 10
1+│Item 10
2−│Item 10
2+│Item 10
3−│Item 10
3+│Item 10
4−│Item 10
4+│Item 10
5−│Item 10
5+│Item 10
6−│Item 10
6+│Item 10
7−│Item 10
7+│Item 10
8−│Item 10
8+│Item 10
9−│Item 10
9+│Item 10
10−│Item 10
10+│Item 10
internal/tui/exp/list/testdata/TestList/should_have_correct_positions_in_list_that_does_not_fits_the_items.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 0
1+│Item 0
2−Item 1
2+Item 1
3−Item 2
3+Item 2
4−Item 3
4+Item 3
5−Item 4
5+Item 4
6−Item 5
6+Item 5
7−Item 6
7+Item 6
8−Item 7
8+Item 7
9−Item 8
9+Item 8
10−Item 9
10+Item 9
internal/tui/exp/list/testdata/TestList/should_have_correct_positions_in_list_that_does_not_fits_the_items_and_has_multi_line_items.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 0
1+│Item 0
2−Item 1
2+Item 1
3−Item 1
3+Item 1
4−Item 2
4+Item 2
5−Item 2
5+Item 2
6−Item 2
6+Item 2
7−Item 3
7+Item 3
8−Item 3
8+Item 3
9−Item 3
9+Item 3
10−Item 3
10+Item 3
internal/tui/exp/list/testdata/TestList/should_have_correct_positions_in_list_that_does_not_fits_the_items_and_has_multi_line_items_backwards.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 29
1+│Item 29
2−│Item 29
2+│Item 29
3−│Item 29
3+│Item 29
4−│Item 29
4+│Item 29
5−│Item 29
5+│Item 29
6−│Item 29
6+│Item 29
7−│Item 29
7+│Item 29
8−│Item 29
8+│Item 29
9−│Item 29
9+│Item 29
10−│Item 29
10+│Item 29
internal/tui/exp/list/testdata/TestList/should_have_correct_positions_in_list_that_does_not_fits_the_items_backwards.golden+10−10
@@ -1,10 +1,10 @@
1−Item 20
1+Item 20
2−Item 21
2+Item 21
3−Item 22
3+Item 22
4−Item 23
4+Item 23
5−Item 24
5+Item 24
6−Item 25
6+Item 25
7−Item 26
7+Item 26
8−Item 27
8+Item 27
9−Item 28
9+Item 28
10−│Item 29
10+│Item 29
internal/tui/exp/list/testdata/TestList/should_have_correct_positions_in_list_that_fits_the_items.golden+20−5
@@ -1,5 +1,20 @@
1−│Item 0
1+│Item 0
2−Item 1
2+Item 1
3−Item 2
3+Item 2
4−Item 3
4+Item 3
5−Item 4
5+Item 4
6+
7+
8+
9+
10+
11+
12+
13+
14+
15+
16+
17+
18+
19+
20+
internal/tui/exp/list/testdata/TestList/should_have_correct_positions_in_list_that_fits_the_items_backwards.golden+20−5
@@ -1,5 +1,20 @@
1−Item 0
1+Item 0
2−Item 1
2+Item 1
3−Item 2
3+Item 2
4−Item 3
4+Item 3
5−│Item 4
5+│Item 4
6+
7+
8+
9+
10+
11+
12+
13+
14+
15+
16+
17+
18+
19+
20+
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_down.golden+10−10
@@ -1,10 +1,10 @@
1−Item 6
1+Item 6
2−Item 6
2+Item 6
3−Item 6
3+Item 6
4−│Item 7
4+│Item 7
5−│Item 7
5+│Item 7
6−│Item 7
6+│Item 7
7−│Item 7
7+│Item 7
8−│Item 7
8+│Item 7
9−│Item 7
9+│Item 7
10−│Item 7
10+│Item 7
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_down_and_up.golden+10−10
@@ -1,10 +1,10 @@
1−Item 0
1+Item 0
2−Item 1
2+Item 1
3−Item 1
3+Item 1
4−Item 2
4+Item 2
5−Item 2
5+Item 2
6−Item 2
6+Item 2
7−│Item 3
7+│Item 3
8−│Item 3
8+│Item 3
9−│Item 3
9+│Item 3
10−│Item 3
10+│Item 3
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_up.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 28
1+│Item 28
2−│Item 28
2+│Item 28
3−│Item 28
3+│Item 28
4−│Item 28
4+│Item 28
5−│Item 28
5+│Item 28
6−Item 29
6+Item 29
7−Item 29
7+Item 29
8−Item 29
8+Item 29
9−Item 29
9+Item 29
10−Item 29
10+Item 29
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_up_and_down.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 29
1+│Item 29
2−│Item 29
2+│Item 29
3−│Item 29
3+│Item 29
4−│Item 29
4+│Item 29
5−│Item 29
5+│Item 29
6−│Item 29
6+│Item 29
7−│Item 29
7+│Item 29
8−│Item 29
8+│Item 29
9−│Item 29
9+│Item 29
10−│Item 29
10+│Item 29
internal/tui/exp/list/testdata/TestListMovement/should_not_change_offset_when_new_items_are_appended_and_we_are_at_the_bottom_in_backwards_list.golden+10−10
@@ -1,10 +1,10 @@
1−Item 29
1+Item 29
2−Item 29
2+Item 29
3−Item 29
3+Item 29
4−Item 29
4+Item 29
5−Item 29
5+Item 29
6−Item 29
6+Item 29
7−Item 29
7+Item 29
8−Item 29
8+Item 29
9−Item 29
9+Item 29
10−│Testing
10+│Testing 
internal/tui/exp/list/testdata/TestListMovement/should_not_change_offset_when_new_items_are_prepended_and_we_are_at_the_top_in_forward_list.golden+10−10
@@ -1,10 +1,10 @@
1−│Testing
1+│Testing 
2−Item 0
2+Item 0
3−Item 1
3+Item 1
4−Item 1
4+Item 1
5−Item 2
5+Item 2
6−Item 2
6+Item 2
7−Item 2
7+Item 2
8−Item 3
8+Item 3
9−Item 3
9+Item 3
10−Item 3
10+Item 3
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_if_an_item_is_appended_and_we_are_in_forward_list.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 2
1+│Item 2
2−Item 3
2+Item 3
3−Item 4
3+Item 4
4−Item 5
4+Item 5
5−Item 6
5+Item 6
6−Item 7
6+Item 7
7−Item 8
7+Item 8
8−Item 9
8+Item 9
9−Item 10
9+Item 10
10−Item 11
10+Item 11
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_if_an_item_is_prepended_and_we_are_in_backwards_list.golden+10−10
@@ -1,10 +1,10 @@
1−Item 18
1+Item 18
2−Item 19
2+Item 19
3−Item 20
3+Item 20
4−Item 21
4+Item 21
5−Item 22
5+Item 22
6−Item 23
6+Item 23
7−Item 24
7+Item 24
8−Item 25
8+Item 25
9−Item 26
9+Item 26
10−│Item 27
10+│Item 27
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_new_items_are_added_but_we_moved_down_in_forward_list.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 2
1+│Item 2
2−Item 3
2+Item 3
3−Item 4
3+Item 4
4−Item 5
4+Item 5
5−Item 6
5+Item 6
6−Item 7
6+Item 7
7−Item 8
7+Item 8
8−Item 9
8+Item 9
9−Item 10
9+Item 10
10−Item 11
10+Item 11
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_new_items_are_added_but_we_moved_up_in_backwards_list.golden+10−10
@@ -1,10 +1,10 @@
1−Item 18
1+Item 18
2−Item 19
2+Item 19
3−Item 20
3+Item 20
4−Item 21
4+Item 21
5−Item 22
5+Item 22
6−Item 23
6+Item 23
7−Item 24
7+Item 24
8−Item 25
8+Item 25
9−Item 26
9+Item 26
10−│Item 27
10+│Item 27
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_the_hight_of_an_item_above_is_decreases_in_forward_list.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 0
1+│Item 0
2−Item 1
2+Item 1
3−Item 2
3+Item 2
4−Item 3
4+Item 3
5−Item 4
5+Item 4
6−Item 5
6+Item 5
7−Item 6
7+Item 6
8−Item 7
8+Item 7
9−Item 8
9+Item 8
10−Item 9
10+Item 9
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_the_hight_of_an_item_above_is_increased_in_backwards_list.golden+10−10
@@ -1,10 +1,10 @@
1−Item 18
1+Item 18
2−Item 19
2+Item 19
3−Item 20
3+Item 20
4−Item 21
4+Item 21
5−Item 22
5+Item 22
6−Item 23
6+Item 23
7−Item 24
7+Item 24
8−Item 25
8+Item 25
9−Item 26
9+Item 26
10−│Item 27
10+│Item 27
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_the_hight_of_an_item_above_is_increased_in_forward_list.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 2
1+│Item 2
2−Item 3
2+Item 3
3−Item 4
3+Item 4
4−Item 5
4+Item 5
5−Item 6
5+Item 6
6−Item 7
6+Item 7
7−Item 8
7+Item 8
8−Item 9
8+Item 9
9−Item 10
9+Item 10
10−Item 11
10+Item 11
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_the_hight_of_an_item_below_is_decreases_in_backwards_list.golden+10−10
@@ -1,10 +1,10 @@
1−Item 21
1+Item 21
2−Item 22
2+Item 22
3−Item 23
3+Item 23
4−Item 24
4+Item 24
5−Item 25
5+Item 25
6−Item 26
6+Item 26
7−Item 27
7+Item 27
8−Item 28
8+Item 28
9−│Item 29
9+│Item 29
10−Item 30
10+Item 30
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_the_hight_of_an_item_below_is_increased_in_backwards_list.golden+10−10
@@ -1,10 +1,10 @@
1−Item 18
1+Item 18
2−Item 19
2+Item 19
3−Item 20
3+Item 20
4−Item 21
4+Item 21
5−Item 22
5+Item 22
6−Item 23
6+Item 23
7−Item 24
7+Item 24
8−Item 25
8+Item 25
9−Item 26
9+Item 26
10−│Item 27
10+│Item 27
internal/tui/exp/list/testdata/TestListMovement/should_stay_at_the_position_it_is_when_the_hight_of_an_item_below_is_increased_in_forward_list.golden+10−10
@@ -1,10 +1,10 @@
1−│Item 2
1+│Item 2
2−Item 3
2+Item 3
3−Item 4
3+Item 4
4−Item 5
4+Item 5
5−Item 6
5+Item 6
6−Item 7
6+Item 7
7−Item 8
7+Item 8
8−Item 9
8+Item 9
9−Item 10
9+Item 10
10−Item 11
10+Item 11