import { useCallback, useEffect, useState, type RefObject, type SyntheticEvent } from "react"; import { AlertTriangle, Loader2, ShieldAlert } from "lucide-react"; import type { EmbedStatus } from "../hooks/usePreviewEmbed"; export interface PreviewIframeProps { url: string | null; embedStatus: EmbedStatus; onEmbedStatusChange: (status: EmbedStatus) => void; iframeRef: RefObject; blockReason: string | null; onRetry?: () => void; className?: string; /** @deprecated Use blockReason instead */ embedContext?: string | null; } const DEFAULT_IFRAME_CLASS = "devserver-preview-iframe"; export function PreviewIframe({ url, embedStatus, onEmbedStatusChange, iframeRef, blockReason, onRetry, className = DEFAULT_IFRAME_CLASS, embedContext: deprecatedEmbedContext, }: PreviewIframeProps) { // Support both blockReason and legacy embedContext const context = blockReason ?? deprecatedEmbedContext ?? null; const [attempt, setAttempt] = useState(0); useEffect(() => { if (!url || embedStatus !== "unknown") { return; } setAttempt((current) => current + 1); onEmbedStatusChange("loading"); }, [embedStatus, onEmbedStatusChange, url]); const handleLoad = useCallback(() => { const iframeEl = iframeRef.current; if (!iframeEl) { onEmbedStatusChange("embedded"); return; } try { const frameHref = iframeEl.contentWindow?.location?.href; if (frameHref === "about:blank" && iframeEl.src !== "about:blank") { onEmbedStatusChange("blocked"); return; } } catch { // Cross-origin access can throw; do not treat it as blocked. } onEmbedStatusChange("embedded"); }, [iframeRef, onEmbedStatusChange]); const handleError = useCallback((event: SyntheticEvent) => { event.stopPropagation(); onEmbedStatusChange("error"); }, [onEmbedStatusChange]); const handleOpenInNewTab = useCallback(() => { if (!url) { return; } window.open(url, "_blank", "noopener,noreferrer"); }, [url]); if (!url) { return null; } return (