Spaces:
Runtime error
Runtime error
import { json, type LoaderFunctionArgs } from '@remix-run/cloudflare'; | |
import { useLoaderData } from '@remix-run/react'; | |
import { useCallback, useEffect, useRef, useState } from 'react'; | |
const PREVIEW_CHANNEL = 'preview-updates'; | |
export async function loader({ params }: LoaderFunctionArgs) { | |
const previewId = params.id; | |
if (!previewId) { | |
throw new Response('Preview ID is required', { status: 400 }); | |
} | |
return json({ previewId }); | |
} | |
export default function WebContainerPreview() { | |
const { previewId } = useLoaderData<typeof loader>(); | |
const iframeRef = useRef<HTMLIFrameElement>(null); | |
const broadcastChannelRef = useRef<BroadcastChannel>(); | |
const [previewUrl, setPreviewUrl] = useState(''); | |
// Handle preview refresh | |
const handleRefresh = useCallback(() => { | |
if (iframeRef.current && previewUrl) { | |
// Force a clean reload | |
iframeRef.current.src = ''; | |
requestAnimationFrame(() => { | |
if (iframeRef.current) { | |
iframeRef.current.src = previewUrl; | |
} | |
}); | |
} | |
}, [previewUrl]); | |
// Notify other tabs that this preview is ready | |
const notifyPreviewReady = useCallback(() => { | |
if (broadcastChannelRef.current && previewUrl) { | |
broadcastChannelRef.current.postMessage({ | |
type: 'preview-ready', | |
previewId, | |
url: previewUrl, | |
timestamp: Date.now(), | |
}); | |
} | |
}, [previewId, previewUrl]); | |
useEffect(() => { | |
// Initialize broadcast channel | |
broadcastChannelRef.current = new BroadcastChannel(PREVIEW_CHANNEL); | |
// Listen for preview updates | |
broadcastChannelRef.current.onmessage = (event) => { | |
if (event.data.previewId === previewId) { | |
if (event.data.type === 'refresh-preview' || event.data.type === 'file-change') { | |
handleRefresh(); | |
} | |
} | |
}; | |
// Construct the WebContainer preview URL | |
const url = `https://${previewId}.local-credentialless.webcontainer-api.io`; | |
setPreviewUrl(url); | |
// Set the iframe src | |
if (iframeRef.current) { | |
iframeRef.current.src = url; | |
} | |
// Notify other tabs that this preview is ready | |
notifyPreviewReady(); | |
// Cleanup | |
return () => { | |
broadcastChannelRef.current?.close(); | |
}; | |
}, [previewId, handleRefresh, notifyPreviewReady]); | |
return ( | |
<div className="w-full h-full"> | |
<iframe | |
ref={iframeRef} | |
title="WebContainer Preview" | |
className="w-full h-full border-none" | |
sandbox="allow-scripts allow-forms allow-popups allow-modals allow-storage-access-by-user-activation allow-same-origin" | |
allow="cross-origin-isolated" | |
loading="eager" | |
onLoad={notifyPreviewReady} | |
/> | |
</div> | |
); | |
} | |