type FullscreenState = ReturnType> type FullscreenControls = { fullscreen: FullscreenState open: () => Promise close: () => Promise toggle: () => Promise } const syncFullscreenState = (fullscreen: FullscreenState) => { fullscreen.value = Boolean(document.fullscreenElement) } let fullscreenListenerInstalled = false export const useFullscreen = () => { const fullscreen = useState("fullscreen", () => false) const open = async () => { fullscreen.value = true if (!import.meta.client) return true if (!document.documentElement.requestFullscreen) return true try { await document.documentElement.requestFullscreen() return true } catch { // Keep the in-app fullscreen player open when native fullscreen is denied. return false } } const close = async () => { if (import.meta.client && document.fullscreenElement) { try { await document.exitFullscreen() } catch { // Fall through to keep the app state consistent. } } fullscreen.value = false return true } const toggle = async () => { if (fullscreen.value) return close() return open() } if (import.meta.client && !fullscreenListenerInstalled) { fullscreenListenerInstalled = true document.addEventListener("fullscreenchange", () => { syncFullscreenState(fullscreen) }) syncFullscreenState(fullscreen) } return { fullscreen, open, close, toggle, } as FullscreenControls }