// Native-video compositing state. // // TRACES: UR-003, UR-004 | DR-150, DR-152 // // Two separate concerns live here, deliberately: // // 1. `experimentalNativeVideo` — the user-facing opt-in flag. Rust already // decides *which backend this platform has* (`useHtml5Element` from // `player_play_item`); this flag only *suppresses* that decision so a // half-working spike cannot ship as a regression. It never turns native on // where Rust says HTML5. // // 2. `nativeVideoActive` — whether a native surface is on screen right now. // Setting it toggles `data-native-video` on , which is what the CSS in // app.css keys off to clear the app's opaque backgrounds so the SurfaceView // behind the WebView is visible. It is deliberately NOT derived from the // flag: the backgrounds must come back the moment the player unmounts. // // Frontend-only preference, stored in localStorage per the `jellytau-view-mode` // precedent in library.ts — no Rust settings command backs this. import { writable } from "svelte/store"; const STORAGE_KEY = "jellytau-experimental-native-video"; /** The attribute app.css keys its transparency rules off. */ const NATIVE_VIDEO_ATTR = "data-native-video"; function load(): boolean { if (typeof localStorage === "undefined") return false; try { return localStorage.getItem(STORAGE_KEY) === "true"; } catch { // Private-mode / disabled storage — default to the safe (HTML5) path. return false; } } function persist(enabled: boolean) { if (typeof localStorage === "undefined") return; try { localStorage.setItem(STORAGE_KEY, String(enabled)); } catch { // Quota or private-mode failure — keep the in-memory value. } } function createExperimentalNativeVideoStore() { const { subscribe, set } = writable(load()); return { subscribe, set(enabled: boolean) { persist(enabled); set(enabled); }, /** Read the current value without subscribing (init-time decisions). */ current: load, }; } /** User opt-in for the native Android video path. Default off. */ export const experimentalNativeVideo = createExperimentalNativeVideoStore(); function createNativeVideoActiveStore() { const { subscribe, set } = writable(false); return { subscribe, /** * Mark a native video surface as visible (or gone) and sync the * attribute that app.css uses to clear opaque backgrounds. */ set(active: boolean) { if (typeof document !== "undefined") { if (active) { document.documentElement.setAttribute(NATIVE_VIDEO_ATTR, "active"); } else { document.documentElement.removeAttribute(NATIVE_VIDEO_ATTR); } } set(active); }, }; } /** * Whether a native video surface is currently on screen. Must be cleared on * player teardown, or the rest of the app renders over a transparent window. */ export const nativeVideoActive = createNativeVideoActiveStore();