Follows the log's end, as a Mount on the log, whose parent is what
scrolls. It tells the reader leaving the end (a scroll up) from the log
growing: a scroll that moves up and leaves the end stops following, and
one that reaches within NEAR of the end starts again. While following,
any growth (a message, a streamed word, a picture loading) scrolls to
the end; otherwise the first message in view is held where it was, so a
page of older messages lands above it unseen. It turns the browser's own
scroll anchoring off on the scroller, which would correct the same growth
a second time, and which Safari lacks. Without a ResizeObserver (a DOM with no
layout) it only reports the end reached. A Scene test of a page that
draws the scroller resolves it: Scene.Mount.resolve(FollowEnd, Message.ReachedEnd()).
A message scroller: a conversation's messages in a ScrollArea that
keeps the newest in view while the reader is at the bottom, stops
following once they scroll up, and offers a button back to the latest
that counts what arrived meanwhile. Older messages loaded above leave
what the reader looks at where it was.
The scroll position is the browser's, and the Mount on the log reads it:
while the log's end is in view it scrolls to the end whenever the log
grows, a streamed answer included, and otherwise keeps the first message
in view at the same place. The Model only mirrors what the Mount saw, for
the button; the application tells it what arrived (Appended), since
only the application knows a new message from an older one loaded above.
The log is the APG's role="log", named by label: a reader hears what
is added to it, and a ChatMessage still streaming is aria-busy, so an
answer is read once whole. The scroller fills the height of what holds it.
Scrolls the scroller id to its end once the render has committed,
smoothly unless motion is stilled. A jump button that held the focus
hands it to the scrolling area, since the button goes once the end is in
view.