fix: review round 1 — row press after a rename, related reset, dead items on removal, honest Prev/Next

- A press that begins while a field in the row is edited only ends the editing (rowOpen latches it
  at pointerdown); the card layout's row press is now tested.
- A related video reset in the player forgets the session's stored position.
- Removing the playing video passes over known-dead items and steps the way it went.
- Prev/Next off only because of dead items stay focusable and say why (HU/EN).
- The docked bar's scroller leaves room for focus rings.
- A focus-opened About card is not closed by its own scroll into view.
- EN "Clear watch progress" for the video reset, distinct from a page's "Reset view".
- The signed-out title read goes to the page yt-dlp read; the retitle writes are conditional.
- Tests: the cap of three errors, the bar's own Fullscreen, the group glyph, the backfill's reads.
This commit is contained in:
2026-11-01 05:36:59 +01:00
parent 245c0a2019
commit c910cf2f20
25 changed files with 411 additions and 63 deletions
@@ -5,6 +5,7 @@ import type { VideoDetail } from "../../lib/api";
import type { AutoMode, LoopMode } from "../../lib/playerModes";
import { IconButton } from "../../components/ui/IconButton";
import { StateMessage } from "../../components/ui/QueryState";
import { unavailable } from "../../components/ui/unavailable";
import { ClampedText } from "../../components/ui/ClampedText";
import { renderDescription } from "./descriptionLinks";
@@ -28,6 +29,8 @@ export function PlayerTransportControls({
queueLength,
canPrev,
canNext,
prevReason,
nextReason,
autoMode,
loopMode,
onPrev,
@@ -41,6 +44,11 @@ export function PlayerTransportControls({
/** Whether there is a playable item that way (one YouTube refused is passed over). */
canPrev: boolean;
canNext: boolean;
/** Why Previous / Next are off when items are left that way (only ones YouTube refused): the
* button stays focusable and its tooltip says so. Unset at the queue's ends, which explain
* themselves. */
prevReason?: string;
nextReason?: string;
autoMode: AutoMode;
loopMode: LoopMode;
onPrev: () => void;
@@ -62,6 +70,7 @@ export function PlayerTransportControls({
title: string;
onClick: () => void;
disabled?: boolean;
reason?: string;
lit?: boolean;
};
const step: Control[] = [
@@ -72,6 +81,7 @@ export function PlayerTransportControls({
title: `${t("player.previous")} · Shift+←`,
onClick: onPrev,
disabled: !canPrev,
reason: canPrev ? undefined : prevReason,
},
{
key: "next",
@@ -80,6 +90,7 @@ export function PlayerTransportControls({
title: `${t("player.next")} · Shift+→`,
onClick: onNext,
disabled: !canNext,
reason: canNext ? undefined : nextReason,
},
];
// Persistent playback settings — cycle on click, saved to your account.
@@ -107,10 +118,10 @@ export function PlayerTransportControls({
key={c.key}
variant={docked ? "overlay" : undefined}
onClick={c.onClick}
disabled={c.disabled}
{...unavailable(c.disabled, c.reason)}
lit={c.lit}
label={c.name}
title={c.title}
title={c.reason ? `${c.title} · ${c.reason}` : c.title}
>
{c.glyph}
</IconButton>