MCPcopy Index your code
hub / github.com/enowdev/enowX-Coder / HtmlPreview

Function HtmlPreview

src/components/chat/HtmlPreview.tsx:49–211  ·  view source on GitHub ↗
({ code, title, onSendPrompt })

Source from the content-addressed store, hash-verified

47}
48
49export const HtmlPreview: React.FC<HtmlPreviewProps> = ({ code, title, onSendPrompt }) => {
50 const iframeRef = useRef<HTMLIFrameElement>(null);
51 const [expanded, setExpanded] = useState(false);
52 const [showSource, setShowSource] = useState(false);
53 const [copied, setCopied] = useState(false);
54 const [iframeHeight, setIframeHeight] = useState(200);
55 const theme = useUIStore((s) => s.theme);
56
57 // Debounced srcdoc — only rebuild after code stops changing for 400ms
58 // This prevents iframe reload on every streaming token
59 const [debouncedCode, setDebouncedCode] = useState(code);
60 const debounceTimer = useRef<ReturnType<typeof setTimeout> | null>(null);
61
62 useEffect(() => {
63 if (debounceTimer.current) clearTimeout(debounceTimer.current);
64 debounceTimer.current = setTimeout(() => {
65 setDebouncedCode(code);
66 }, 400);
67 return () => { if (debounceTimer.current) clearTimeout(debounceTimer.current); };
68 }, [code]);
69
70 const srcdoc = React.useMemo(() => buildSrcdoc(debouncedCode, theme), [debouncedCode, theme]);
71
72 // Measure iframe content height directly via contentDocument (same-origin allowed by sandbox)
73 const pollTimers = useRef<ReturnType<typeof setTimeout>[]>([]);
74
75 const measureHeight = useCallback(() => {
76 const iframe = iframeRef.current;
77 if (!iframe) return;
78 try {
79 const doc = iframe.contentDocument;
80 if (!doc?.body) return;
81 // Wrap all body content in a measuring div to get exact height
82 const h = doc.body.scrollHeight;
83 if (h > 20) {
84 setIframeHeight(h);
85 }
86 } catch {}
87 }, []);
88
89 // Poll height after load and at intervals to catch async renders (Chart.js etc)
90 const startPolling = useCallback(() => {
91 pollTimers.current.forEach(clearTimeout);
92 pollTimers.current = [];
93 [0, 100, 300, 600, 1000, 2000, 4000].forEach((delay) => {
94 pollTimers.current.push(setTimeout(measureHeight, delay));
95 });
96 }, [measureHeight]);
97
98 // Re-poll when debounced code changes (iframe reloads)
99 useEffect(() => {
100 startPolling();
101 return () => pollTimers.current.forEach(clearTimeout);
102 }, [debouncedCode, startPolling]);
103
104 // Re-measure on window resize so iframe adapts when user resizes the app
105 useEffect(() => {
106 let resizeRaf = 0;

Callers

nothing calls this directly

Calls 2

cnFunction · 0.90
buildSrcdocFunction · 0.85

Tested by

no test coverage detected