UNPKG

@assistant-ui/react

Version:

Open-source TypeScript/React library for building production-grade AI chat experiences

189 lines (188 loc) • 5.73 kB
"use client"; import { useIsomorphicLayoutEffect } from "../utils/useIsomorphicLayoutEffect.js"; import { invokeUserCallback } from "@assistant-ui/core/internal"; import { c } from "@assistant-ui/tap/react-shim/compiler-runtime"; import { useEffect, useRef, useState } from "@assistant-ui/tap/react-shim"; import { jsx } from "react/jsx-runtime"; import { SafeContentFrame, isShimLoadError } from "safe-content-frame"; // src/sandbox-host/SandboxHost.tsx const DEFAULT_PRODUCT = "assistant-ui-sandbox"; const DEFAULT_MAX_HEIGHT = 800; const LOAD_TIMEOUT_MS = 1e4; function isSandboxFrameMessage(event, frame) { return event.source === frame.iframe.contentWindow && event.origin === frame.origin; } function SandboxHost(t0) { const $ = c(21); const { content, contentKey, sandbox, maxHeight: t1, createBridge, onError, containerProps } = t0; const maxHeight = t1 === void 0 ? DEFAULT_MAX_HEIGHT : t1; const containerRef = useRef(null); const [contentHeight, setContentHeight] = useState(void 0); let t2; if ($[0] !== content || $[1] !== createBridge || $[2] !== onError || $[3] !== sandbox) { t2 = { content, sandbox, createBridge, onError }; $[0] = content; $[1] = createBridge; $[2] = onError; $[3] = sandbox; $[4] = t2; } else t2 = $[4]; const liveRef = useRef(t2); let t3; let t4; if ($[5] !== content || $[6] !== createBridge || $[7] !== onError || $[8] !== sandbox) { t3 = () => { liveRef.current = { content, sandbox, createBridge, onError }; }; t4 = [ content, sandbox, createBridge, onError ]; $[5] = content; $[6] = createBridge; $[7] = onError; $[8] = sandbox; $[9] = t3; $[10] = t4; } else { t3 = $[9]; t4 = $[10]; } useIsomorphicLayoutEffect(t3, t4); let t5; if ($[11] === Symbol.for("react.memo_cache_sentinel")) { t5 = () => { const container = containerRef.current; if (!container) return; let cancelled = false; let frame = null; let bridge = null; let onMessage = null; const renderController = new AbortController(); const { content: liveContent, sandbox: sb } = liveRef.current; const reportError = (err) => { const error = err instanceof Error ? err : new Error(String(err)); invokeUserCallback("assistant-ui", "SandboxHost onError", liveRef.current.onError?.bind(liveRef.current), error); }; const scf = new SafeContentFrame(sb?.product ?? DEFAULT_PRODUCT, { ...sb?.sandbox !== void 0 && { sandbox: sb.sandbox }, ...sb?.useShadowDom !== void 0 && { useShadowDom: sb.useShadowDom }, ...sb?.enableBrowserCaching !== void 0 && { enableBrowserCaching: sb.enableBrowserCaching }, ...sb?.salt !== void 0 && { salt: sb.salt } }); const renderOpts = { signal: renderController.signal, ...sb?.unsafeDocumentWrite !== void 0 && { unsafeDocumentWrite: sb.unsafeDocumentWrite } }; scf.renderHtml(liveContent.html, container, renderOpts).then((rendered) => { if (cancelled) { rendered.dispose(); return; } frame = rendered; bridge = liveRef.current.createBridge({ iframe: rendered.iframe, origin: rendered.origin, sendMessage: rendered.sendMessage }, { setHeight: (height) => { if (typeof height === "number" && Number.isFinite(height) && height > 0) setContentHeight(height); } }); onMessage = (event) => { if (!isSandboxFrameMessage(event, rendered)) return; bridge?.onMessage(event); }; window.addEventListener("message", onMessage); rendered.fullyLoadedPromiseWithTimeout(LOAD_TIMEOUT_MS).catch((err_0) => { if (cancelled) return; if (isShimLoadError(err_0) && err_0.code === "render-timeout") return; reportError(err_0); }); }).catch((err_1) => { if (cancelled) return; frame?.dispose(); frame = null; reportError(err_1); }); return () => { cancelled = true; let cleanupFailed = false; let cleanupError; const runCleanup = (cleanup) => { try { cleanup(); } catch (t6) { const error_0 = t6; if (cleanupFailed) console.error(error_0); else { cleanupFailed = true; cleanupError = error_0; } } }; if (onMessage) { const listener = onMessage; onMessage = null; runCleanup(() => window.removeEventListener("message", listener)); } const bridgeToDispose = bridge; bridge = null; if (bridgeToDispose) runCleanup(() => bridgeToDispose.dispose()); runCleanup(() => renderController.abort()); const frameToDispose = frame; frame = null; if (frameToDispose) runCleanup(() => frameToDispose.dispose()); runCleanup(() => setContentHeight(void 0)); if (cleanupFailed) throw cleanupError; }; }; $[11] = t5; } else t5 = $[11]; let t6; if ($[12] !== contentKey) { t6 = [contentKey]; $[12] = contentKey; $[13] = t6; } else t6 = $[13]; useEffect(t5, t6); const resolvedHeight = contentHeight != null ? Math.min(contentHeight, maxHeight) : void 0; let t7; if ($[14] !== resolvedHeight || $[15] !== sandbox?.style) { t7 = resolvedHeight != null ? { ...sandbox?.style, height: resolvedHeight } : sandbox?.style; $[14] = resolvedHeight; $[15] = sandbox?.style; $[16] = t7; } else t7 = $[16]; const mergedStyle = t7; const t8 = sandbox?.className; let t9; if ($[17] !== containerProps || $[18] !== mergedStyle || $[19] !== t8) { t9 = /* @__PURE__ */ jsx("div", { ...containerProps, ref: containerRef, className: t8, style: mergedStyle }); $[17] = containerProps; $[18] = mergedStyle; $[19] = t8; $[20] = t9; } else t9 = $[20]; return t9; } export { SandboxHost, isSandboxFrameMessage }; //# sourceMappingURL=SandboxHost.js.map