Skip to content

Fix drag-and-drop memory leak and reordering with variable item heights#1

Open
AustinMKerr wants to merge 3 commits into
mainfrom
claude/complex-tree-memory-drag-odnn8b
Open

Fix drag-and-drop memory leak and reordering with variable item heights#1
AustinMKerr wants to merge 3 commits into
mainfrom
claude/complex-tree-memory-drag-odnn8b

Conversation

@AustinMKerr

Copy link
Copy Markdown
Owner

Summary

Fixes two issues in the dynamic item-height drag-and-drop path that were introduced alongside the itemsHeightArray refactor.

1. Memory leak during drag

computeItemHeightArray() called console.log({ itemHeights }) (and console.log("Document not found")) every time it ran — and it runs on every dragover event, which fires continuously while dragging. Objects passed to console.log are retained by the browser's console heap and can never be garbage-collected, so memory grew unbounded for the duration of each drag (and it spammed the console / added work on a hot path).

Fix: removed the hot-path logging. The function now just measures and returns the heights.

2. Reordering doesn't track when nodes are different sizes

The cumulative-height loop in getHoveringPosition only assigned linearIndex / hoveringPosition when the pointer landed inside an item's vertical range. When the pointer was below the last item (e.g. dragging into the empty space at the bottom of the tree), the loop never matched, so both values stayed 0 and the drop indicator snapped to the top of the tree. linearIndex was also derived directly from the (possibly out-of-range) loop index rather than being clamped against the real linear-item list.

Fix: rewrote the calculation to

  • compute a fractional "linear index" from each item's measured height (integer part = item index, fractional part = how far into that item the pointer is), so tracking is correct regardless of differing node sizes;
  • treat "pointer below the last item" as past-the-end, so the existing bottom-drop detection resolves to a bottom offset;
  • clamp linearIndex to the actual linearItems range, matching the original robust behavior.

Also removed dead commented-out code left over from the WIP.

Notes

  • DragBetweenLine already positions the indicator by summing per-item heights, so it stays consistent with the corrected linearIndex.
  • No API changes; itemHeight / itemsHeightArray context fields are unchanged.

Testing

Type/logic reviewed locally. A full tsc/jest run wasn't possible in this environment because node_modules isn't installed (dependency resolution errors are unrelated to these changes).

🤖 Generated with Claude Code


Generated by Claude Code

claude added 3 commits June 24, 2026 00:43
…m heights

Two issues in the dynamic-height drag-and-drop path:

- Memory leak: computeItemHeightArray() logged its result object (and a
  "Document not found" string) on every dragover event, which fires
  continuously during a drag. Objects passed to console.log are retained
  by the browser console heap and can never be GC'd, so memory grew
  unbounded for the duration of each drag. Removed the hot-path logging.

- Reordering didn't track when items have different sizes: the cumulative-
  height loop only assigned linearIndex/hoveringPosition when the pointer
  fell within an item, so dragging below the last item (into the tree's
  empty space) left both at 0 and snapped the drop indicator to the top.
  Rewrote it to compute a fractional linear index from each item's measured
  height, clamp linearIndex against the real linear-item list, and treat
  "below the last item" as past-the-end so the bottom-drop offset resolves
  correctly. Removed dead commented-out code left from the WIP.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GtzjXm3E3o416rYjqWzEqv
- Use `i += 1` instead of `i++` in the hovering-position loop (airbnb
  no-plusplus) and hoist the loop condition into a named variable to keep
  lines within the 80-col prettier limit.
- Drop the now-unused `itemHeight` from DragBetweenLine's destructure
  (it was left over when the line offset switched to itemsHeightArray) and
  extract the summed-height offset into a `lineOffset` variable.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GtzjXm3E3o416rYjqWzEqv
…eights

The dynamic-height refactor switched the drag math to computeItemHeightArray,
but the test harness only mocked computeItemHeight. Under the auto-mocked
layoutUtils module, computeItemHeightArray returned undefined, so reading its
.length threw and every drag-and-drop test failed.

Mock it to return uniform 10px heights (matching dragOver's clientY math of
itemIndex * 10 + offset). With uniform heights the cumulative-height
calculation is mathematically identical to the previous clientY / itemHeight
formula, so behavior is unchanged.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GtzjXm3E3o416rYjqWzEqv
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants