From 033343793f1abb9038084c287b9b15f7fb33e62d Mon Sep 17 00:00:00 2001 From: Mitchell-Johnson Date: Fri, 23 Oct 2026 23:48:37 -0700 Subject: [PATCH] =?UTF-8?q?files:=20the=20player's=20=E2=88=925=20s=20and?= =?UTF-8?q?=20+5=20s=20buttons,=20as=20Ham=20Share's?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 --- web/scripts/e2e-player.mjs | 13 +++++++++++++ web/src/Player.tsx | 14 +++++++++++++- 2 files changed, 26 insertions(+), 1 deletion(-) diff --git a/web/scripts/e2e-player.mjs b/web/scripts/e2e-player.mjs index 5c5ef5c..2e455e6 100644 --- a/web/scripts/e2e-player.mjs +++ b/web/scripts/e2e-player.mjs @@ -67,6 +67,19 @@ if (await page.locator('canvas.vp-sphere').count()) { } await page.screenshot({ path: `${SHOTS}/h1-auto.png` }) +// The 5-second buttons: back and on, the ball with them. +{ + await controls() + const t0 = (await vstate()).t + await page.getByRole('button', { name: 'On 5 seconds' }).click() + const t1 = (await vstate()).t + await page.getByRole('button', { name: 'Back 5 seconds' }).click() + await page.getByRole('button', { name: 'Back 5 seconds' }).click() + const t2 = (await vstate()).t + const ball = await page.evaluate(() => Number(document.querySelector('.vp input[type=range]').value)) + check('+5 and −5 buttons', t1 - t0 > 4 && t1 - t0 < 6.5 && t1 - t2 > 9 && t1 - t2 < 11 && Math.abs(ball - t2) < 1, `${t0.toFixed(1)} → ${t1.toFixed(1)} → ${t2.toFixed(1)}, ball ${ball.toFixed(1)}`) +} + const mid = Math.floor(s.d / 2) mark('seek') await page.evaluate((m) => { document.querySelector('.vp video').currentTime = m }, mid) diff --git a/web/src/Player.tsx b/web/src/Player.tsx index 4b22c25..a336700 100644 --- a/web/src/Player.tsx +++ b/web/src/Player.tsx @@ -68,6 +68,14 @@ const I = ({ d, size: sz = 20, fill }: { d: string; size?: number; fill?: boolea const PLAY = 'M7 4.5v15l12.5-7.5z', PAUSE = 'M8 5v14M16 5v14', CHECK = 'M5 12.5l4.5 4.5L19 7.5', TUNE = 'M4 7h10M18 7h2M4 17h4M12 17h8M16 5v4M10 15v4' const FULL = 'M4 9V4h5M20 9V4h-5M4 15v5h5M20 15v5h-5', SMALL = 'M9 4v5H4M15 4v5h5M9 20v-5H4M15 20v-5h5', PHONE = 'M8 3h8a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H8a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zM3 9a9 9 0 0 0 0 6M21 9a9 9 0 0 1 0 6' +// Five seconds back or on: a circling arrow with the 5 inside. +const Skip = ({ back }: { back?: boolean }) => ( + +) + export default function Player({ path, sudo }: { path: string; sudo: boolean; name: string }) { const [open, setOpen] = useState(null) const [err, setErr] = useState('') @@ -234,7 +242,9 @@ export default function Player({ path, sudo }: { path: string; sudo: boolean; na const seekBy = useCallback((d: number) => { const v = video.current if (!v) return - v.currentTime = Math.max(0, Math.min(info?.duration ?? v.duration, v.currentTime + d)) + const to = Math.max(0, Math.min(info?.duration ?? v.duration, v.currentTime + d)) + v.currentTime = to + setTime(to) // the ball goes there at once, made yet or not poke() }, [info, poke]) // Full screen: the browser's where it has one for an element, else (an iPhone) the window. @@ -336,6 +346,8 @@ export default function Player({ path, sudo }: { path: string; sudo: boolean; na
+ + {clock(shown)} / {clock(dur)} {vr && canMotion() && (