feat(plex): on-screen ±10s skip buttons flanking play/pause (E6 S3)
Add discoverable rewind-10 / forward-10 controls to the Plex player control bar, flanking play/pause (D5-A). New Skip10 glyph: an outline circular arrow with a centred '10', deliberately distinct from the episode-level SkipBack/SkipForward and from Restart's plain RotateCcw. Reuses seekTo; mirrors the keyboard ←/→ (seekStep=10). i18n HU/EN aria-labels.
This commit is contained in:
@@ -38,7 +38,7 @@ import {
|
|||||||
subShadowCss,
|
subShadowCss,
|
||||||
type PlexPlayerPrefs,
|
type PlexPlayerPrefs,
|
||||||
} from "../lib/plexPlayerPrefs";
|
} from "../lib/plexPlayerPrefs";
|
||||||
import { Ctrl, PanelHead } from "./plexPlayerControls";
|
import { Ctrl, PanelHead, Skip10 } from "./plexPlayerControls";
|
||||||
import { PlexPlayerSettings } from "./PlexPlayerSettings";
|
import { PlexPlayerSettings } from "./PlexPlayerSettings";
|
||||||
import {
|
import {
|
||||||
useAutoSkip,
|
useAutoSkip,
|
||||||
@@ -936,9 +936,19 @@ export default function PlexPlayer({ itemId, onClose, queue }: Props) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="flex items-center gap-3 text-white">
|
<div className="flex items-center gap-3 text-white">
|
||||||
<Ctrl label={t("plex.player.playPause")} onClick={togglePlay} align="start">
|
<Ctrl
|
||||||
|
label={t("plex.player.rewind10")}
|
||||||
|
onClick={() => seekTo(absRef.current - 10)}
|
||||||
|
align="start"
|
||||||
|
>
|
||||||
|
<Skip10 dir="back" />
|
||||||
|
</Ctrl>
|
||||||
|
<Ctrl label={t("plex.player.playPause")} onClick={togglePlay}>
|
||||||
{playing ? <Pause className="w-6 h-6" /> : <Play className="w-6 h-6" />}
|
{playing ? <Pause className="w-6 h-6" /> : <Play className="w-6 h-6" />}
|
||||||
</Ctrl>
|
</Ctrl>
|
||||||
|
<Ctrl label={t("plex.player.forward10")} onClick={() => seekTo(absRef.current + 10)}>
|
||||||
|
<Skip10 dir="forward" />
|
||||||
|
</Ctrl>
|
||||||
<Ctrl label={t("plex.player.restart")} onClick={() => seekTo(0)}>
|
<Ctrl label={t("plex.player.restart")} onClick={() => seekTo(0)}>
|
||||||
<RotateCcw className="w-5 h-5" />
|
<RotateCcw className="w-5 h-5" />
|
||||||
</Ctrl>
|
</Ctrl>
|
||||||
|
|||||||
@@ -1,6 +1,49 @@
|
|||||||
import { type ReactNode } from "react";
|
import { type ReactNode } from "react";
|
||||||
import { RotateCcw } from "lucide-react";
|
import { RotateCcw } from "lucide-react";
|
||||||
|
|
||||||
|
// ±10s skip glyph: an outline circular arrow with a "10" in the centre — the YouTube/Plex-native
|
||||||
|
// muscle-memory shape. Deliberately distinct from the episode-level SkipBack/SkipForward (double
|
||||||
|
// triangles) and from Restart's plain RotateCcw. The arrowhead is an L-corner at the arc's open end,
|
||||||
|
// the same construction lucide uses for RotateCcw/RotateCw, so it sits consistently in the bar.
|
||||||
|
export function Skip10({ dir }: { dir: "back" | "forward" }) {
|
||||||
|
const back = dir === "back";
|
||||||
|
return (
|
||||||
|
<svg
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeWidth={2}
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeLinejoin="round"
|
||||||
|
className="w-6 h-6"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
{back ? (
|
||||||
|
<>
|
||||||
|
<path d="M4 8.5 A8 8 0 1 1 4 12" />
|
||||||
|
<path d="M4 8.5 L4 4 M4 8.5 L8.5 8.5" />
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<path d="M20 8.5 A8 8 0 1 0 20 12" />
|
||||||
|
<path d="M20 8.5 L20 4 M20 8.5 L15.5 8.5" />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
<text
|
||||||
|
x="12"
|
||||||
|
y="14.5"
|
||||||
|
textAnchor="middle"
|
||||||
|
fontSize="8"
|
||||||
|
fontWeight={700}
|
||||||
|
fill="currentColor"
|
||||||
|
stroke="none"
|
||||||
|
>
|
||||||
|
10
|
||||||
|
</text>
|
||||||
|
</svg>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// --- settings-panel building blocks ------------------------------------------------------------
|
// --- settings-panel building blocks ------------------------------------------------------------
|
||||||
export function PanelHead({ children }: { children: ReactNode }) {
|
export function PanelHead({ children }: { children: ReactNode }) {
|
||||||
return (
|
return (
|
||||||
|
|||||||
@@ -160,6 +160,8 @@
|
|||||||
"skipCredits": "Skip credits",
|
"skipCredits": "Skip credits",
|
||||||
"playPause": "Play / pause (space)",
|
"playPause": "Play / pause (space)",
|
||||||
"restart": "Restart from beginning",
|
"restart": "Restart from beginning",
|
||||||
|
"rewind10": "Back 10 seconds",
|
||||||
|
"forward10": "Forward 10 seconds",
|
||||||
"prev": "Previous episode",
|
"prev": "Previous episode",
|
||||||
"next": "Next episode",
|
"next": "Next episode",
|
||||||
"mute": "Mute (m)",
|
"mute": "Mute (m)",
|
||||||
|
|||||||
@@ -160,6 +160,8 @@
|
|||||||
"skipCredits": "Stáblista átugrása",
|
"skipCredits": "Stáblista átugrása",
|
||||||
"playPause": "Lejátszás / szünet (szóköz)",
|
"playPause": "Lejátszás / szünet (szóköz)",
|
||||||
"restart": "Újra az elejéről",
|
"restart": "Újra az elejéről",
|
||||||
|
"rewind10": "10 másodperc vissza",
|
||||||
|
"forward10": "10 másodperc előre",
|
||||||
"prev": "Előző rész",
|
"prev": "Előző rész",
|
||||||
"next": "Következő rész",
|
"next": "Következő rész",
|
||||||
"mute": "Némítás (m)",
|
"mute": "Némítás (m)",
|
||||||
|
|||||||
Reference in New Issue
Block a user