From 359873cff19988b7f33cb9c7aab778d67b7e32bc Mon Sep 17 00:00:00 2001 From: vincent Date: Sat, 29 Aug 2026 10:19:09 +0200 Subject: [PATCH] fix: real video fullscreen, and edge navigation arrows MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The full-screen button did nothing: WKWebView inside a Tauri window has element fullscreen disabled outright, so both requestFullscreen() and WebKit's webkitEnterFullscreen() are inert, and Tauri exposes no switch for it. Fullscreening the window alone is not the same thing either — the player's header and footer stay on screen around the picture. The button now fullscreens the window and hides every piece of chrome, so the video alone fills the display, with Esc and a hover control to exit. Prev/next also get large arrows on the left and right edges of the picture, fading in on hover, alongside the header buttons. --- src-tauri/capabilities/default.json | 4 +- src-tauri/gen/schemas/capabilities.json | 2 +- src/components/Player.tsx | 130 +++++++++++++++++++++--- 3 files changed, 119 insertions(+), 17 deletions(-) diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 4d4367a..711b73f 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -8,6 +8,8 @@ "permissions": [ "core:default", "opener:default", - "dialog:default" + "dialog:default", + "core:window:allow-set-fullscreen", + "core:window:allow-is-fullscreen" ] } diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json index af42e3e..58fc9c9 100644 --- a/src-tauri/gen/schemas/capabilities.json +++ b/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","opener:default","dialog:default"]}} \ No newline at end of file +{"default":{"identifier":"default","description":"Capability for the main window","local":true,"windows":["main"],"permissions":["core:default","opener:default","dialog:default","core:window:allow-set-fullscreen","core:window:allow-is-fullscreen"]}} \ No newline at end of file diff --git a/src/components/Player.tsx b/src/components/Player.tsx index faaa27b..2940cfd 100644 --- a/src/components/Player.tsx +++ b/src/components/Player.tsx @@ -1,3 +1,4 @@ +import { getCurrentWindow } from "@tauri-apps/api/window"; import { useCallback, useEffect, useRef, useState } from "react"; import { fileUrl, openExternal, resolveStream, savePlayback } from "../api"; import type { FeedItem } from "../types"; @@ -29,17 +30,19 @@ function teardown(v: HTMLVideoElement | null) { if (!v) return; // Safari's PiP is the non-standard presentation-mode API; the spec one is // tried too, since either may be the live implementation. + const webkit = v as WebkitVideo; try { - const webkit = v as HTMLVideoElement & { - webkitPresentationMode?: string; - webkitSetPresentationMode?: (mode: string) => void; - }; if (webkit.webkitPresentationMode && webkit.webkitPresentationMode !== "inline") { webkit.webkitSetPresentationMode?.("inline"); } } catch { /* not supported here */ } + try { + if (webkit.webkitDisplayingFullscreen) webkit.webkitExitFullscreen?.(); + } catch { + /* not supported here */ + } try { if (document.pictureInPictureElement) void document.exitPictureInPicture(); } catch { @@ -60,6 +63,15 @@ function teardown(v: HTMLVideoElement | null) { } } +/** The WebKit-only members we rely on for PiP and native video fullscreen. */ +type WebkitVideo = HTMLVideoElement & { + webkitPresentationMode?: string; + webkitSetPresentationMode?: (mode: string) => void; + webkitEnterFullscreen?: () => void; + webkitExitFullscreen?: () => void; + webkitDisplayingFullscreen?: boolean; +}; + /** Save at most this often while playing; also saved on close. */ const SAVE_EVERY_MS = 5000; /** Ignore a saved position this close to either end — nothing useful to resume. */ @@ -135,12 +147,58 @@ export default function Player({ if (at > RESUME_EDGE_S && at < v.duration - RESUME_EDGE_S) v.currentTime = at; }; - const goFullscreen = () => { - // The stage rather than the