From c34f7ea67352341100b9924aa135731b751506e6 Mon Sep 17 00:00:00 2001 From: Rudy Celekli Date: Mon, 12 Oct 2026 21:10:57 -0400 Subject: [PATCH] fix(app): Use the latest streamed preview when the sandbox becomes ready --- .../__tests__/open-generative-ui-renderer.test.tsx | 13 +++++++++++++ .../generative-ui/open-generative-ui/renderer.tsx | 11 ++++++++--- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/apps/app/src/components/generative-ui/open-generative-ui/__tests__/open-generative-ui-renderer.test.tsx b/apps/app/src/components/generative-ui/open-generative-ui/__tests__/open-generative-ui-renderer.test.tsx index 65cdb2a..bf29436 100644 --- a/apps/app/src/components/generative-ui/open-generative-ui/__tests__/open-generative-ui-renderer.test.tsx +++ b/apps/app/src/components/generative-ui/open-generative-ui/__tests__/open-generative-ui-renderer.test.tsx @@ -647,4 +647,17 @@ describe("OpenGenUIActivityRenderer", () => { }); expect(parsed.success).toBe(true); }); + + it("uses the latest streamed preview when sandbox readiness is delayed", async () => { + const base = { cssComplete: true, htmlComplete: false, generating: true }; + const { rerender } = renderRenderer({ ...base, css: "p{color:red}", html: ["

old

"] }); + await flushImport(); + rerender(rendererElement({ ...base, css: "p{color:blue}", html: ["

latest

"] })); + await act(async () => { await new Promise((r) => setTimeout(r, THROTTLE_MS + 50)); }); + await resolveSandboxReady(); + expect(runCallsContaining("Idiomorph.morph").at(-1)?.[0]).toContain("latest"); + expect(runCallsContaining("document.head.innerHTML").at(-1)?.[0]).toContain("color:blue"); + expect(mockCreate).toHaveBeenCalledTimes(1); + }); + }); diff --git a/apps/app/src/components/generative-ui/open-generative-ui/renderer.tsx b/apps/app/src/components/generative-ui/open-generative-ui/renderer.tsx index 4fb3a40..0dd8a41 100644 --- a/apps/app/src/components/generative-ui/open-generative-ui/renderer.tsx +++ b/apps/app/src/components/generative-ui/open-generative-ui/renderer.tsx @@ -173,6 +173,10 @@ const OpenGenUIActivityRendererInner = React.memo( const previewBody = partialHtml ? processPartialHtml(partialHtml) : undefined; const previewStyles = partialHtml ? extractCompleteStyles(partialHtml) : ""; const hasPreview = cssReady && !!previewBody?.trim(); + const latestPreviewRef = useRef({ css, previewStyles, previewBody }); + useEffect(() => { + latestPreviewRef.current = { css, previewStyles, previewBody }; + }, [css, previewStyles, previewBody]); const hasVisibleSandbox = !!fullHtml || hasPreview; const containerRef = useRef(null); @@ -210,15 +214,16 @@ const OpenGenUIActivityRendererInner = React.memo( sandbox.promise.then(() => { if (cancelled) return; previewReadyRef.current = true; + const latest = latestPreviewRef.current; sandbox.run(IDIOMORPH_JS); sandbox.run( `document.head.innerHTML = ${JSON.stringify( - buildPreviewHeadContent(css, previewStyles) + buildPreviewHeadContent(latest.css, latest.previewStyles) )}` ); - if (previewBody) { - sandbox.run(buildPreviewBodyMorph(previewBody)); + if (latest.previewBody) { + sandbox.run(buildPreviewBodyMorph(latest.previewBody)); } }); })