diff options
| author | Adam Soltys <[email protected]> | 2026-10-16 22:13:16 -0700 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-16 22:13:16 -0700 |
| commit | 7d09e7f2b01f72db518f7cf906d887c22a49387e (patch) | |
| tree | 2ea6d565bf6518ca6c471b43a339a710d3f90024 | |
| parent | 550bd5cb8be1a879083d987edca7b4e676ac3557 (diff) | |
| download | coinosv3-7d09e7f2b01f72db518f7cf906d887c22a49387e.tar.gz coinosv3-7d09e7f2b01f72db518f7cf906d887c22a49387e.zip | |
The wheel coasts wherever the pointer is while a feed or a thread is up.
The coast only took a notch whose target sat inside the feed or the thread, so a pointer resting on the rail, the header or the wallet column beside the feed on a wide screen got the browser's plain scroll (Adam, 2026-10-16: "when my cursor is hovered over the right side bar or any part of the page that's not the feed, wheeling scrolls without coasting. can we make it coast no matter where my pointer is?"). Now the question is whether a feed or a thread is the page on screen; the pointer's spot only matters when it is over something that takes the wheel itself — a text box, the chips strip, the lightbox, the pager, a box that scrolls on its own — and on a page with no feed on it (the wallet) the wheel stays the browser's.
feed-wheel-reverse-test: the fixture's page carries a feed unless told otherwise; a notch with the pointer over the wallet column, or over nothing in particular, coasts; one on a page without a feed does not.
Co-Authored-By: Claude Fable 5.1 <[email protected]>
Claude-Session: https://claude.ai/code/session_01J1Kon8bxJoHRpJdSzshCkp
| -rw-r--r-- | src/app.js | 12 | ||||
| -rw-r--r-- | tools/feed-wheel-reverse-test.js | 19 |
2 files changed, 22 insertions, 9 deletions
@@ -6617,12 +6617,20 @@ if (INTENT_URI) { // selection, and a drag that moved isn't a click on what it started on. (() => { if (typeof window === 'undefined' || matchMedia('(prefers-reduced-motion: reduce)').matches) return; - const SCOPE = '.feed-page, .notes-feed, .thread-page'; // a thread scrolls the same as a feed (Adam, 2026-10-16) + // The wheel coasts whenever a feed or a thread is the page being scrolled + // — wherever the pointer happens to be: over the posts, the header, the + // rail, the wallet column beside the feed on a wide screen (Adam, + // 2026-10-16: "when my cursor is hovered over the right side bar or any + // part of the page that's not the feed, wheeling scrolls without + // coasting. can we make it coast no matter where my pointer is?") — + // except over something that takes the wheel itself: a text box, the + // chips strip, the lightbox, the pager, a box that scrolls on its own. + const SCOPE = '.feed-page, .notes-feed, .thread-page'; // the pages that coast (a thread scrolls the same as a feed) const TAU = 325; // ms: how quickly a coast dies down (the phone's feel) const VMAX = 16; // px/ms const inScope = (t) => { const el = t && t.closest ? t : t && t.parentElement; - if (!el || !el.closest(SCOPE)) return false; + if (!el || !document.querySelector(SCOPE)) return false; // (A video or an audio player is part of the feed: a notch over a clip // coasts like one over the words — the wheel does nothing to a player // itself, so there is nothing to leave it. Adam, 2026-10-16: "over a diff --git a/tools/feed-wheel-reverse-test.js b/tools/feed-wheel-reverse-test.js index 352bc0e..80e2fde 100644 --- a/tools/feed-wheel-reverse-test.js +++ b/tools/feed-wheel-reverse-test.js @@ -5,16 +5,18 @@ const source = await Bun.file('src/app.js').text(); const start = source.indexOf('(() => {', source.indexOf('// The feed scrolls like a phone')); const end = source.indexOf("\nwindow.addEventListener('load'", start); assert(start > 0 && end > start); -function fixture(inside = '') { +function fixture(inside = '', { feedOnPage = true } = {}) { let now = 100, id = 0; const handlers = new Map(), frames = new Map(), steps = []; const window = { scrollY: 5000, addEventListener: (name, fn) => handlers.set(name, fn), scrollBy: ({ top }) => { steps.push(top); window.scrollY += top; }, }; - const document = { body: {}, addEventListener() {}, removeEventListener() {} }; - // the target sits in a feed page (or, for a thread case, a thread page), inside the given element - const target = { closest: (selector) => selector.split(/,\s*/).some((p) => p === inside || (p === '.feed-page' && inside !== '.thread-page')) ? target : null, + // the page shows a feed (or, for a thread case, a thread) unless told otherwise + const document = { body: {}, addEventListener() {}, removeEventListener() {}, + querySelector: (selector) => (feedOnPage && selector.split(/,\s*/).some((p) => p === (inside === '.thread-page' ? '.thread-page' : '.feed-page')) ? {} : null) }; + // the target sits inside the given element (over the posts unless it names something else) + const target = { closest: (selector) => selector.split(/,\s*/).some((p) => p === inside) ? target : null, scrollHeight: 100, clientHeight: 100, parentElement: document.body }; runInNewContext(source.slice(start, end), { window, document, matchMedia: () => ({ matches: false }), performance: { now: () => now }, @@ -76,16 +78,19 @@ for (const dir of [1, -1]) { } // A notch over a video (or an audio player) in the feed coasts like any // other — the wheel does nothing to a player itself — and so does one on a -// thread page. -for (const sel of ['video', 'audio', '.thread-page']) { +// thread page, and one with the pointer anywhere else on the page while a +// feed is up (the rail, the wallet column, the header). +for (const sel of ['video', 'audio', '.thread-page', '.wallet-column', '']) { const f = fixture(sel); const y = f.y; f.wheel(1); settle(f); assert(f.y - y > 280 && f.y - y < 330, 'a notch over ' + sel + ' coasts a full push: ' + (f.y - y)); } -// ...while a notch in a text box, or in the media pager, is left alone. +// ...while a notch in a text box, or in the media pager, is left alone — and +// so is every notch on a page with no feed on it (the wallet). for (const sel of ['textarea', '.media-pager']) { const f = fixture(sel); const y = f.y; f.wheel(1); settle(f); assert.equal(f.y, y, 'a notch in ' + sel + ' is not ours'); } +{ const f = fixture('', { feedOnPage: false }); const y = f.y; f.wheel(1); settle(f); assert.equal(f.y, y, 'no feed on the page: the wheel is the browser\'s'); } console.log('✓ a turn back against a flight brakes, the next notch is the gentle start, then the wind-up; against a stroll or a dead coast the gentle start at once; stale frames; videos and threads coast'); |
