SIGN IN SIGN UP

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