mirror of
https://github.com/charmbracelet/crush.git
synced 2025-08-02 05:20:46 +03:00
chore: rebase fix + sync map
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_down.golden
generated
vendored
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_down.golden
generated
vendored
@@ -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
|
||||
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_down_and_up.golden
generated
vendored
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_down_and_up.golden
generated
vendored
@@ -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
|
||||
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_up.golden
generated
vendored
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_up.golden
generated
vendored
@@ -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
|
||||
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_up_and_down.golden
generated
vendored
20
internal/tui/exp/list/testdata/TestListMovement/should_move_viewport_up_and_down.golden
generated
vendored
@@ -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
|
||||
Reference in New Issue
Block a user