fix(web): the mirror replaces only what it must, and sideways is not "load more"
The freeze added in 41018d9 was not enough, because it was a hold and not a fix. Two things were still true. First, what chromium actually does. replaceChildren does not merely lose a selection anchored inside it: it COLLAPSES it to (container, 0) -- rangeCount stays 1 and the anchor becomes the mirror itself at offset 0. So the next extend, whether that is a drag continuing or a phone's handle being moved, runs from the top of the output and the reader watches the FIRST LINE highlight itself. That is the report, and WebSelectionCollapseTests measures it. Second, the rebuild had a third path the guard never saw. `scroll` says nothing about which axis moved, and scrollTop === 0 is true for the whole of a sideways drag -- permanently true when the output is shorter than the box -- so `if (scrollTop === 0) loadMore()` fired on horizontal scrolling. Measured: one wheel right took the read from 200 lines to 600 and the next to 1000, each answer a wholesale different content, and loadMore reaches refreshPane directly so mirrorTick's guard was never consulted. Select on a long line, scroll right to read the rest of it, and the mirror was rebuilt under your hands. - mirrorPatch replaces only what it must. Identical content touches no DOM at all -- most ticks, since an idle pane polled every 3s returns the same bytes and this was rebuilt 20 times a minute -- and content that only grew at the tail is appended to the text node already on screen, so ranges above it keep both their offsets and the characters those offsets covered. Anything else keeps the matching prefix of ansiFragment's runs (nodeName plus the inline style string) and replaces from the first that differs. This is what closes the window the freeze cannot: a touch drag dismisses the old selection before it makes the new one, and a tick landing in between is the one that moves the anchor. - It returns whether anything changed, because the scroll fix-up below it pins an unscrolled mirror to the bottom -- running that on a tick that changed nothing is how a reader who nudged the view got pulled back every 3s. - Only a vertical arrival at the top asks for more lines, and loadMore joins the other three writes behind selectionInside: its answer puts hundreds of lines in FRONT of what is on screen, which is the one update that cannot be applied non-destructively. tests/test_web_selection.py is 16 tests over three classes. Neutering mirrorPatch fails 4, restoring the old scroll handler fails 3, neutering selectionInside fails 4.
N
Nick007 committed
5f05b9ef73148019999509040b43af9dd2ade86b
Parent: 1a8286f