@@ -16,8 +16,7 @@ type List struct {
16 width, height int
16 width, height int
19− items []Item
19+ items []Item
20− indexMap map[string]int // ID -> index for fast lookup
22 // Focus & Selection
21 // Focus & Selection
23 focused bool
22 focused bool
@@ -28,23 +27,23 @@ type List struct {
28 totalHeight int
27 totalHeight int
30 // Item positioning in master buffer
29 // Item positioning in master buffer
31− itemPositions map[string]itemPosition
30+ itemPositions []itemPosition
33 // Viewport state
32 // Viewport state
34 offset int // Scroll offset in lines from top
33 offset int // Scroll offset in lines from top
36 // Mouse state
35 // Mouse state
37 mouseDown bool
36 mouseDown bool
38− mouseDownItem string // Item ID where mouse was pressed
37+ mouseDownItem int // Item index where mouse was pressed
39− mouseDownX int // X position in item content (character offset)
38+ mouseDownX int // X position in item content (character offset)
40− mouseDownY int // Y position in item (line offset)
39+ mouseDownY int // Y position in item (line offset)
41− mouseDragItem string // Current item being dragged over
40+ mouseDragItem int // Current item index being dragged over
42− mouseDragX int // Current X in item content
41+ mouseDragX int // Current X in item content
43− mouseDragY int // Current Y in item
42+ mouseDragY int // Current Y in item
45 // Dirty tracking
44 // Dirty tracking
46 dirty bool
45 dirty bool
47− dirtyItems map[string]bool
46+ dirtyItems map[int]bool
50type itemPosition struct {
49type itemPosition struct {
@@ -56,15 +55,11 @@ type itemPosition struct {
56func New(items ...Item) *List {
55func New(items ...Item) *List {
57 l := &List{
56 l := &List{
58 items: items,
57 items: items,
59− indexMap: make(map[string]int, len(items)),
60− itemPositions: make(map[string]itemPosition, len(items)),
58+ itemPositions: make([]itemPosition, len(items)),
61− dirtyItems: make(map[string]bool),
59+ dirtyItems: make(map[int]bool),
62 selectedIdx: -1,
60 selectedIdx: -1,
63− }
61+ mouseDownItem: -1,
64−
62+ mouseDragItem: -1,
66− for i, item := range items {
67− l.indexMap[item.ID()] = i
70 l.dirty = true
65 l.dirty = true
@@ -251,7 +246,7 @@ func (l *List) rebuildMasterBuffer() {
252 // Draw each item
247 // Draw each item
253 currentY := 0
248 currentY := 0
254− for _, item := range l.items {
249+ for i, item := range l.items {
255 itemHeight := item.Height(l.width)
250 itemHeight := item.Height(l.width)
257 // Draw item to master buffer
252 // Draw item to master buffer
@@ -259,7 +254,7 @@ func (l *List) rebuildMasterBuffer() {
259 item.Draw(l.masterBuffer, area)
254 item.Draw(l.masterBuffer, area)
261 // Store position
256 // Store position
262− l.itemPositions[item.ID()] = itemPosition{
257+ l.itemPositions[i] = itemPosition{
263 startLine: currentY,
258 startLine: currentY,
264 height: itemHeight,
259 height: itemHeight,
@@ -269,7 +264,7 @@ func (l *List) rebuildMasterBuffer() {
271 l.dirty = false
266 l.dirty = false
272− l.dirtyItems = make(map[string]bool)
267+ l.dirtyItems = make(map[int]bool)
275// updateDirtyItems efficiently updates only changed items using slice operations.
270// updateDirtyItems efficiently updates only changed items using slice operations.
@@ -280,21 +275,9 @@ func (l *List) updateDirtyItems() {
281 // Check if all dirty items have unchanged heights
276 // Check if all dirty items have unchanged heights
282 allSameHeight := true
277 allSameHeight := true
283− for id := range l.dirtyItems {
278+ for idx := range l.dirtyItems {
284− idx, ok := l.indexMap[id]
289 item := l.items[idx]
279 item := l.items[idx]
290− pos, ok := l.itemPositions[id]
280+ pos := l.itemPositions[idx]
293− l.dirtyItems = make(map[string]bool)
294− l.rebuildMasterBuffer()
298 newHeight := item.Height(l.width)
281 newHeight := item.Height(l.width)
299 if newHeight != pos.height {
282 if newHeight != pos.height {
300 allSameHeight = false
283 allSameHeight = false
@@ -305,10 +288,9 @@ func (l *List) updateDirtyItems() {
305 // Optimization: If all dirty items have unchanged heights, re-render in place
288 // Optimization: If all dirty items have unchanged heights, re-render in place
306 if allSameHeight {
289 if allSameHeight {
307 buf := l.masterBuffer.Buffer
290 buf := l.masterBuffer.Buffer
308− for id := range l.dirtyItems {
291+ for idx := range l.dirtyItems {
309− idx := l.indexMap[id]
310 item := l.items[idx]
292 item := l.items[idx]
311− pos := l.itemPositions[id]
293+ pos := l.itemPositions[idx]
313 // Clear the item's area
295 // Clear the item's area
314 for y := pos.startLine; y < pos.startLine+pos.height && y < len(buf.Lines); y++ {
296 for y := pos.startLine; y < pos.startLine+pos.height && y < len(buf.Lines); y++ {
@@ -320,23 +302,22 @@ func (l *List) updateDirtyItems() {
320 item.Draw(l.masterBuffer, area)
302 item.Draw(l.masterBuffer, area)
323− l.dirtyItems = make(map[string]bool)
305+ l.dirtyItems = make(map[int]bool)
327 // Height changed - full rebuild
309 // Height changed - full rebuild
328 l.dirty = true
310 l.dirty = true
329− l.dirtyItems = make(map[string]bool)
311+ l.dirtyItems = make(map[int]bool)
330 l.rebuildMasterBuffer()
312 l.rebuildMasterBuffer()
333// updatePositionsBelow updates the startLine for all items below the given index.
315// updatePositionsBelow updates the startLine for all items below the given index.
334func (l *List) updatePositionsBelow(fromIdx int, delta int) {
316func (l *List) updatePositionsBelow(fromIdx int, delta int) {
335 for i := fromIdx + 1; i < len(l.items); i++ {
317 for i := fromIdx + 1; i < len(l.items); i++ {
337− pos := l.itemPositions[item.ID()]
318+ pos := l.itemPositions[i]
338 pos.startLine += delta
319 pos.startLine += delta
339− l.itemPositions[item.ID()] = pos
320+ l.itemPositions[i] = pos
@@ -395,13 +376,7 @@ func (l *List) Len() int {
395// SetItems replaces all items in the list.
376// SetItems replaces all items in the list.
396func (l *List) SetItems(items []Item) {
377func (l *List) SetItems(items []Item) {
397 l.items = items
378 l.items = items
398− l.indexMap = make(map[string]int, len(items))
399− l.itemPositions = make(map[string]itemPosition, len(items))
379+ l.itemPositions = make([]itemPosition, len(items))
401− for i, item := range items {
402− l.indexMap[item.ID()] = i
405 l.dirty = true
380 l.dirty = true
@@ -410,15 +385,15 @@ func (l *List) Items() []Item {
410 return l.items
385 return l.items
413−// AppendItem adds an item to the end of the list.
388+// AppendItem adds an item to the end of the list. Returns true if successful.
414−func (l *List) AppendItem(item Item) {
389+func (l *List) AppendItem(item Item) bool {
415 l.items = append(l.items, item)
390 l.items = append(l.items, item)
416− l.indexMap[item.ID()] = len(l.items) - 1
391+ l.itemPositions = append(l.itemPositions, itemPosition{})
418 // If buffer not built yet, mark dirty for full rebuild
393 // If buffer not built yet, mark dirty for full rebuild
419 if l.masterBuffer == nil || l.width <= 0 {
394 if l.masterBuffer == nil || l.width <= 0 {
420 l.dirty = true
395 l.dirty = true
421− return
396+ return true
424 // Process any pending dirty items before modifying buffer structure
399 // Process any pending dirty items before modifying buffer structure
@@ -442,23 +417,20 @@ func (l *List) AppendItem(item Item) {
442 item.Draw(l.masterBuffer, area)
417 item.Draw(l.masterBuffer, area)
444 // Update tracking
419 // Update tracking
445− l.itemPositions[item.ID()] = itemPosition{
420+ l.itemPositions[len(l.items)-1] = itemPosition{
446 startLine: startLine,
421 startLine: startLine,
447 height: itemHeight,
422 height: itemHeight,
449 l.totalHeight += itemHeight
424 l.totalHeight += itemHeight
452−// PrependItem adds an item to the beginning of the list.
429+// PrependItem adds an item to the beginning of the list. Returns true if
453−func (l *List) PrependItem(item Item) {
431+func (l *List) PrependItem(item Item) bool {
454 l.items = append([]Item{item}, l.items...)
432 l.items = append([]Item{item}, l.items...)
455−
433+ l.itemPositions = append([]itemPosition{{}}, l.itemPositions...)
456− // Rebuild index map (all indices shifted)
457− l.indexMap = make(map[string]int, len(l.items))
458− for i, itm := range l.items {
459− l.indexMap[itm.ID()] = i
462 if l.selectedIdx >= 0 {
434 if l.selectedIdx >= 0 {
463 l.selectedIdx++
435 l.selectedIdx++
@@ -466,7 +438,7 @@ func (l *List) PrependItem(item Item) {
466 // If buffer not built yet, mark dirty for full rebuild
438 // If buffer not built yet, mark dirty for full rebuild
467 if l.masterBuffer == nil || l.width <= 0 {
439 if l.masterBuffer == nil || l.width <= 0 {
468 l.dirty = true
440 l.dirty = true
469− return
441+ return true
472 // Process any pending dirty items before modifying buffer structure
444 // Process any pending dirty items before modifying buffer structure
@@ -492,42 +464,41 @@ func (l *List) PrependItem(item Item) {
492 item.Draw(l.masterBuffer, area)
464 item.Draw(l.masterBuffer, area)
494 // Update all positions (shift everything down)
466 // Update all positions (shift everything down)
495− for i := 1; i < len(l.items); i++ {
467+ for i := range l.itemPositions {
496− itm := l.items[i]
468+ pos := l.itemPositions[i]
497− if pos, ok := l.itemPositions[itm.ID()]; ok {
498− pos.startLine += itemHeight
469+ pos.startLine += itemHeight
499− l.itemPositions[itm.ID()] = pos
470+ l.itemPositions[i] = pos
503− // Add position for new item
473+ // Add position for new item at start
504− l.itemPositions[item.ID()] = itemPosition{
474+ l.itemPositions[0] = itemPosition{
505 startLine: 0,
475 startLine: 0,
506 height: itemHeight,
476 height: itemHeight,
508 l.totalHeight += itemHeight
479 l.totalHeight += itemHeight
511−// UpdateItem replaces an item with the same ID.
484+// UpdateItem replaces an item with the same index. Returns true if successful.
512−func (l *List) UpdateItem(id string, item Item) {
485+func (l *List) UpdateItem(idx int, item Item) bool {
513− idx, ok := l.indexMap[id]
486+ if idx < 0 || idx >= len(l.items) {
515− return
487+ return false
518 l.items[idx] = item
489 l.items[idx] = item
519− l.dirtyItems[id] = true
490+ l.dirtyItems[idx] = true
522−// DeleteItem removes an item by ID.
494+// DeleteItem removes an item by index. Returns true if successful.
523−func (l *List) DeleteItem(id string) {
495+func (l *List) DeleteItem(idx int) bool {
524− idx, ok := l.indexMap[id]
496+ if idx < 0 || idx >= len(l.items) {
526− return
497+ return false
529 // Get position before deleting
500 // Get position before deleting
530− pos, hasPos := l.itemPositions[id]
501+ pos := l.itemPositions[idx]
532 // Process any pending dirty items before modifying buffer structure
503 // Process any pending dirty items before modifying buffer structure
533 if len(l.dirtyItems) > 0 {
504 if len(l.dirtyItems) > 0 {
@@ -535,13 +506,7 @@ func (l *List) DeleteItem(id string) {
537 l.items = append(l.items[:idx], l.items[idx+1:]...)
508 l.items = append(l.items[:idx], l.items[idx+1:]...)
538− delete(l.indexMap, id)
509+ l.itemPositions = append(l.itemPositions[:idx], l.itemPositions[idx+1:]...)
539− delete(l.itemPositions, id)
541− // Rebuild index map for items after deleted one
542− for i := idx; i < len(l.items); i++ {
543− l.indexMap[l.items[i].ID()] = i
546 // Adjust selection
511 // Adjust selection
547 if l.selectedIdx == idx {
512 if l.selectedIdx == idx {
@@ -557,9 +522,9 @@ func (l *List) DeleteItem(id string) {
559 // If buffer not built yet, mark dirty for full rebuild
524 // If buffer not built yet, mark dirty for full rebuild
560− if l.masterBuffer == nil || !hasPos {
525+ if l.masterBuffer == nil {
561 l.dirty = true
526 l.dirty = true
562− return
527+ return true
565 // Efficient delete: remove lines from buffer
530 // Efficient delete: remove lines from buffer
@@ -575,6 +540,8 @@ func (l *List) DeleteItem(id string) {
575 // Position data corrupt, rebuild
540 // Position data corrupt, rebuild
576 l.dirty = true
541 l.dirty = true
580// Focus focuses the list and the selected item (if focusable).
547// Focus focuses the list and the selected item (if focusable).
@@ -595,12 +562,10 @@ func (l *List) Focused() bool {
597// SetSelected sets the selected item by ID.
564// SetSelected sets the selected item by ID.
598−func (l *List) SetSelected(id string) {
565+func (l *List) SetSelected(idx int) {
599− idx, ok := l.indexMap[id]
566+ if idx < 0 || idx >= len(l.items) {
604 if l.selectedIdx == idx {
569 if l.selectedIdx == idx {
@@ -613,33 +578,25 @@ func (l *List) SetSelected(id string) {
613 if prevIdx >= 0 && prevIdx < len(l.items) {
578 if prevIdx >= 0 && prevIdx < len(l.items) {
614 if f, ok := l.items[prevIdx].(Focusable); ok {
579 if f, ok := l.items[prevIdx].(Focusable); ok {
616− l.dirtyItems[l.items[prevIdx].ID()] = true
581+ l.dirtyItems[prevIdx] = true
620 if f, ok := l.items[idx].(Focusable); ok {
585 if f, ok := l.items[idx].(Focusable); ok {
621 f.Focus()
586 f.Focus()
622− l.dirtyItems[l.items[idx].ID()] = true
587+ l.dirtyItems[idx] = true
627−// SetSelectedIndex sets the selected item by index.
628−func (l *List) SetSelectedIndex(idx int) {
629− if idx < 0 || idx >= len(l.items) {
632− l.SetSelected(l.items[idx].ID())
635// SelectFirst selects the first item in the list.
592// SelectFirst selects the first item in the list.
636func (l *List) SelectFirst() {
593func (l *List) SelectFirst() {
637− l.SetSelectedIndex(0)
594+ l.SetSelected(0)
640// SelectLast selects the last item in the list.
597// SelectLast selects the last item in the list.
641func (l *List) SelectLast() {
598func (l *List) SelectLast() {
642− l.SetSelectedIndex(len(l.items) - 1)
599+ l.SetSelected(len(l.items) - 1)
645// SelectNextWrap selects the next item in the list (wraps to beginning).
602// SelectNextWrap selects the next item in the list (wraps to beginning).
@@ -679,7 +636,7 @@ func (l *List) selectNext(wrap bool) {
681 // Select and scroll to this item
638 // Select and scroll to this item
682− l.SetSelected(l.items[nextIdx].ID())
639+ l.SetSelected(nextIdx)
@@ -721,7 +678,7 @@ func (l *List) selectPrev(wrap bool) {
723 // Select and scroll to this item
680 // Select and scroll to this item
724− l.SetSelected(l.items[prevIdx].ID())
681+ l.SetSelected(prevIdx)
@@ -773,13 +730,9 @@ func (l *List) ScrollToBottom() {
775// ScrollToItem scrolls to make the item with the given ID visible.
732// ScrollToItem scrolls to make the item with the given ID visible.
776−func (l *List) ScrollToItem(id string) {
733+func (l *List) ScrollToItem(idx int) {
777 l.ensureBuilt()
734 l.ensureBuilt()
778− pos, ok := l.itemPositions[id]
735+ pos := l.itemPositions[idx]
783 itemStart := pos.startLine
736 itemStart := pos.startLine
784 itemEnd := pos.startLine + pos.height
737 itemEnd := pos.startLine + pos.height
785 viewStart := l.offset
738 viewStart := l.offset
@@ -805,7 +758,7 @@ func (l *List) ScrollToSelected() {
805 if l.selectedIdx < 0 || l.selectedIdx >= len(l.items) {
758 if l.selectedIdx < 0 || l.selectedIdx >= len(l.items) {
808− l.ScrollToItem(l.items[l.selectedIdx].ID())
761+ l.ScrollToItem(l.selectedIdx)
811// Offset returns the current scroll offset.
764// Offset returns the current scroll offset.
@@ -825,15 +778,12 @@ func (l *List) SelectFirstInView() {
825 viewportStart := l.offset
778 viewportStart := l.offset
826 viewportEnd := l.offset + l.height
779 viewportEnd := l.offset + l.height
828− for i, item := range l.items {
781+ for i := range l.items {
829− pos, ok := l.itemPositions[item.ID()]
782+ pos := l.itemPositions[i]
834 // Check if item is fully within viewport bounds
784 // Check if item is fully within viewport bounds
835 if pos.startLine >= viewportStart && (pos.startLine+pos.height) <= viewportEnd {
785 if pos.startLine >= viewportStart && (pos.startLine+pos.height) <= viewportEnd {
836− l.SetSelectedIndex(i)
786+ l.SetSelected(i)
@@ -847,15 +797,11 @@ func (l *List) SelectLastInView() {
847 viewportEnd := l.offset + l.height
797 viewportEnd := l.offset + l.height
849 for i := len(l.items) - 1; i >= 0; i-- {
799 for i := len(l.items) - 1; i >= 0; i-- {
851− pos, ok := l.itemPositions[item.ID()]
800+ pos := l.itemPositions[i]
856 // Check if item is fully within viewport bounds
802 // Check if item is fully within viewport bounds
857 if pos.startLine >= viewportStart && (pos.startLine+pos.height) <= viewportEnd {
803 if pos.startLine >= viewportStart && (pos.startLine+pos.height) <= viewportEnd {
858− l.SetSelectedIndex(i)
804+ l.SetSelected(i)
@@ -868,11 +814,7 @@ func (l *List) SelectedItemInView() bool {
870 // Get selected item ID and position
816 // Get selected item ID and position
871− item := l.items[l.selectedIdx]
872− pos, ok := l.itemPositions[item.ID()]
817+ pos := l.itemPositions[l.selectedIdx]
877 // Check if item is within viewport bounds
819 // Check if item is within viewport bounds
878 viewportStart := l.offset
820 viewportStart := l.offset
@@ -896,7 +838,7 @@ func (l *List) focusSelectedItem() {
896 item := l.items[l.selectedIdx]
838 item := l.items[l.selectedIdx]
897 if f, ok := item.(Focusable); ok {
839 if f, ok := item.(Focusable); ok {
898 f.Focus()
840 f.Focus()
899− l.dirtyItems[item.ID()] = true
841+ l.dirtyItems[l.selectedIdx] = true
@@ -909,7 +851,7 @@ func (l *List) blurSelectedItem() {
909 item := l.items[l.selectedIdx]
851 item := l.items[l.selectedIdx]
910 if f, ok := item.(Focusable); ok {
852 if f, ok := item.(Focusable); ok {
912− l.dirtyItems[item.ID()] = true
854+ l.dirtyItems[l.selectedIdx] = true
@@ -923,8 +865,8 @@ func (l *List) HandleMouseDown(x, y int) bool {
923 bufferY := y + l.offset
865 bufferY := y + l.offset
925 // Find which item was clicked
867 // Find which item was clicked
926− itemID, itemY := l.findItemAtPosition(bufferY)
868+ itemIdx, itemY := l.findItemAtPosition(bufferY)
927− if itemID == "" {
869+ if itemIdx < 0 {
928 return false
870 return false
@@ -933,17 +875,15 @@ func (l *List) HandleMouseDown(x, y int) bool {
933 // Items can interpret this as character offset in their content
875 // Items can interpret this as character offset in their content
935 l.mouseDown = true
877 l.mouseDown = true
936− l.mouseDownItem = itemID
878+ l.mouseDownItem = itemIdx
937 l.mouseDownX = x
879 l.mouseDownX = x
938 l.mouseDownY = itemY
880 l.mouseDownY = itemY
939− l.mouseDragItem = itemID
881+ l.mouseDragItem = itemIdx
940 l.mouseDragX = x
882 l.mouseDragX = x
941 l.mouseDragY = itemY
883 l.mouseDragY = itemY
943 // Select the clicked item
885 // Select the clicked item
944− if idx, ok := l.indexMap[itemID]; ok {
886+ l.SetSelected(itemIdx)
945− l.SetSelectedIndex(idx)
948 return true
888 return true
@@ -962,12 +902,12 @@ func (l *List) HandleMouseDrag(x, y int) bool {
962 bufferY := y + l.offset
902 bufferY := y + l.offset
964 // Find which item we're dragging over
904 // Find which item we're dragging over
965− itemID, itemY := l.findItemAtPosition(bufferY)
905+ itemIdx, itemY := l.findItemAtPosition(bufferY)
966− if itemID == "" {
906+ if itemIdx < 0 {
967 return false
907 return false
970− l.mouseDragItem = itemID
910+ l.mouseDragItem = itemIdx
971 l.mouseDragX = x
911 l.mouseDragX = x
972 l.mouseDragY = itemY
912 l.mouseDragY = itemY
@@ -994,47 +934,46 @@ func (l *List) HandleMouseUp(x, y int) bool {
995// ClearHighlight clears any active text highlighting.
935// ClearHighlight clears any active text highlighting.
996func (l *List) ClearHighlight() {
936func (l *List) ClearHighlight() {
997− for _, item := range l.items {
937+ for i, item := range l.items {
998 if h, ok := item.(Highlightable); ok {
938 if h, ok := item.(Highlightable); ok {
999 h.SetHighlight(-1, -1, -1, -1)
939 h.SetHighlight(-1, -1, -1, -1)
1000− l.dirtyItems[item.ID()] = true
940+ l.dirtyItems[i] = true
943+ l.mouseDownItem = -1
944+ l.mouseDragItem = -1
1005// findItemAtPosition finds the item at the given master buffer y coordinate.
947// findItemAtPosition finds the item at the given master buffer y coordinate.
1006−// Returns the item ID and the y offset within that item.
948+// Returns the item index and the y offset within that item. It returns -1, -1
949+// if no item is found.
1007−func (l *List) findItemAtPosition(bufferY int) (itemID string, itemY int) {
950+func (l *List) findItemAtPosition(bufferY int) (itemIdx int, itemY int) {
1008 if bufferY < 0 || bufferY >= l.totalHeight {
951 if bufferY < 0 || bufferY >= l.totalHeight {
1009− return "", 0
952+ return -1, -1
1012 // Linear search through items to find which one contains this y
955 // Linear search through items to find which one contains this y
1013 // This could be optimized with binary search if needed
956 // This could be optimized with binary search if needed
1014− for _, item := range l.items {
957+ for i := range l.items {
1015− pos, ok := l.itemPositions[item.ID()]
958+ pos := l.itemPositions[i]
1020 if bufferY >= pos.startLine && bufferY < pos.startLine+pos.height {
959 if bufferY >= pos.startLine && bufferY < pos.startLine+pos.height {
1021− return item.ID(), bufferY - pos.startLine
960+ return i, bufferY - pos.startLine
1025− return "", 0
964+ return -1, -1
1028// updateHighlight updates the highlight range for highlightable items.
967// updateHighlight updates the highlight range for highlightable items.
1029// Supports highlighting across multiple items and respects drag direction.
968// Supports highlighting across multiple items and respects drag direction.
1030func (l *List) updateHighlight() {
969func (l *List) updateHighlight() {
1031− if l.mouseDownItem == "" {
970+ if l.mouseDownItem < 0 {
1035 // Get start and end item indices
974 // Get start and end item indices
1036− downItemIdx := l.indexMap[l.mouseDownItem]
975+ downItemIdx := l.mouseDownItem
1037− dragItemIdx := l.indexMap[l.mouseDragItem]
976+ dragItemIdx := l.mouseDragItem
1039 // Determine selection direction
978 // Determine selection direction
1040 draggingDown := dragItemIdx > downItemIdx ||
979 draggingDown := dragItemIdx > downItemIdx ||
@@ -1064,10 +1003,10 @@ func (l *List) updateHighlight() {
1066 // Clear all highlights first
1005 // Clear all highlights first
1067− for _, item := range l.items {
1006+ for i, item := range l.items {
1068 if h, ok := item.(Highlightable); ok {
1007 if h, ok := item.(Highlightable); ok {
1069 h.SetHighlight(-1, -1, -1, -1)
1008 h.SetHighlight(-1, -1, -1, -1)
1070− l.dirtyItems[item.ID()] = true
1009+ l.dirtyItems[i] = true
@@ -1083,18 +1022,18 @@ func (l *List) updateHighlight() {
1083 item.SetHighlight(startLine, startCol, endLine, endCol)
1022 item.SetHighlight(startLine, startCol, endLine, endCol)
1084 } else if idx == startItemIdx {
1023 } else if idx == startItemIdx {
1085 // First item - from start position to end of item
1024 // First item - from start position to end of item
1086− pos := l.itemPositions[l.items[idx].ID()]
1025+ pos := l.itemPositions[idx]
1087 item.SetHighlight(startLine, startCol, pos.height-1, 9999) // 9999 = end of line
1026 item.SetHighlight(startLine, startCol, pos.height-1, 9999) // 9999 = end of line
1088 } else if idx == endItemIdx {
1027 } else if idx == endItemIdx {
1089 // Last item - from start of item to end position
1028 // Last item - from start of item to end position
1090 item.SetHighlight(0, 0, endLine, endCol)
1029 item.SetHighlight(0, 0, endLine, endCol)
1091 } else {
1030 } else {
1092 // Middle item - fully highlighted
1031 // Middle item - fully highlighted
1093− pos := l.itemPositions[l.items[idx].ID()]
1032+ pos := l.itemPositions[idx]
1094 item.SetHighlight(0, 0, pos.height-1, 9999)
1033 item.SetHighlight(0, 0, pos.height-1, 9999)
1097− l.dirtyItems[l.items[idx].ID()] = true
1036+ l.dirtyItems[idx] = true
@@ -1110,7 +1049,7 @@ func (l *List) GetHighlightedText() string {
1110 var result strings.Builder
1049 var result strings.Builder
1112 // Iterate through items to find highlighted ones
1051 // Iterate through items to find highlighted ones
1113− for _, item := range l.items {
1052+ for i, item := range l.items {
1114 h, ok := item.(Highlightable)
1053 h, ok := item.(Highlightable)
1115 if !ok {
1054 if !ok {
1116 continue
1055 continue
@@ -1121,10 +1060,7 @@ func (l *List) GetHighlightedText() string {
1121 continue
1060 continue
1124− pos, ok := l.itemPositions[item.ID()]
1063+ pos := l.itemPositions[i]
1129 // Extract text from highlighted region in master buffer
1065 // Extract text from highlighted region in master buffer
1130 for y := startLine; y <= endLine && y < pos.height; y++ {
1066 for y := startLine; y <= endLine && y < pos.height; y++ {