Rewrite oklch/color-mix and space-separated rgb before iframe preview so pie and bar fills are not dropped on Chrome 109. Co-authored-by: Cursor <cursoragent@cursor.com>
46 lines
1.5 KiB
TypeScript
46 lines
1.5 KiB
TypeScript
import { useEffect, useState } from "react";
|
|
|
|
import { prepareGeneratedHtml } from "@/core/utils/legacy-html-preview";
|
|
|
|
/**
|
|
* Sandboxed preview of a generated HTML document.
|
|
*
|
|
* Renders via a `blob:` URL rather than `srcDoc`. A srcDoc document's base URL
|
|
* is `about:srcdoc`, so clicking an in-page anchor (`<a href="#section">`)
|
|
* navigates the frame to `about:blank#section` and BLANKS it. A `blob:` URL is
|
|
* a real document URL, so fragment links scroll within the frame as expected —
|
|
* all without enabling scripts (the sandbox stays script-free for security; the
|
|
* server already sanitizes script vectors and this is defense-in-depth).
|
|
*
|
|
* The `charset=utf-8` on the blob type is required: a blob: URL whose
|
|
* Content-Type omits the charset is decoded with the OS default (GBK/Latin-1 on
|
|
* Chinese Windows), garbling CJK text when the HTML lacks a `<meta charset>`.
|
|
*/
|
|
export function HtmlPreviewFrame({
|
|
html,
|
|
className = "h-[70vh] w-full rounded-md border bg-white",
|
|
}: {
|
|
html: string;
|
|
className?: string;
|
|
}) {
|
|
const [url, setUrl] = useState<string>();
|
|
|
|
useEffect(() => {
|
|
const blob = new Blob([prepareGeneratedHtml(html ?? "")], {
|
|
type: "text/html;charset=utf-8",
|
|
});
|
|
const objectUrl = URL.createObjectURL(blob);
|
|
setUrl(objectUrl);
|
|
return () => URL.revokeObjectURL(objectUrl);
|
|
}, [html]);
|
|
|
|
return (
|
|
<iframe
|
|
title="HTML 预览"
|
|
sandbox="allow-scripts allow-forms"
|
|
src={url}
|
|
className={className}
|
|
/>
|
|
);
|
|
}
|