← vibesdk
src/routes/chat/components/preview-compat-banner.tsx
2.1 KB
Download file
import { useMemo, useState } from 'react';
import { ExternalLink, TriangleAlert, X } from 'lucide-react';
import { Alert, AlertDescription } from '@/components/ui/alert';
import { Button } from '@/components/ui/button';
import { isAppleWebKitBrowser, isCrossSitePreview } from '@/lib/utils';
const DISMISS_KEY = 'preview-compat-dismissed';
function readDismissed(): boolean {
try {
return sessionStorage.getItem(DISMISS_KEY) === '1';
} catch {
return false;
}
}
interface PreviewCompatBannerProps {
previewUrl: string;
}
/**
* Advisory banner shown above the preview when the viewer is on an Apple/WebKit
* browser AND the preview runs on a different registrable domain than the
* dashboard. In that case Safari blocks the cross-site preview cookie, so the
* embedded iframe's dynamic requests can fail. Opening the preview in a new tab
* loads it first-party, where it works fully.
*/
export function PreviewCompatBanner({ previewUrl }: PreviewCompatBannerProps) {
const [dismissed, setDismissed] = useState(readDismissed);
const show = useMemo(
() => isAppleWebKitBrowser() && isCrossSitePreview(previewUrl),
[previewUrl],
);
if (!show || dismissed) {
return null;
}
const dismiss = () => {
try {
sessionStorage.setItem(DISMISS_KEY, '1');
} catch {
// Ignore storage failures; banner just won't persist its dismissal.
}
setDismissed(true);
};
return (
<Alert className="rounded-none border-x-0 border-t-0 flex items-center gap-3">
<TriangleAlert className="text-amber-500" />
<AlertDescription className="flex-1">
Preview may not fully load in Safari due to preview being served from a
different domain.
</AlertDescription>
<Button
variant="outline"
size="sm"
onClick={() =>
window.open(previewUrl, '_blank', 'noopener,noreferrer')
}
>
<ExternalLink />
Open in new tab
</Button>
<button
type="button"
aria-label="Dismiss"
onClick={dismiss}
className="p-1 rounded hover:bg-kumo-elevated transition-colors"
>
<X className="size-4 text-text-primary/60" />
</button>
</Alert>
);
}