From acac2431b592600be2c37878851cf8e16b6ceba3 Mon Sep 17 00:00:00 2001 From: Adam Soltys Date: Sat, 26 Sep 2026 07:42:28 -0700 Subject: Feed: the new-posts pill glides up to the top instead of cutting there. An instant scrollTo read as a page refresh. A long way down, most of the distance is closed in one step (so the glide is short and passes rows the window has mounted), then the last two screens roll by; reduced-motion keeps the cut. Co-Authored-By: Claude Fable 5.1 --- src/features/messages.js | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/features') diff --git a/src/features/messages.js b/src/features/messages.js index 1910546..a44a9b7 100644 --- a/src/features/messages.js +++ b/src/features/messages.js @@ -4335,8 +4335,21 @@ export function messagesFeature(ctx) { if (!c) return; admitFeed(c.deferred || [], c, true); c.unseen = 0; - try { window.scrollTo({ top: 0 }); } catch {} render(); + glideToTop(); + } + // Up to the new posts as a glide, not a cut: an instant scrollTo read as a + // page refresh. A long way down, most of the distance is closed in one + // step first — so the glide is short, and the rows it passes are the + // ones the window has mounted — and the last couple of screens roll by. + function glideToTop() { + try { + const H = window.innerHeight || 800; + const reduce = window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches; + if (reduce) { window.scrollTo({ top: 0 }); return; } + if (window.scrollY > H * 2) window.scrollTo({ top: H * 2 }); + requestAnimationFrame(() => { try { window.scrollTo({ top: 0, behavior: 'smooth' }); } catch { window.scrollTo(0, 0); } }); + } catch { try { window.scrollTo(0, 0); } catch {} } } // One pass over the plan: each relay is asked only for the authors it // actually carries. The slowest relay doesn't hold up the rest — every -- cgit v1.2.3