import type { ViewportSize } from '@/app/document/io/types' export function resolveBrowserFileURL(path: string): URL { const url = new URL(path, window.location.href) url.hash = '' return url } export function yieldToUI(): Promise { return new Promise((resolve) => { requestAnimationFrame(() => resolve()) }) } type ViewportEditor = { zoomToFit: () => void } export function createDocumentViewportActions(editor: ViewportEditor, viewportSize: ViewportSize) { function setViewportSize(width: number, height: number) { viewportSize.width = width viewportSize.height = height } async function fitCurrentPageToViewport() { await yieldToUI() editor.zoomToFit() } return { setViewportSize, fitCurrentPageToViewport } } // The File System Access pickers (showSaveFilePicker/showOpenFilePicker) and // window.prompt are blocked by browsers inside cross-origin sub-frames // (Chrome: "Cross origin sub frames aren't allowed to show a file picker"). // Detect that context so callers can fall back to a plain download / file // input instead of surfacing the picker error. export function isCrossOriginSubFrame(): boolean { const top = window.top if (!top || top === window) return false try { // Same-origin sub-frame: the top window is reachable and pickers are allowed. void top.location.href return false } catch { // Cross-origin sub-frame: top navigation is blocked, pickers are not allowed. return true } } export function downloadBlob(data: Uint8Array, filename: string, mime: string) { const blob = new Blob([data.buffer as ArrayBuffer], { type: mime }) const url = URL.createObjectURL(blob) const a = document.createElement('a') a.href = url a.download = filename a.style.display = 'none' document.body.appendChild(a) a.click() setTimeout(() => { document.body.removeChild(a) URL.revokeObjectURL(url) }, 100) }