diff options
| author | Adam Soltys <[email protected]> | 2026-10-13 06:17:39 +0000 |
|---|---|---|
| committer | Adam Soltys <[email protected]> | 2026-10-13 06:17:39 +0000 |
| commit | 8f80948eb174a273f3b1dea963bb91745bcf323b (patch) | |
| tree | a8e704dd6896be17df41c7f844e21f18be5fdb92 /tools/feed-pager-ahead-test.js | |
| parent | b330cc707fab62365c29b57d414c46aa198febef (diff) | |
| download | coinosv3-8f80948eb174a273f3b1dea963bb91745bcf323b.tar.gz coinosv3-8f80948eb174a273f3b1dea963bb91745bcf323b.zip | |
Media pager: up and down go from post to post, left and right along a post's pictures. Adam: swipes and arrows up/down should go to the next post's media, left/right to the next within a post. The pager is now posts stacked top to bottom, each a row of its pictures or clips side by side — a quoted post's join the post that quotes it — with dots along the top when a post has more than one, saying how many and which. Up and down (swipe, arrows, j/k, Page) move a post at a time and land on a post where its row was left; left and right (swipe, arrows, h/l) move within the post and stop at its ends. A post's pictures all load as it comes near (those along its row too); a clip's head start goes to the next post's (where its row rests), the next along this one, and the post after next, at half; the look-ahead (the feed paging on, the media crawl) counts posts. Pictures pan sideways as well as up and down at 1×. Tests find the slide on screen by where it sits (the pager's children are posts now); feed-picture-pager walks right within a post, down past one with none, back up to where a post was left, left to its first, and a sideways swipe next to a swipe up; quote-pager walks the quoting post's row (its own picture, then the quote's).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01KPw21NEpWeTGu6swVASmBt
Diffstat (limited to 'tools/feed-pager-ahead-test.js')
| -rw-r--r-- | tools/feed-pager-ahead-test.js | 20 |
1 files changed, 10 insertions, 10 deletions
diff --git a/tools/feed-pager-ahead-test.js b/tools/feed-pager-ahead-test.js index 90cbe96..057ac3e 100644 --- a/tools/feed-pager-ahead-test.js +++ b/tools/feed-pager-ahead-test.js @@ -70,10 +70,10 @@ const waitFor = async (fn, arg, ms = 10000) => { for (let i = 0; i < ms / 100; i // the slide the pager rests on, by its picture const curPic = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); if (!sc) return null; - const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); + const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return s ? s.getAttribute('data-k').split('|')[1].split('/').pop() : 'between'; }); -const resting = async (name) => waitFor((n) => { const sc = document.querySelector('.mp-scroll'); if (!sc) return false; const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return !!s && s.getAttribute('data-k').endsWith('/' + n); }, name, 4000); +const resting = async (name) => waitFor((n) => { const sc = document.querySelector('.mp-scroll'); if (!sc) return false; const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return !!s && s.getAttribute('data-k').endsWith('/' + n); }, name, 4000); const openPic = async (word) => { await page.evaluate((w) => { const r = [...document.querySelectorAll('.notes-feed [data-key]')].find((n) => n.textContent.includes(w)); r.scrollIntoView({ block: 'center' }); }, word); await sleep(400); @@ -82,12 +82,12 @@ const openPic = async (word) => { await waitFor(() => !!document.querySelector('.media-pager'), null, 4000); return at.y0; }; -const tapPager = async () => { const at = await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4) || sc.firstChild; const r = s.querySelector('img.mp-media').getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; }); await page.mouse.click(at.x, at.y); }; +const tapPager = async () => { const at = await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }) || sc.firstChild; const r = s.querySelector('img.mp-media').getBoundingClientRect(); return { x: r.x + r.width / 2, y: r.y + r.height / 2 }; }); await page.mouse.click(at.x, at.y); }; const rowTop = (word) => page.evaluate((w) => { const r = [...document.querySelectorAll('.notes-feed [data-key]')].find((n) => n.textContent.includes(w)); return r ? Math.round(r.getBoundingClientRect().top) : null; }, word); -const slides = () => page.evaluate(() => document.querySelectorAll('.mp-scroll > .mp-slide').length); -const curIdx = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); if (!sc) return -1; return [...sc.querySelectorAll(':scope > .mp-slide')].findIndex((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); }); -const curName = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = sc && [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return s ? s.getAttribute('data-k').split('/').pop() : null; }); +const slides = () => page.evaluate(() => document.querySelectorAll('.mp-scroll .mp-slide').length); +const curIdx = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); if (!sc) return -1; return [...sc.querySelectorAll('.mp-slide')].findIndex((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); }); +const curName = () => page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const s = sc && [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return s ? s.getAttribute('data-k').split('/').pop() : null; }); try { await page.setViewport({ width: 390, height: 844 }); @@ -128,11 +128,11 @@ try { maxSlides = Math.max(maxSlides, n); if (i >= 0 && n - 1 - i <= 4) { // give the look-ahead a moment: it should get past three ahead again (or the feed's end) - await waitFor((k) => { const sc = document.querySelector('.mp-scroll'); const j = [...sc.querySelectorAll(':scope > .mp-slide')].findIndex((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return sc.querySelectorAll(':scope > .mp-slide').length - 1 - j > 4 || sc.querySelectorAll(':scope > .mp-slide').length >= k; }, TOTAL, 6000); + await waitFor((k) => { const sc = document.querySelector('.mp-scroll'); const j = [...sc.querySelectorAll('.mp-slide')].findIndex((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return sc.querySelectorAll('.mp-slide').length - 1 - j > 4 || sc.querySelectorAll('.mp-slide').length >= k; }, TOTAL, 6000); } const before = await curIdx(); await page.keyboard.press('ArrowDown'); - await waitFor((b) => { const sc = document.querySelector('.mp-scroll'); return [...sc.querySelectorAll(':scope > .mp-slide')].findIndex((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4) > b; }, before, 2000); + await waitFor((b) => { const sc = document.querySelector('.mp-scroll'); return [...sc.querySelectorAll('.mp-slide')].findIndex((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }) > b; }, before, 2000); } // past the last picture is the pager's end (its loading screen): back up onto the last if (await page.evaluate(() => { const sc = document.querySelector('.mp-scroll'); const t = sc.querySelector('.mp-tail'); return t && Math.abs(t.offsetTop - sc.scrollTop) < 4; })) { await page.keyboard.press('ArrowUp'); await sleep(800); } @@ -140,11 +140,11 @@ try { check('the pager grew as the end came near', grew, first + ' → ' + (await slides())); check('every picture in the feed became reachable', (await slides()) === TOTAL, (await slides()) + ' of ' + TOTAL); check('…past fifteen posts with none', (await curName()) === 'p' + (TOTAL - 1) + '.png', await curName()); - check('…in the feed\'s order', await page.evaluate((t) => [...document.querySelectorAll('.mp-scroll > .mp-slide')].every((s, i) => s.getAttribute('data-k').endsWith('/p' + i + '.png')), TOTAL)); + check('…in the feed\'s order', await page.evaluate((t) => [...document.querySelectorAll('.mp-scroll .mp-slide')].every((s, i) => s.getAttribute('data-k').endsWith('/p' + i + '.png')), TOTAL)); console.log('\n[closing lands the feed on the last picture\'s post]'); await page.keyboard.press('ArrowUp'); - await waitFor((n) => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll(':scope > .mp-slide')].find((x) => Math.abs(x.offsetTop - sc.scrollTop) < 4); return s && s.getAttribute('data-k').endsWith('/p' + n + '.png'); }, TOTAL - 2, 3000); + await waitFor((n) => { const sc = document.querySelector('.mp-scroll'); const s = [...sc.querySelectorAll('.mp-slide')].find((x) => { const r = x.getBoundingClientRect(); return Math.abs(r.top) < 4 && Math.abs(r.left) < 4; }); return s && s.getAttribute('data-k').endsWith('/p' + n + '.png'); }, TOTAL - 2, 3000); const want = 'picture number ' + (TOTAL - 2) + ' '; await page.keyboard.press('Escape'); check('the pager closes', await waitFor(() => !document.querySelector('.media-pager') && !!document.querySelector('.notes-feed'), null, 3000)); |
