summaryrefslogtreecommitdiffstats
path: root/packages/meshbay-hub/tests/test_layout_responsive.py
diff options
context:
space:
mode:
authorChristophe Besson <cbesson@gmail.com>2026-09-01 23:34:49 +0200
committerChristophe Besson <cbesson@gmail.com>2026-09-01 23:34:49 +0200
commit32855a95e11032302f8d24036f6f2dd44b829368 (patch)
treef0c00979232c1fd4c5bad0ddd726fbbde15eff1f /packages/meshbay-hub/tests/test_layout_responsive.py
parent799d87999c8324564dce5159191532e008dd93d2 (diff)
downloadmeshbay-32855a95e11032302f8d24036f6f2dd44b829368.tar.gz
fix(hub): let the reader scroll up in the chat again
The chat could not be read back: any wheel gesture was undone in the frame it happened in, and the "jump to latest" button never appeared. None of the pins in ChatPanel are at fault -- every one of them is guarded by "only if the reader is at the bottom". The reader never got to stop being at the bottom. fit() set the panel's height, read documentElement.scrollHeight back and subtracted the overflow, so the document alternately did and did not overflow the window. The page scrollbar appeared and vanished with it and visualViewport fired resize at every pass -- the event fit() is bound to. It therefore re-entered itself for the life of the panel: measured at 240 firings in two seconds on a page nobody was touching, against 2 for a bare document. Each pass ran fitAndPin, which re-pinned the list to the bottom before the scroll event that would have recorded the gesture was delivered a frame later, so atBottomRef never went false. - fit() learns the space below the panel once and remembers it on the element instead of re-deriving it by writing and measuring back. At the steady state it writes nothing, so it produces no resize. A real window resize or an orientation change forgets the learnt value and measures again (the page under the panel may have reflowed); visualViewport deliberately does not, since a phone fires it constantly. - The scroll-to-bottom is now scoped to *arrival*, which is all it was ever for: opening the group, or coming back to the Chat tab, including the thumbnails and link-preview cards that keep growing the list for a second afterwards. It ends when the reader takes hold of the scroll, and the ResizeObserver disconnects there. - That release is recorded from the gesture (wheel/touchmove/pointerdown/ keydown), not from the scroll event, which arrives too late to protect anything. Unchanged: landing on the newest message, following new messages while already at the bottom, the "load older" anchor and the unread marker. tests/harness/chat_scroll_probe.py mounts the real ChatPanel in a browser and reads a conversation back; test_chat_scroll_up.py asserts against it. With the fix reverted, five of its six tests fail and the sixth -- landing on the newest message -- still passes, which is the property that must not have been traded away. A structural test cannot see any of this, which is why it is measured. test_layout_responsive.py pinned the listener's name and follows the rename. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01W8oRqEHhnKUr1NfmTVdcyL
Diffstat (limited to 'packages/meshbay-hub/tests/test_layout_responsive.py')
-rw-r--r--packages/meshbay-hub/tests/test_layout_responsive.py15
1 files changed, 11 insertions, 4 deletions
diff --git a/packages/meshbay-hub/tests/test_layout_responsive.py b/packages/meshbay-hub/tests/test_layout_responsive.py
index d7a680d..4ee07d0 100644
--- a/packages/meshbay-hub/tests/test_layout_responsive.py
+++ b/packages/meshbay-hub/tests/test_layout_responsive.py
@@ -145,12 +145,19 @@ def test_the_measurement_survives_a_scrolled_page(app):
def test_the_panel_refits_when_the_viewport_changes(app):
- # `fit` is wrapped by `fitAndPin` (which also keeps the view pinned to
- # the bottom on a resize) — that is what the listeners bind to.
+ # `fit` is wrapped by `fitAndPin` (which also keeps the view pinned to the
+ # bottom on a resize), and on the window by `refit`, which additionally
+ # forgets the learnt space below the panel — a real viewport change can
+ # mean the page under it reflowed. `visualViewport` deliberately does not
+ # forget: a phone fires that event constantly, and re-deriving the leftover
+ # there is what made fit() oscillate and weld the reader to the bottom of
+ # the conversation (see test_chat_scroll_up.py).
for event in ("resize", "orientationchange"):
- assert f"addEventListener('{event}', fitAndPin)" in app
+ assert f"addEventListener('{event}', refit)" in app
assert "visualViewport?.addEventListener('resize', fitAndPin)" in app
- assert "removeEventListener('resize', fitAndPin)" in app, "the listener must be released"
+ assert "removeEventListener('resize', refit)" in app, "the listener must be released"
+ assert "el._chatFitBelow = 0" in app, (
+ "a window resize must forget the learnt leftover and measure it again")
def test_the_css_floor_does_not_fight_the_measurement(css, app):