markdown-flow-ui 0.1.97 → 0.1.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/@braintree_sanitize-url@7.1.1/node_modules/@braintree/sanitize-url/dist/index.cjs.js +1 -1
  2. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/@braintree_sanitize-url@7.1.1/node_modules/@braintree/sanitize-url/dist/index.cjs.js.map +1 -1
  3. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/@braintree_sanitize-url@7.1.1/node_modules/@braintree/sanitize-url/dist/index.es.js +1 -1
  4. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.cjs.js +1 -1
  5. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.es.js +1 -1
  6. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/hast-util-to-jsx-runtime@2.3.6/node_modules/hast-util-to-jsx-runtime/lib/index.cjs.js +1 -1
  7. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/hast-util-to-jsx-runtime@2.3.6/node_modules/hast-util-to-jsx-runtime/lib/index.es.js +1 -1
  8. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/c4Diagram-YG6GDRKO.cjs.js +1 -1
  9. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/c4Diagram-YG6GDRKO.es.js +1 -1
  10. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-S3R3BYOJ.cjs.js +1 -1
  11. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-S3R3BYOJ.es.js +1 -1
  12. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-TZMSLE5B.cjs.js +1 -1
  13. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/chunk-TZMSLE5B.es.js +1 -1
  14. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/ganttDiagram-LVOFAZNH.cjs.js +1 -1
  15. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/ganttDiagram-LVOFAZNH.es.js +1 -1
  16. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/sequenceDiagram-WL72ISMW.cjs.js +1 -1
  17. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/mermaid@11.12.1/node_modules/mermaid/dist/chunks/mermaid.core/sequenceDiagram-WL72ISMW.es.js +1 -1
  18. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-input@1.8.0_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-input/es/BaseInput.cjs.js +1 -1
  19. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-input@1.8.0_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-input/es/BaseInput.es.js +1 -1
  20. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-textarea@1.10.2_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-textarea/es/ResizableTextArea.cjs.js +1 -1
  21. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-textarea@1.10.2_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-textarea/es/ResizableTextArea.es.js +1 -1
  22. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-textarea@1.10.2_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-textarea/es/TextArea.cjs.js +1 -1
  23. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-textarea@1.10.2_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-textarea/es/TextArea.es.js +1 -1
  24. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-textarea@1.10.2_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-textarea/es/index.cjs.js +1 -1
  25. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-textarea@1.10.2_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-textarea/es/index.es.js +1 -1
  26. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-util@5.44.4_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-util/es/ref.cjs.js +1 -1
  27. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/rc-util@5.44.4_react-dom@19.0.1_react@19.0.1__react@19.0.1/node_modules/rc-util/es/ref.es.js +1 -1
  28. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/remark-flow@0.1.6/node_modules/remark-flow/dist/index.cjs.js +1 -1
  29. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/remark-flow@0.1.6/node_modules/remark-flow/dist/index.es.js +1 -1
  30. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/remark-flow@0.1.6/node_modules/remark-flow/dist/remark-custom-variable.cjs.js +1 -1
  31. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/remark-flow@0.1.6/node_modules/remark-flow/dist/remark-custom-variable.es.js +1 -1
  32. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/remark-flow@0.1.6/node_modules/remark-flow/dist/remark-interaction.cjs.js +1 -1
  33. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/remark-flow@0.1.6/node_modules/remark-flow/dist/remark-interaction.es.js +1 -1
  34. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/style-to-object@1.0.11/node_modules/style-to-object/cjs/index.cjs.js +1 -1
  35. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/style-to-object@1.0.11/node_modules/style-to-object/cjs/index.es.js +1 -1
  36. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/unified@11.0.5/node_modules/unified/lib/index.cjs.js +1 -1
  37. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/unified@11.0.5/node_modules/unified/lib/index.es.js +1 -1
  38. package/dist/_virtual/index.cjs10.js +1 -1
  39. package/dist/_virtual/index.cjs11.js +1 -1
  40. package/dist/_virtual/index.cjs2.js +1 -1
  41. package/dist/_virtual/index.cjs3.js +1 -1
  42. package/dist/_virtual/index.cjs4.js +1 -1
  43. package/dist/_virtual/index.cjs5.js +1 -1
  44. package/dist/_virtual/index.cjs6.js +1 -1
  45. package/dist/_virtual/index.cjs7.js +1 -1
  46. package/dist/_virtual/index.cjs8.js +1 -1
  47. package/dist/_virtual/index.cjs9.js +1 -1
  48. package/dist/_virtual/index.es10.js +2 -3
  49. package/dist/_virtual/index.es10.js.map +1 -1
  50. package/dist/_virtual/index.es11.js +3 -2
  51. package/dist/_virtual/index.es11.js.map +1 -1
  52. package/dist/_virtual/index.es2.js +2 -5
  53. package/dist/_virtual/index.es2.js.map +1 -1
  54. package/dist/_virtual/index.es3.js +4 -2
  55. package/dist/_virtual/index.es3.js.map +1 -1
  56. package/dist/_virtual/index.es4.js +5 -4
  57. package/dist/_virtual/index.es4.js.map +1 -1
  58. package/dist/_virtual/index.es5.js +5 -2
  59. package/dist/_virtual/index.es5.js.map +1 -1
  60. package/dist/_virtual/index.es6.js +4 -4
  61. package/dist/_virtual/index.es7.js +2 -5
  62. package/dist/_virtual/index.es7.js.map +1 -1
  63. package/dist/_virtual/index.es8.js +3 -2
  64. package/dist/_virtual/index.es8.js.map +1 -1
  65. package/dist/_virtual/index.es9.js +2 -3
  66. package/dist/_virtual/index.es9.js.map +1 -1
  67. package/dist/components/ContentRender/IframeSandbox.cjs.js +4 -4
  68. package/dist/components/ContentRender/IframeSandbox.cjs.js.map +1 -1
  69. package/dist/components/ContentRender/IframeSandbox.es.js +164 -145
  70. package/dist/components/ContentRender/IframeSandbox.es.js.map +1 -1
  71. package/dist/components/ContentRender/SandboxApp.cjs.js +2 -2
  72. package/dist/components/ContentRender/SandboxApp.cjs.js.map +1 -1
  73. package/dist/components/ContentRender/SandboxApp.es.js +126 -90
  74. package/dist/components/ContentRender/SandboxApp.es.js.map +1 -1
  75. package/dist/components/ui/inputGroup/textarea.cjs.js +1 -1
  76. package/dist/components/ui/inputGroup/textarea.es.js +1 -1
  77. package/dist/markdown-flow-ui-lib.css +1 -1
  78. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"IframeSandbox.cjs.js","sources":["../../../src/components/ContentRender/IframeSandbox.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport SandboxApp from \"./SandboxApp\";\nimport { splitContentSegments } from \"./utils/split-content\";\nimport ContentRender from \"./ContentRender\";\n// Lazy-load iframe vendor libraries and inject them into the sandbox document.\nconst loadBlackboardVendor = () =>\n import(\"./blackboard-vendor\").then((m) => m.injectBlackboardLibraries);\nexport interface IframeSandboxProps {\n content: string;\n className?: string;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n fullScreenButtonText?: string;\n hideFullScreen?: boolean;\n mode?: \"content\" | \"blackboard\";\n type: \"sandbox\" | \"markdown\";\n}\n\nconst normalizeTailwindHeightTokens = (className: string) =>\n className\n .split(/\\s+/)\n .filter(Boolean)\n .map((token) => token.split(\":\").pop() || token);\n\nconst parseViewportHeightCss = (value: string) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const matched = normalized.match(/^([0-9.]+)(vh|dvh|svh|lvh)$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst extractViewportHeightFromTailwindClass = (className: string) => {\n if (!className.trim()) return null;\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n if (\n normalizedTokens.includes(\"h-screen\") ||\n normalizedTokens.includes(\"h-dvh\")\n ) {\n return \"100dvh\";\n }\n if (normalizedTokens.includes(\"h-svh\")) {\n return \"100svh\";\n }\n if (normalizedTokens.includes(\"h-lvh\")) {\n return \"100lvh\";\n }\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+(vh|dvh|svh|lvh)\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)(vh|dvh|svh|lvh)\\]$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst IframeSandbox: React.FC<IframeSandboxProps> = ({\n content,\n type,\n className,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n hideFullScreen = false,\n mode = \"content\",\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const rootRef = useRef<Root | null>(null);\n const docRef = useRef<Document | null>(null);\n const updateHeightRef = useRef<() => void>(() => {});\n const [, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const isBlackboardMode = mode === \"blackboard\";\n const prevHtmlRef = useRef<string>(\"\");\n const htmlContent = React.useMemo(() => {\n const segments = splitContentSegments(content);\n // console.log('segments=====', segments);\n const sandboxSegments = segments.filter((seg) => seg.type === \"sandbox\");\n const sandboxContent =\n mode === \"blackboard\"\n ? sandboxSegments[sandboxSegments.length - 1]?.value || \"\"\n : sandboxSegments.map((seg) => seg.value).join(\"\\n\");\n return sandboxContent || \"\";\n }, [content, mode]);\n const rootViewportHeightCss = React.useMemo(() => {\n const normalized = htmlContent.trim();\n if (!normalized) return null;\n const rootMatch = normalized.match(/^<([a-zA-Z][\\w:-]*)(\\s[^>]*?)?>/);\n if (!rootMatch) return null;\n const attrs = rootMatch[2] || \"\";\n const heightAttrMatch = attrs.match(/\\bheight\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (heightAttrMatch) {\n const explicitHeightCss = parseViewportHeightCss(heightAttrMatch[1]);\n if (explicitHeightCss) return explicitHeightCss;\n }\n const styleAttrMatch = attrs.match(/\\bstyle\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n const styleHeightMatch = styleAttrMatch?.match(\n /\\bheight\\s*:\\s*([^;]+)/i\n )?.[1];\n if (styleHeightMatch) {\n const styleHeightCss = parseViewportHeightCss(styleHeightMatch);\n if (styleHeightCss) return styleHeightCss;\n }\n const classAttrMatch = attrs.match(/\\bclass\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n if (!classAttrMatch) return null;\n return extractViewportHeightFromTailwindClass(classAttrMatch);\n }, [htmlContent]);\n const hasRootVhHeight = Boolean(rootViewportHeightCss);\n useEffect(() => {\n if (mode !== \"blackboard\") {\n prevHtmlRef.current = htmlContent;\n return;\n }\n const prev = prevHtmlRef.current;\n const isContinuation = prev && htmlContent.startsWith(prev);\n if (!isContinuation && prev) {\n setResetToken((token) => token + 1);\n }\n prevHtmlRef.current = htmlContent;\n }, [htmlContent, mode]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe) return undefined;\n\n const doc = iframe.contentDocument;\n if (!doc) return undefined;\n\n doc.open();\n doc.write(`<!DOCTYPE html>\n<html${mode === \"blackboard\" ? ' style=\"height: 100%;\"' : \"\"}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <style>\n html, body, #root { width: 100%; height: 100%; }\n html, body { margin: 0; padding: 0; overflow: auto; }\n *, *::before, *::after { box-sizing: border-box; }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n </body>\n</html>`);\n doc.close();\n\n docRef.current = doc;\n\n const rootEl = doc.getElementById(\"root\");\n if (!rootEl) return undefined;\n\n const root = createRoot(rootEl);\n rootRef.current = root;\n let isDestroyed = false;\n\n const parseExplicitHeight = (\n value: string,\n parentViewportHeight: number\n ) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const numeric = Number.parseFloat(normalized);\n if (Number.isNaN(numeric)) return null;\n if (/(dvh|svh|lvh|vh)$/i.test(normalized)) {\n return (numeric / 100) * parentViewportHeight;\n }\n if (normalized.endsWith(\"px\") || /^[0-9.]+$/.test(normalized)) {\n return numeric;\n }\n return null;\n };\n const parseTailwindHeightClass = (\n className: string,\n parentViewportHeight: number\n ) => {\n if (!className.trim()) return null;\n const viewportHeightCss =\n extractViewportHeightFromTailwindClass(className);\n if (viewportHeightCss) {\n return parseExplicitHeight(viewportHeightCss, parentViewportHeight);\n }\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+px\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)px\\]$/i);\n if (!matched) return null;\n const numeric = Number.parseFloat(matched[1]);\n if (Number.isNaN(numeric)) return null;\n return numeric;\n };\n\n const resolveExplicitHeight = () => {\n if (!iframeRef.current || !doc.body) return null;\n const wrapper = doc.body.querySelector(\n \".sandbox-wrapper\"\n ) as HTMLElement | null;\n const container = wrapper?.firstElementChild as HTMLElement | null;\n if (!container) return null;\n const elements = Array.from(container.children) as HTMLElement[];\n if (elements.length !== 1) return null;\n const target = elements[0];\n const heightValue = target.style.height || target.getAttribute(\"height\");\n const parentViewportHeight =\n iframeRef.current.ownerDocument?.documentElement?.clientHeight ||\n window.innerHeight;\n const parsed = heightValue\n ? parseExplicitHeight(heightValue, parentViewportHeight)\n : null;\n if (parsed !== null) {\n return Math.ceil(parsed);\n }\n const classHeight = parseTailwindHeightClass(\n target.getAttribute(\"class\") || \"\",\n parentViewportHeight\n );\n return classHeight !== null ? Math.ceil(classHeight) : null;\n };\n\n const updateHeight = () => {\n if (!iframeRef.current || !doc.body) return;\n const bodyRect = doc.body.getBoundingClientRect();\n const htmlRect = doc.documentElement?.getBoundingClientRect();\n const bodyHeight = bodyRect.height;\n const htmlHeight = htmlRect?.height || 0;\n const contentHeight = Math.max(bodyHeight, htmlHeight);\n const explicitHeight = resolveExplicitHeight();\n const nextHeight = Math.max(\n 200,\n explicitHeight ?? Math.ceil(contentHeight)\n );\n setHeight(nextHeight);\n };\n const scheduleHeightUpdate = () => {\n requestAnimationFrame(() => {\n if (isDestroyed) return;\n updateHeight();\n });\n };\n updateHeightRef.current = scheduleHeightUpdate;\n\n updateHeight();\n scheduleHeightUpdate();\n\n // Inject Tailwind/DaisyUI/GSAP into iframe for all sandbox modes.\n // Dynamic import keeps ~3.3 MB of vendor libs out of the main bundle.\n // Tailwind's MutationObserver ensures styles apply even if content renders first.\n loadBlackboardVendor()\n .then((inject) => {\n if (isDestroyed) return;\n inject(doc);\n scheduleHeightUpdate();\n })\n .catch(() => {\n if (isDestroyed) return;\n scheduleHeightUpdate();\n });\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\n isDestroyed = true;\n resizeObserver.disconnect();\n // Defer unmount to avoid React warning when parent is mid-render\n setTimeout(() => {\n root.unmount();\n rootRef.current = null;\n docRef.current = null;\n updateHeightRef.current = () => {};\n }, 0);\n };\n }, []);\n\n useEffect(() => {\n const onFullscreenChange = () => {\n setIsFullscreen(Boolean(document.fullscreenElement));\n };\n document.addEventListener(\"fullscreenchange\", onFullscreenChange);\n return () =>\n document.removeEventListener(\"fullscreenchange\", onFullscreenChange);\n }, []);\n\n const toggleFullscreen = () => {\n const target = containerRef.current || iframeRef.current;\n if (!target) return;\n if (document.fullscreenElement) {\n document.exitFullscreen().catch(() => {});\n return;\n }\n if (target.requestFullscreen) {\n target.requestFullscreen().catch(() => {});\n }\n };\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n root.render(\n <SandboxApp\n html={htmlContent}\n loadingText={loadingText}\n styleLoadingText={styleLoadingText}\n scriptLoadingText={scriptLoadingText}\n fullScreenButtonText={fullScreenButtonText}\n hideFullScreen={hideFullScreen}\n resetToken={resetToken}\n hasRootVhHeight={hasRootVhHeight}\n mode={mode}\n />\n );\n requestAnimationFrame(() => updateHeightRef.current?.());\n }, [\n content,\n htmlContent,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n resetToken,\n mode,\n ]);\n const containerClassName = [\n \"w-full relative content-render-iframe-sandbox\",\n isBlackboardMode\n ? \"h-full overflow-auto flex flex-col\"\n : \"aspect-[16/9] overflow-hidden flex items-center justify-center\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n ref={containerRef}\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className={containerClassName}\n >\n {!hideFullScreen && (\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n className={\n \"absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer\"\n }\n >\n {isFullscreen ? \"退出全屏\" : fullScreenButtonText || \"全屏浏览\"}\n </button>\n )}\n {mode === \"blackboard\" && type === \"markdown\" ? (\n <ContentRender content={content} />\n ) : (\n <iframe\n ref={iframeRef}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n allow=\"fullscreen\"\n allowFullScreen\n className={[className, \"w-full h-full mx-auto my-auto block\"]\n .filter(Boolean)\n .join(\" \")}\n style={{\n height: \"100%\",\n margin: \"auto\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["loadBlackboardVendor","m","normalizeTailwindHeightTokens","className","token","parseViewportHeightCss","value","normalized","matched","extractViewportHeightFromTailwindClass","normalizedTokens","arbitraryToken","IframeSandbox","content","type","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","isBlackboardMode","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","hasRootVhHeight","rootMatch","attrs","heightAttrMatch","explicitHeightCss","styleHeightMatch","styleHeightCss","classAttrMatch","useEffect","prev","iframe","doc","rootEl","root","createRoot","isDestroyed","parseExplicitHeight","parentViewportHeight","numeric","parseTailwindHeightClass","viewportHeightCss","resolveExplicitHeight","container","elements","target","heightValue","parsed","classHeight","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","explicitHeight","nextHeight","scheduleHeightUpdate","inject","resizeObserver","onFullscreenChange","toggleFullscreen","jsx","SandboxApp","containerClassName","jsxs","ContentRender"],"mappings":"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"}
1
+ {"version":3,"file":"IframeSandbox.cjs.js","sources":["../../../src/components/ContentRender/IframeSandbox.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport SandboxApp from \"./SandboxApp\";\nimport { splitContentSegments } from \"./utils/split-content\";\nimport ContentRender from \"./ContentRender\";\n// Lazy-load iframe vendor libraries and inject them into the sandbox document.\nconst loadBlackboardVendor = () =>\n import(\"./blackboard-vendor\").then((m) => m.injectBlackboardLibraries);\n\nconst COMPLETE_IMAGE_TAG_PATTERN = /<img\\b[^>]*>/i;\nconst POST_IMAGE_STREAM_DEBOUNCE_MS = 180;\nexport interface IframeSandboxProps {\n content: string;\n className?: string;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n fullScreenButtonText?: string;\n hideFullScreen?: boolean;\n mode?: \"content\" | \"blackboard\";\n type: \"sandbox\" | \"markdown\";\n}\n\nconst normalizeTailwindHeightTokens = (className: string) =>\n className\n .split(/\\s+/)\n .filter(Boolean)\n .map((token) => token.split(\":\").pop() || token);\n\nconst parseViewportHeightCss = (value: string) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const matched = normalized.match(/^([0-9.]+)(vh|dvh|svh|lvh)$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst extractViewportHeightFromTailwindClass = (className: string) => {\n if (!className.trim()) return null;\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n if (\n normalizedTokens.includes(\"h-screen\") ||\n normalizedTokens.includes(\"h-dvh\")\n ) {\n return \"100dvh\";\n }\n if (normalizedTokens.includes(\"h-svh\")) {\n return \"100svh\";\n }\n if (normalizedTokens.includes(\"h-lvh\")) {\n return \"100lvh\";\n }\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+(vh|dvh|svh|lvh)\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)(vh|dvh|svh|lvh)\\]$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst IframeSandbox: React.FC<IframeSandboxProps> = ({\n content,\n type,\n className,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n hideFullScreen = false,\n mode = \"content\",\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const rootRef = useRef<Root | null>(null);\n const docRef = useRef<Document | null>(null);\n const updateHeightRef = useRef<() => void>(() => {});\n const [, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const isBlackboardMode = mode === \"blackboard\";\n const prevHtmlRef = useRef<string>(\"\");\n const htmlContent = React.useMemo(() => {\n const segments = splitContentSegments(content);\n // console.log('segments=====', segments);\n const sandboxSegments = segments.filter((seg) => seg.type === \"sandbox\");\n const sandboxContent =\n mode === \"blackboard\"\n ? sandboxSegments[sandboxSegments.length - 1]?.value || \"\"\n : sandboxSegments.map((seg) => seg.value).join(\"\\n\");\n return sandboxContent || \"\";\n }, [content, mode]);\n const [renderHtmlContent, setRenderHtmlContent] = useState(htmlContent);\n const prevIncomingHtmlRef = useRef(htmlContent);\n const pendingHtmlRef = useRef(htmlContent);\n const deferRenderTimerRef = useRef<number | null>(null);\n\n const clearDeferredRenderTimer = () => {\n if (deferRenderTimerRef.current === null) return;\n window.clearTimeout(deferRenderTimerRef.current);\n deferRenderTimerRef.current = null;\n };\n\n useEffect(\n () => () => {\n clearDeferredRenderTimer();\n },\n []\n );\n\n useEffect(() => {\n const prevIncomingHtml = prevIncomingHtmlRef.current;\n prevIncomingHtmlRef.current = htmlContent;\n\n const isAppendOnlyStream =\n !!prevIncomingHtml &&\n htmlContent.length > prevIncomingHtml.length &&\n htmlContent.startsWith(prevIncomingHtml);\n const containsCompleteImage = COMPLETE_IMAGE_TAG_PATTERN.test(htmlContent);\n const shouldDeferRender = isAppendOnlyStream && containsCompleteImage;\n\n if (!shouldDeferRender) {\n clearDeferredRenderTimer();\n pendingHtmlRef.current = htmlContent;\n setRenderHtmlContent(htmlContent);\n return;\n }\n\n pendingHtmlRef.current = htmlContent;\n clearDeferredRenderTimer();\n deferRenderTimerRef.current = window.setTimeout(() => {\n setRenderHtmlContent(pendingHtmlRef.current);\n deferRenderTimerRef.current = null;\n }, POST_IMAGE_STREAM_DEBOUNCE_MS);\n }, [htmlContent]);\n\n const rootViewportHeightCss = React.useMemo(() => {\n const normalized = renderHtmlContent.trim();\n if (!normalized) return null;\n const rootMatch = normalized.match(/^<([a-zA-Z][\\w:-]*)(\\s[^>]*?)?>/);\n if (!rootMatch) return null;\n const attrs = rootMatch[2] || \"\";\n const heightAttrMatch = attrs.match(/\\bheight\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (heightAttrMatch) {\n const explicitHeightCss = parseViewportHeightCss(heightAttrMatch[1]);\n if (explicitHeightCss) return explicitHeightCss;\n }\n const styleAttrMatch = attrs.match(/\\bstyle\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n const styleHeightMatch = styleAttrMatch?.match(\n /\\bheight\\s*:\\s*([^;]+)/i\n )?.[1];\n if (styleHeightMatch) {\n const styleHeightCss = parseViewportHeightCss(styleHeightMatch);\n if (styleHeightCss) return styleHeightCss;\n }\n const classAttrMatch = attrs.match(/\\bclass\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n if (!classAttrMatch) return null;\n return extractViewportHeightFromTailwindClass(classAttrMatch);\n }, [renderHtmlContent]);\n const hasRootVhHeight = Boolean(rootViewportHeightCss);\n useEffect(() => {\n if (mode !== \"blackboard\") {\n prevHtmlRef.current = htmlContent;\n return;\n }\n const prev = prevHtmlRef.current;\n const isContinuation = prev && htmlContent.startsWith(prev);\n if (!isContinuation && prev) {\n setResetToken((token) => token + 1);\n }\n prevHtmlRef.current = htmlContent;\n }, [htmlContent, mode]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe) return undefined;\n\n const doc = iframe.contentDocument;\n if (!doc) return undefined;\n\n doc.open();\n doc.write(`<!DOCTYPE html>\n<html${mode === \"blackboard\" ? ' style=\"height: 100%;\"' : \"\"}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <style>\n html, body, #root { width: 100%; height: 100%; }\n html, body { margin: 0; padding: 0; overflow: auto; }\n *, *::before, *::after { box-sizing: border-box; }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n </body>\n</html>`);\n doc.close();\n\n docRef.current = doc;\n\n const rootEl = doc.getElementById(\"root\");\n if (!rootEl) return undefined;\n\n const root = createRoot(rootEl);\n rootRef.current = root;\n let isDestroyed = false;\n\n const parseExplicitHeight = (\n value: string,\n parentViewportHeight: number\n ) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const numeric = Number.parseFloat(normalized);\n if (Number.isNaN(numeric)) return null;\n if (/(dvh|svh|lvh|vh)$/i.test(normalized)) {\n return (numeric / 100) * parentViewportHeight;\n }\n if (normalized.endsWith(\"px\") || /^[0-9.]+$/.test(normalized)) {\n return numeric;\n }\n return null;\n };\n const parseTailwindHeightClass = (\n className: string,\n parentViewportHeight: number\n ) => {\n if (!className.trim()) return null;\n const viewportHeightCss =\n extractViewportHeightFromTailwindClass(className);\n if (viewportHeightCss) {\n return parseExplicitHeight(viewportHeightCss, parentViewportHeight);\n }\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+px\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)px\\]$/i);\n if (!matched) return null;\n const numeric = Number.parseFloat(matched[1]);\n if (Number.isNaN(numeric)) return null;\n return numeric;\n };\n\n const resolveExplicitHeight = () => {\n if (!iframeRef.current || !doc.body) return null;\n const wrapper = doc.body.querySelector(\n \".sandbox-wrapper\"\n ) as HTMLElement | null;\n const container = wrapper?.firstElementChild as HTMLElement | null;\n if (!container) return null;\n const elements = Array.from(container.children) as HTMLElement[];\n if (elements.length !== 1) return null;\n const target = elements[0];\n const heightValue = target.style.height || target.getAttribute(\"height\");\n const parentViewportHeight =\n iframeRef.current.ownerDocument?.documentElement?.clientHeight ||\n window.innerHeight;\n const parsed = heightValue\n ? parseExplicitHeight(heightValue, parentViewportHeight)\n : null;\n if (parsed !== null) {\n return Math.ceil(parsed);\n }\n const classHeight = parseTailwindHeightClass(\n target.getAttribute(\"class\") || \"\",\n parentViewportHeight\n );\n return classHeight !== null ? Math.ceil(classHeight) : null;\n };\n\n const updateHeight = () => {\n if (!iframeRef.current || !doc.body) return;\n const bodyRect = doc.body.getBoundingClientRect();\n const htmlRect = doc.documentElement?.getBoundingClientRect();\n const bodyHeight = bodyRect.height;\n const htmlHeight = htmlRect?.height || 0;\n const contentHeight = Math.max(bodyHeight, htmlHeight);\n const explicitHeight = resolveExplicitHeight();\n const nextHeight = Math.max(\n 200,\n explicitHeight ?? Math.ceil(contentHeight)\n );\n setHeight(nextHeight);\n };\n const scheduleHeightUpdate = () => {\n requestAnimationFrame(() => {\n if (isDestroyed) return;\n updateHeight();\n });\n };\n updateHeightRef.current = scheduleHeightUpdate;\n\n updateHeight();\n scheduleHeightUpdate();\n\n // Inject Tailwind/DaisyUI/GSAP into iframe for all sandbox modes.\n // Dynamic import keeps ~3.3 MB of vendor libs out of the main bundle.\n // Tailwind's MutationObserver ensures styles apply even if content renders first.\n loadBlackboardVendor()\n .then((inject) => {\n if (isDestroyed) return;\n inject(doc);\n scheduleHeightUpdate();\n })\n .catch(() => {\n if (isDestroyed) return;\n scheduleHeightUpdate();\n });\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\n isDestroyed = true;\n resizeObserver.disconnect();\n // Defer unmount to avoid React warning when parent is mid-render\n setTimeout(() => {\n root.unmount();\n rootRef.current = null;\n docRef.current = null;\n updateHeightRef.current = () => {};\n }, 0);\n };\n }, []);\n\n useEffect(() => {\n const onFullscreenChange = () => {\n setIsFullscreen(Boolean(document.fullscreenElement));\n };\n document.addEventListener(\"fullscreenchange\", onFullscreenChange);\n return () =>\n document.removeEventListener(\"fullscreenchange\", onFullscreenChange);\n }, []);\n\n const toggleFullscreen = () => {\n const target = containerRef.current || iframeRef.current;\n if (!target) return;\n if (document.fullscreenElement) {\n document.exitFullscreen().catch(() => {});\n return;\n }\n if (target.requestFullscreen) {\n target.requestFullscreen().catch(() => {});\n }\n };\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n root.render(\n <SandboxApp\n html={renderHtmlContent}\n loadingText={loadingText}\n styleLoadingText={styleLoadingText}\n scriptLoadingText={scriptLoadingText}\n fullScreenButtonText={fullScreenButtonText}\n hideFullScreen={hideFullScreen}\n resetToken={resetToken}\n hasRootVhHeight={hasRootVhHeight}\n mode={mode}\n />\n );\n requestAnimationFrame(() => updateHeightRef.current?.());\n }, [\n renderHtmlContent,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n resetToken,\n mode,\n ]);\n const containerClassName = [\n \"w-full relative content-render-iframe-sandbox\",\n isBlackboardMode\n ? \"h-full overflow-auto flex flex-col\"\n : \"aspect-[16/9] overflow-hidden flex items-center justify-center\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n ref={containerRef}\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className={containerClassName}\n >\n {!hideFullScreen && (\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n className={\n \"absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer\"\n }\n >\n {isFullscreen ? \"退出全屏\" : fullScreenButtonText || \"全屏浏览\"}\n </button>\n )}\n {mode === \"blackboard\" && type === \"markdown\" ? (\n <ContentRender content={content} />\n ) : (\n <iframe\n ref={iframeRef}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n allow=\"fullscreen\"\n allowFullScreen\n className={[className, \"w-full h-full mx-auto my-auto block\"]\n .filter(Boolean)\n .join(\" \")}\n style={{\n height: \"100%\",\n margin: \"auto\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["loadBlackboardVendor","m","COMPLETE_IMAGE_TAG_PATTERN","POST_IMAGE_STREAM_DEBOUNCE_MS","normalizeTailwindHeightTokens","className","token","parseViewportHeightCss","value","normalized","matched","extractViewportHeightFromTailwindClass","normalizedTokens","arbitraryToken","IframeSandbox","content","type","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","isBlackboardMode","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","renderHtmlContent","setRenderHtmlContent","prevIncomingHtmlRef","pendingHtmlRef","deferRenderTimerRef","clearDeferredRenderTimer","useEffect","prevIncomingHtml","isAppendOnlyStream","containsCompleteImage","hasRootVhHeight","rootMatch","attrs","heightAttrMatch","explicitHeightCss","styleHeightMatch","styleHeightCss","classAttrMatch","prev","iframe","doc","rootEl","root","createRoot","isDestroyed","parseExplicitHeight","parentViewportHeight","numeric","parseTailwindHeightClass","viewportHeightCss","resolveExplicitHeight","container","elements","target","heightValue","parsed","classHeight","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","explicitHeight","nextHeight","scheduleHeightUpdate","inject","resizeObserver","onFullscreenChange","toggleFullscreen","jsx","SandboxApp","containerClassName","jsxs","ContentRender"],"mappings":"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"}
@@ -1,79 +1,99 @@
1
- import { j as H } from "../../_virtual/jsx-runtime.es.js";
2
- import I, { useRef as w, useState as N, useEffect as M } from "react";
3
- import { createRoot as Q } from "react-dom/client";
4
- import X from "./SandboxApp.es.js";
5
- import { splitContentSegments as tt } from "./utils/split-content.es.js";
6
- import et from "./ContentRender.es.js";
7
- const nt = () => import("./blackboard-vendor.es.js").then((n) => n.injectBlackboardLibraries), _ = (n) => n.split(/\s+/).filter(Boolean).map((r) => r.split(":").pop() || r), O = (n) => {
8
- const r = n.trim().toLowerCase();
1
+ import { j as R } from "../../_virtual/jsx-runtime.es.js";
2
+ import U, { useRef as h, useState as j, useEffect as x } from "react";
3
+ import { createRoot as ot } from "react-dom/client";
4
+ import st from "./SandboxApp.es.js";
5
+ import { splitContentSegments as it } from "./utils/split-content.es.js";
6
+ import lt from "./ContentRender.es.js";
7
+ const ct = () => import("./blackboard-vendor.es.js").then((i) => i.injectBlackboardLibraries), ut = /<img\b[^>]*>/i, at = 180, J = (i) => i.split(/\s+/).filter(Boolean).map((r) => r.split(":").pop() || r), Y = (i) => {
8
+ const r = i.trim().toLowerCase();
9
9
  if (!r) return null;
10
- const u = r.match(/^([0-9.]+)(vh|dvh|svh|lvh)$/i);
11
- return u ? `${u[1]}${u[2].toLowerCase()}` : null;
12
- }, W = (n) => {
13
- if (!n.trim()) return null;
14
- const r = _(n);
10
+ const d = r.match(/^([0-9.]+)(vh|dvh|svh|lvh)$/i);
11
+ return d ? `${d[1]}${d[2].toLowerCase()}` : null;
12
+ }, Z = (i) => {
13
+ if (!i.trim()) return null;
14
+ const r = J(i);
15
15
  if (r.includes("h-screen") || r.includes("h-dvh"))
16
16
  return "100dvh";
17
17
  if (r.includes("h-svh"))
18
18
  return "100svh";
19
19
  if (r.includes("h-lvh"))
20
20
  return "100lvh";
21
- const u = r.find(
22
- (C) => /^h-\[[0-9.]+(vh|dvh|svh|lvh)\]$/i.test(C)
21
+ const d = r.find(
22
+ (E) => /^h-\[[0-9.]+(vh|dvh|svh|lvh)\]$/i.test(E)
23
23
  );
24
- if (!u) return null;
25
- const g = u.match(/^h-\[([0-9.]+)(vh|dvh|svh|lvh)\]$/i);
26
- return g ? `${g[1]}${g[2].toLowerCase()}` : null;
27
- }, ut = ({
28
- content: n,
24
+ if (!d) return null;
25
+ const v = d.match(/^h-\[([0-9.]+)(vh|dvh|svh|lvh)\]$/i);
26
+ return v ? `${v[1]}${v[2].toLowerCase()}` : null;
27
+ }, vt = ({
28
+ content: i,
29
29
  type: r,
30
- className: u,
31
- loadingText: g,
32
- styleLoadingText: C,
33
- scriptLoadingText: T,
34
- fullScreenButtonText: j,
30
+ className: d,
31
+ loadingText: v,
32
+ styleLoadingText: E,
33
+ scriptLoadingText: S,
34
+ fullScreenButtonText: z,
35
35
  hideFullScreen: V = !1,
36
- mode: c = "content"
36
+ mode: a = "content"
37
37
  }) => {
38
- const q = w(null), v = w(null), z = w(null), L = w(null), F = w(() => {
39
- }), [, P] = N(480), [D, U] = N(0), [Y, Z] = N(!1), G = c === "blackboard", $ = w(""), h = I.useMemo(() => {
40
- const t = tt(n).filter((a) => a.type === "sandbox");
41
- return (c === "blackboard" ? t[t.length - 1]?.value || "" : t.map((a) => a.value).join(`
38
+ const O = h(null), w = h(null), F = h(null), L = h(null), $ = h(() => {
39
+ }), [, K] = j(480), [q, Q] = j(0), [X, tt] = j(!1), et = a === "blackboard", N = h(""), n = U.useMemo(() => {
40
+ const e = it(i).filter((l) => l.type === "sandbox");
41
+ return (a === "blackboard" ? e[e.length - 1]?.value || "" : e.map((l) => l.value).join(`
42
42
  `)) || "";
43
- }, [n, c]), S = !!I.useMemo(() => {
44
- const e = h.trim();
45
- if (!e) return null;
46
- const t = e.match(/^<([a-zA-Z][\w:-]*)(\s[^>]*?)?>/);
43
+ }, [i, a]), [k, P] = j(n), G = h(n), B = h(n), H = h(null), _ = () => {
44
+ H.current !== null && (window.clearTimeout(H.current), H.current = null);
45
+ };
46
+ x(
47
+ () => () => {
48
+ _();
49
+ },
50
+ []
51
+ ), x(() => {
52
+ const t = G.current;
53
+ G.current = n;
54
+ const e = !!t && n.length > t.length && n.startsWith(t), o = ut.test(n);
55
+ if (!(e && o)) {
56
+ _(), B.current = n, P(n);
57
+ return;
58
+ }
59
+ B.current = n, _(), H.current = window.setTimeout(() => {
60
+ P(B.current), H.current = null;
61
+ }, at);
62
+ }, [n]);
63
+ const W = !!U.useMemo(() => {
64
+ const t = k.trim();
47
65
  if (!t) return null;
48
- const s = t[2] || "", a = s.match(/\bheight\s*=\s*["']([^"']+)["']/i);
49
- if (a) {
50
- const m = O(a[1]);
51
- if (m) return m;
66
+ const e = t.match(/^<([a-zA-Z][\w:-]*)(\s[^>]*?)?>/);
67
+ if (!e) return null;
68
+ const o = e[2] || "", l = o.match(/\bheight\s*=\s*["']([^"']+)["']/i);
69
+ if (l) {
70
+ const f = Y(l[1]);
71
+ if (f) return f;
52
72
  }
53
- const y = s.match(/\bstyle\s*=\s*["']([^"']+)["']/i)?.[1]?.match(
73
+ const C = o.match(/\bstyle\s*=\s*["']([^"']+)["']/i)?.[1]?.match(
54
74
  /\bheight\s*:\s*([^;]+)/i
55
75
  )?.[1];
56
- if (y) {
57
- const m = O(y);
58
- if (m) return m;
76
+ if (C) {
77
+ const f = Y(C);
78
+ if (f) return f;
59
79
  }
60
- const k = s.match(/\bclass\s*=\s*["']([^"']+)["']/i)?.[1];
61
- return k ? W(k) : null;
62
- }, [h]);
63
- M(() => {
64
- if (c !== "blackboard") {
65
- $.current = h;
80
+ const M = o.match(/\bclass\s*=\s*["']([^"']+)["']/i)?.[1];
81
+ return M ? Z(M) : null;
82
+ }, [k]);
83
+ x(() => {
84
+ if (a !== "blackboard") {
85
+ N.current = n;
66
86
  return;
67
87
  }
68
- const e = $.current;
69
- !(e && h.startsWith(e)) && e && U((s) => s + 1), $.current = h;
70
- }, [h, c]), M(() => {
71
- const e = v.current;
72
- if (!e) return;
73
- const t = e.contentDocument;
88
+ const t = N.current;
89
+ !(t && n.startsWith(t)) && t && Q((o) => o + 1), N.current = n;
90
+ }, [n, a]), x(() => {
91
+ const t = w.current;
74
92
  if (!t) return;
75
- t.open(), t.write(`<!DOCTYPE html>
76
- <html${c === "blackboard" ? ' style="height: 100%;"' : ""}>
93
+ const e = t.contentDocument;
94
+ if (!e) return;
95
+ e.open(), e.write(`<!DOCTYPE html>
96
+ <html${a === "blackboard" ? ' style="height: 100%;"' : ""}>
77
97
  <head>
78
98
  <meta charset="utf-8" />
79
99
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
@@ -86,144 +106,143 @@ const nt = () => import("./blackboard-vendor.es.js").then((n) => n.injectBlackbo
86
106
  <body>
87
107
  <div id="root"></div>
88
108
  </body>
89
- </html>`), t.close(), L.current = t;
90
- const s = t.getElementById("root");
91
- if (!s) return;
92
- const a = Q(s);
93
- z.current = a;
94
- let x = !1;
95
- const y = (i, d) => {
96
- const o = i.trim().toLowerCase();
97
- if (!o) return null;
98
- const l = Number.parseFloat(o);
99
- return Number.isNaN(l) ? null : /(dvh|svh|lvh|vh)$/i.test(o) ? l / 100 * d : o.endsWith("px") || /^[0-9.]+$/.test(o) ? l : null;
100
- }, k = (i, d) => {
101
- if (!i.trim()) return null;
102
- const o = W(i);
103
- if (o)
104
- return y(o, d);
105
- const f = _(i).find(
106
- (E) => /^h-\[[0-9.]+px\]$/i.test(E)
109
+ </html>`), e.close(), L.current = e;
110
+ const o = e.getElementById("root");
111
+ if (!o) return;
112
+ const l = ot(o);
113
+ F.current = l;
114
+ let y = !1;
115
+ const C = (c, m) => {
116
+ const s = c.trim().toLowerCase();
117
+ if (!s) return null;
118
+ const u = Number.parseFloat(s);
119
+ return Number.isNaN(u) ? null : /(dvh|svh|lvh|vh)$/i.test(s) ? u / 100 * m : s.endsWith("px") || /^[0-9.]+$/.test(s) ? u : null;
120
+ }, M = (c, m) => {
121
+ if (!c.trim()) return null;
122
+ const s = Z(c);
123
+ if (s)
124
+ return C(s, m);
125
+ const p = J(c).find(
126
+ (A) => /^h-\[[0-9.]+px\]$/i.test(A)
107
127
  );
108
- if (!f) return null;
109
- const p = f.match(/^h-\[([0-9.]+)px\]$/i);
110
128
  if (!p) return null;
111
- const b = Number.parseFloat(p[1]);
129
+ const g = p.match(/^h-\[([0-9.]+)px\]$/i);
130
+ if (!g) return null;
131
+ const b = Number.parseFloat(g[1]);
112
132
  return Number.isNaN(b) ? null : b;
113
- }, m = () => {
114
- if (!v.current || !t.body) return null;
115
- const d = t.body.querySelector(
133
+ }, f = () => {
134
+ if (!w.current || !e.body) return null;
135
+ const m = e.body.querySelector(
116
136
  ".sandbox-wrapper"
117
137
  )?.firstElementChild;
118
- if (!d) return null;
119
- const o = Array.from(d.children);
120
- if (o.length !== 1) return null;
121
- const l = o[0], f = l.style.height || l.getAttribute("height"), p = v.current.ownerDocument?.documentElement?.clientHeight || window.innerHeight, b = f ? y(f, p) : null;
138
+ if (!m) return null;
139
+ const s = Array.from(m.children);
140
+ if (s.length !== 1) return null;
141
+ const u = s[0], p = u.style.height || u.getAttribute("height"), g = w.current.ownerDocument?.documentElement?.clientHeight || window.innerHeight, b = p ? C(p, g) : null;
122
142
  if (b !== null)
123
143
  return Math.ceil(b);
124
- const E = k(
125
- l.getAttribute("class") || "",
126
- p
144
+ const A = M(
145
+ u.getAttribute("class") || "",
146
+ g
127
147
  );
128
- return E !== null ? Math.ceil(E) : null;
129
- }, A = () => {
130
- if (!v.current || !t.body) return;
131
- const i = t.body.getBoundingClientRect(), d = t.documentElement?.getBoundingClientRect(), o = i.height, l = d?.height || 0, f = Math.max(o, l), p = m(), b = Math.max(
148
+ return A !== null ? Math.ceil(A) : null;
149
+ }, D = () => {
150
+ if (!w.current || !e.body) return;
151
+ const c = e.body.getBoundingClientRect(), m = e.documentElement?.getBoundingClientRect(), s = c.height, u = m?.height || 0, p = Math.max(s, u), g = f(), b = Math.max(
132
152
  200,
133
- p ?? Math.ceil(f)
153
+ g ?? Math.ceil(p)
134
154
  );
135
- P(b);
136
- }, R = () => {
155
+ K(b);
156
+ }, T = () => {
137
157
  requestAnimationFrame(() => {
138
- x || A();
158
+ y || D();
139
159
  });
140
160
  };
141
- F.current = R, A(), R(), nt().then((i) => {
142
- x || (i(t), R());
161
+ $.current = T, D(), T(), ct().then((c) => {
162
+ y || (c(e), T());
143
163
  }).catch(() => {
144
- x || R();
164
+ y || T();
145
165
  });
146
- const B = new ResizeObserver(() => A());
147
- return B.observe(t.body), s && B.observe(s), () => {
148
- x = !0, B.disconnect(), setTimeout(() => {
149
- a.unmount(), z.current = null, L.current = null, F.current = () => {
166
+ const I = new ResizeObserver(() => D());
167
+ return I.observe(e.body), o && I.observe(o), () => {
168
+ y = !0, I.disconnect(), setTimeout(() => {
169
+ l.unmount(), F.current = null, L.current = null, $.current = () => {
150
170
  };
151
171
  }, 0);
152
172
  };
153
- }, []), M(() => {
154
- const e = () => {
155
- Z(!!document.fullscreenElement);
173
+ }, []), x(() => {
174
+ const t = () => {
175
+ tt(!!document.fullscreenElement);
156
176
  };
157
- return document.addEventListener("fullscreenchange", e), () => document.removeEventListener("fullscreenchange", e);
177
+ return document.addEventListener("fullscreenchange", t), () => document.removeEventListener("fullscreenchange", t);
158
178
  }, []);
159
- const J = () => {
160
- const e = q.current || v.current;
161
- if (e) {
179
+ const nt = () => {
180
+ const t = O.current || w.current;
181
+ if (t) {
162
182
  if (document.fullscreenElement) {
163
183
  document.exitFullscreen().catch(() => {
164
184
  });
165
185
  return;
166
186
  }
167
- e.requestFullscreen && e.requestFullscreen().catch(() => {
187
+ t.requestFullscreen && t.requestFullscreen().catch(() => {
168
188
  });
169
189
  }
170
190
  };
171
- M(() => {
172
- const e = z.current;
173
- e && (e.render(
174
- /* @__PURE__ */ H.jsx(
175
- X,
191
+ x(() => {
192
+ const t = F.current;
193
+ t && (t.render(
194
+ /* @__PURE__ */ R.jsx(
195
+ st,
176
196
  {
177
- html: h,
178
- loadingText: g,
179
- styleLoadingText: C,
180
- scriptLoadingText: T,
181
- fullScreenButtonText: j,
197
+ html: k,
198
+ loadingText: v,
199
+ styleLoadingText: E,
200
+ scriptLoadingText: S,
201
+ fullScreenButtonText: z,
182
202
  hideFullScreen: V,
183
- resetToken: D,
184
- hasRootVhHeight: S,
185
- mode: c
203
+ resetToken: q,
204
+ hasRootVhHeight: W,
205
+ mode: a
186
206
  }
187
207
  )
188
- ), requestAnimationFrame(() => F.current?.()));
208
+ ), requestAnimationFrame(() => $.current?.()));
189
209
  }, [
190
- n,
191
- h,
192
- g,
193
- C,
194
- T,
195
- j,
196
- D,
197
- c
210
+ k,
211
+ v,
212
+ E,
213
+ S,
214
+ z,
215
+ q,
216
+ a
198
217
  ]);
199
- const K = [
218
+ const rt = [
200
219
  "w-full relative content-render-iframe-sandbox",
201
- G ? "h-full overflow-auto flex flex-col" : "aspect-[16/9] overflow-hidden flex items-center justify-center"
220
+ et ? "h-full overflow-auto flex flex-col" : "aspect-[16/9] overflow-hidden flex items-center justify-center"
202
221
  ].filter(Boolean).join(" ");
203
- return /* @__PURE__ */ H.jsxs(
222
+ return /* @__PURE__ */ R.jsxs(
204
223
  "div",
205
224
  {
206
- ref: q,
207
- "data-root-vh": S ? "true" : "false",
208
- className: K,
225
+ ref: O,
226
+ "data-root-vh": W ? "true" : "false",
227
+ className: rt,
209
228
  children: [
210
- !V && /* @__PURE__ */ H.jsx(
229
+ !V && /* @__PURE__ */ R.jsx(
211
230
  "button",
212
231
  {
213
232
  type: "button",
214
- onClick: J,
233
+ onClick: nt,
215
234
  className: "absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer",
216
- children: Y ? "退出全屏" : j || "全屏浏览"
235
+ children: X ? "退出全屏" : z || "全屏浏览"
217
236
  }
218
237
  ),
219
- c === "blackboard" && r === "markdown" ? /* @__PURE__ */ H.jsx(et, { content: n }) : /* @__PURE__ */ H.jsx(
238
+ a === "blackboard" && r === "markdown" ? /* @__PURE__ */ R.jsx(lt, { content: i }) : /* @__PURE__ */ R.jsx(
220
239
  "iframe",
221
240
  {
222
- ref: v,
241
+ ref: w,
223
242
  sandbox: "allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox",
224
243
  allow: "fullscreen",
225
244
  allowFullScreen: !0,
226
- className: [u, "w-full h-full mx-auto my-auto block"].filter(Boolean).join(" "),
245
+ className: [d, "w-full h-full mx-auto my-auto block"].filter(Boolean).join(" "),
227
246
  style: {
228
247
  height: "100%",
229
248
  margin: "auto"
@@ -235,6 +254,6 @@ const nt = () => import("./blackboard-vendor.es.js").then((n) => n.injectBlackbo
235
254
  );
236
255
  };
237
256
  export {
238
- ut as default
257
+ vt as default
239
258
  };
240
259
  //# sourceMappingURL=IframeSandbox.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"IframeSandbox.es.js","sources":["../../../src/components/ContentRender/IframeSandbox.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport SandboxApp from \"./SandboxApp\";\nimport { splitContentSegments } from \"./utils/split-content\";\nimport ContentRender from \"./ContentRender\";\n// Lazy-load iframe vendor libraries and inject them into the sandbox document.\nconst loadBlackboardVendor = () =>\n import(\"./blackboard-vendor\").then((m) => m.injectBlackboardLibraries);\nexport interface IframeSandboxProps {\n content: string;\n className?: string;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n fullScreenButtonText?: string;\n hideFullScreen?: boolean;\n mode?: \"content\" | \"blackboard\";\n type: \"sandbox\" | \"markdown\";\n}\n\nconst normalizeTailwindHeightTokens = (className: string) =>\n className\n .split(/\\s+/)\n .filter(Boolean)\n .map((token) => token.split(\":\").pop() || token);\n\nconst parseViewportHeightCss = (value: string) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const matched = normalized.match(/^([0-9.]+)(vh|dvh|svh|lvh)$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst extractViewportHeightFromTailwindClass = (className: string) => {\n if (!className.trim()) return null;\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n if (\n normalizedTokens.includes(\"h-screen\") ||\n normalizedTokens.includes(\"h-dvh\")\n ) {\n return \"100dvh\";\n }\n if (normalizedTokens.includes(\"h-svh\")) {\n return \"100svh\";\n }\n if (normalizedTokens.includes(\"h-lvh\")) {\n return \"100lvh\";\n }\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+(vh|dvh|svh|lvh)\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)(vh|dvh|svh|lvh)\\]$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst IframeSandbox: React.FC<IframeSandboxProps> = ({\n content,\n type,\n className,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n hideFullScreen = false,\n mode = \"content\",\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const rootRef = useRef<Root | null>(null);\n const docRef = useRef<Document | null>(null);\n const updateHeightRef = useRef<() => void>(() => {});\n const [, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const isBlackboardMode = mode === \"blackboard\";\n const prevHtmlRef = useRef<string>(\"\");\n const htmlContent = React.useMemo(() => {\n const segments = splitContentSegments(content);\n // console.log('segments=====', segments);\n const sandboxSegments = segments.filter((seg) => seg.type === \"sandbox\");\n const sandboxContent =\n mode === \"blackboard\"\n ? sandboxSegments[sandboxSegments.length - 1]?.value || \"\"\n : sandboxSegments.map((seg) => seg.value).join(\"\\n\");\n return sandboxContent || \"\";\n }, [content, mode]);\n const rootViewportHeightCss = React.useMemo(() => {\n const normalized = htmlContent.trim();\n if (!normalized) return null;\n const rootMatch = normalized.match(/^<([a-zA-Z][\\w:-]*)(\\s[^>]*?)?>/);\n if (!rootMatch) return null;\n const attrs = rootMatch[2] || \"\";\n const heightAttrMatch = attrs.match(/\\bheight\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (heightAttrMatch) {\n const explicitHeightCss = parseViewportHeightCss(heightAttrMatch[1]);\n if (explicitHeightCss) return explicitHeightCss;\n }\n const styleAttrMatch = attrs.match(/\\bstyle\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n const styleHeightMatch = styleAttrMatch?.match(\n /\\bheight\\s*:\\s*([^;]+)/i\n )?.[1];\n if (styleHeightMatch) {\n const styleHeightCss = parseViewportHeightCss(styleHeightMatch);\n if (styleHeightCss) return styleHeightCss;\n }\n const classAttrMatch = attrs.match(/\\bclass\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n if (!classAttrMatch) return null;\n return extractViewportHeightFromTailwindClass(classAttrMatch);\n }, [htmlContent]);\n const hasRootVhHeight = Boolean(rootViewportHeightCss);\n useEffect(() => {\n if (mode !== \"blackboard\") {\n prevHtmlRef.current = htmlContent;\n return;\n }\n const prev = prevHtmlRef.current;\n const isContinuation = prev && htmlContent.startsWith(prev);\n if (!isContinuation && prev) {\n setResetToken((token) => token + 1);\n }\n prevHtmlRef.current = htmlContent;\n }, [htmlContent, mode]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe) return undefined;\n\n const doc = iframe.contentDocument;\n if (!doc) return undefined;\n\n doc.open();\n doc.write(`<!DOCTYPE html>\n<html${mode === \"blackboard\" ? ' style=\"height: 100%;\"' : \"\"}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <style>\n html, body, #root { width: 100%; height: 100%; }\n html, body { margin: 0; padding: 0; overflow: auto; }\n *, *::before, *::after { box-sizing: border-box; }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n </body>\n</html>`);\n doc.close();\n\n docRef.current = doc;\n\n const rootEl = doc.getElementById(\"root\");\n if (!rootEl) return undefined;\n\n const root = createRoot(rootEl);\n rootRef.current = root;\n let isDestroyed = false;\n\n const parseExplicitHeight = (\n value: string,\n parentViewportHeight: number\n ) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const numeric = Number.parseFloat(normalized);\n if (Number.isNaN(numeric)) return null;\n if (/(dvh|svh|lvh|vh)$/i.test(normalized)) {\n return (numeric / 100) * parentViewportHeight;\n }\n if (normalized.endsWith(\"px\") || /^[0-9.]+$/.test(normalized)) {\n return numeric;\n }\n return null;\n };\n const parseTailwindHeightClass = (\n className: string,\n parentViewportHeight: number\n ) => {\n if (!className.trim()) return null;\n const viewportHeightCss =\n extractViewportHeightFromTailwindClass(className);\n if (viewportHeightCss) {\n return parseExplicitHeight(viewportHeightCss, parentViewportHeight);\n }\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+px\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)px\\]$/i);\n if (!matched) return null;\n const numeric = Number.parseFloat(matched[1]);\n if (Number.isNaN(numeric)) return null;\n return numeric;\n };\n\n const resolveExplicitHeight = () => {\n if (!iframeRef.current || !doc.body) return null;\n const wrapper = doc.body.querySelector(\n \".sandbox-wrapper\"\n ) as HTMLElement | null;\n const container = wrapper?.firstElementChild as HTMLElement | null;\n if (!container) return null;\n const elements = Array.from(container.children) as HTMLElement[];\n if (elements.length !== 1) return null;\n const target = elements[0];\n const heightValue = target.style.height || target.getAttribute(\"height\");\n const parentViewportHeight =\n iframeRef.current.ownerDocument?.documentElement?.clientHeight ||\n window.innerHeight;\n const parsed = heightValue\n ? parseExplicitHeight(heightValue, parentViewportHeight)\n : null;\n if (parsed !== null) {\n return Math.ceil(parsed);\n }\n const classHeight = parseTailwindHeightClass(\n target.getAttribute(\"class\") || \"\",\n parentViewportHeight\n );\n return classHeight !== null ? Math.ceil(classHeight) : null;\n };\n\n const updateHeight = () => {\n if (!iframeRef.current || !doc.body) return;\n const bodyRect = doc.body.getBoundingClientRect();\n const htmlRect = doc.documentElement?.getBoundingClientRect();\n const bodyHeight = bodyRect.height;\n const htmlHeight = htmlRect?.height || 0;\n const contentHeight = Math.max(bodyHeight, htmlHeight);\n const explicitHeight = resolveExplicitHeight();\n const nextHeight = Math.max(\n 200,\n explicitHeight ?? Math.ceil(contentHeight)\n );\n setHeight(nextHeight);\n };\n const scheduleHeightUpdate = () => {\n requestAnimationFrame(() => {\n if (isDestroyed) return;\n updateHeight();\n });\n };\n updateHeightRef.current = scheduleHeightUpdate;\n\n updateHeight();\n scheduleHeightUpdate();\n\n // Inject Tailwind/DaisyUI/GSAP into iframe for all sandbox modes.\n // Dynamic import keeps ~3.3 MB of vendor libs out of the main bundle.\n // Tailwind's MutationObserver ensures styles apply even if content renders first.\n loadBlackboardVendor()\n .then((inject) => {\n if (isDestroyed) return;\n inject(doc);\n scheduleHeightUpdate();\n })\n .catch(() => {\n if (isDestroyed) return;\n scheduleHeightUpdate();\n });\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\n isDestroyed = true;\n resizeObserver.disconnect();\n // Defer unmount to avoid React warning when parent is mid-render\n setTimeout(() => {\n root.unmount();\n rootRef.current = null;\n docRef.current = null;\n updateHeightRef.current = () => {};\n }, 0);\n };\n }, []);\n\n useEffect(() => {\n const onFullscreenChange = () => {\n setIsFullscreen(Boolean(document.fullscreenElement));\n };\n document.addEventListener(\"fullscreenchange\", onFullscreenChange);\n return () =>\n document.removeEventListener(\"fullscreenchange\", onFullscreenChange);\n }, []);\n\n const toggleFullscreen = () => {\n const target = containerRef.current || iframeRef.current;\n if (!target) return;\n if (document.fullscreenElement) {\n document.exitFullscreen().catch(() => {});\n return;\n }\n if (target.requestFullscreen) {\n target.requestFullscreen().catch(() => {});\n }\n };\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n root.render(\n <SandboxApp\n html={htmlContent}\n loadingText={loadingText}\n styleLoadingText={styleLoadingText}\n scriptLoadingText={scriptLoadingText}\n fullScreenButtonText={fullScreenButtonText}\n hideFullScreen={hideFullScreen}\n resetToken={resetToken}\n hasRootVhHeight={hasRootVhHeight}\n mode={mode}\n />\n );\n requestAnimationFrame(() => updateHeightRef.current?.());\n }, [\n content,\n htmlContent,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n resetToken,\n mode,\n ]);\n const containerClassName = [\n \"w-full relative content-render-iframe-sandbox\",\n isBlackboardMode\n ? \"h-full overflow-auto flex flex-col\"\n : \"aspect-[16/9] overflow-hidden flex items-center justify-center\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n ref={containerRef}\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className={containerClassName}\n >\n {!hideFullScreen && (\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n className={\n \"absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer\"\n }\n >\n {isFullscreen ? \"退出全屏\" : fullScreenButtonText || \"全屏浏览\"}\n </button>\n )}\n {mode === \"blackboard\" && type === \"markdown\" ? (\n <ContentRender content={content} />\n ) : (\n <iframe\n ref={iframeRef}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n allow=\"fullscreen\"\n allowFullScreen\n className={[className, \"w-full h-full mx-auto my-auto block\"]\n .filter(Boolean)\n .join(\" \")}\n style={{\n height: \"100%\",\n margin: \"auto\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["loadBlackboardVendor","m","normalizeTailwindHeightTokens","className","token","parseViewportHeightCss","value","normalized","matched","extractViewportHeightFromTailwindClass","normalizedTokens","arbitraryToken","IframeSandbox","content","type","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","isBlackboardMode","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","hasRootVhHeight","rootMatch","attrs","heightAttrMatch","explicitHeightCss","styleHeightMatch","styleHeightCss","classAttrMatch","useEffect","prev","iframe","doc","rootEl","root","createRoot","isDestroyed","parseExplicitHeight","parentViewportHeight","numeric","parseTailwindHeightClass","viewportHeightCss","resolveExplicitHeight","container","elements","target","heightValue","parsed","classHeight","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","explicitHeight","nextHeight","scheduleHeightUpdate","inject","resizeObserver","onFullscreenChange","toggleFullscreen","jsx","SandboxApp","containerClassName","jsxs","ContentRender"],"mappings":";;;;;;AAMA,MAAMA,KAAuB,MAC3B,OAAO,2BAAqB,EAAE,KAAK,CAACC,MAAMA,EAAE,yBAAyB,GAajEC,IAAgC,CAACC,MACrCA,EACG,MAAM,KAAK,EACX,OAAO,OAAO,EACd,IAAI,CAACC,MAAUA,EAAM,MAAM,GAAG,EAAE,IAAA,KAASA,CAAK,GAE7CC,IAAyB,CAACC,MAAkB;AAChD,QAAMC,IAAaD,EAAM,KAAA,EAAO,YAAA;AAChC,MAAI,CAACC,EAAY,QAAO;AACxB,QAAMC,IAAUD,EAAW,MAAM,8BAA8B;AAC/D,SAAKC,IACE,GAAGA,EAAQ,CAAC,CAAC,GAAGA,EAAQ,CAAC,EAAE,YAAA,CAAa,KAD1B;AAEvB,GAEMC,IAAyC,CAACN,MAAsB;AACpE,MAAI,CAACA,EAAU,KAAA,EAAQ,QAAO;AAC9B,QAAMO,IAAmBR,EAA8BC,CAAS;AAChE,MACEO,EAAiB,SAAS,UAAU,KACpCA,EAAiB,SAAS,OAAO;AAEjC,WAAO;AAET,MAAIA,EAAiB,SAAS,OAAO;AACnC,WAAO;AAET,MAAIA,EAAiB,SAAS,OAAO;AACnC,WAAO;AAET,QAAMC,IAAiBD,EAAiB;AAAA,IAAK,CAACN,MAC5C,mCAAmC,KAAKA,CAAK;AAAA,EAAA;AAE/C,MAAI,CAACO,EAAgB,QAAO;AAC5B,QAAMH,IAAUG,EAAe,MAAM,oCAAoC;AACzE,SAAKH,IACE,GAAGA,EAAQ,CAAC,CAAC,GAAGA,EAAQ,CAAC,EAAE,YAAA,CAAa,KAD1B;AAEvB,GAEMI,KAA8C,CAAC;AAAA,EACnD,SAAAC;AAAA,EACA,MAAAC;AAAA,EACA,WAAAX;AAAA,EACA,aAAAY;AAAA,EACA,kBAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,sBAAAC;AAAA,EACA,gBAAAC,IAAiB;AAAA,EACjB,MAAAC,IAAO;AACT,MAAM;AACJ,QAAMC,IAAeC,EAAuB,IAAI,GAC1CC,IAAYD,EAA0B,IAAI,GAC1CE,IAAUF,EAAoB,IAAI,GAClCG,IAASH,EAAwB,IAAI,GACrCI,IAAkBJ,EAAmB,MAAM;AAAA,EAAC,CAAC,GAC7C,GAAGK,CAAS,IAAIC,EAAS,GAAG,GAC5B,CAACC,GAAYC,CAAa,IAAIF,EAAS,CAAC,GACxC,CAACG,GAAcC,CAAe,IAAIJ,EAAS,EAAK,GAChDK,IAAmBb,MAAS,cAC5Bc,IAAcZ,EAAe,EAAE,GAC/Ba,IAAcC,EAAM,QAAQ,MAAM;AAGtC,UAAMC,IAFWC,GAAqBzB,CAAO,EAEZ,OAAO,CAAC0B,MAAQA,EAAI,SAAS,SAAS;AAKvE,YAHEnB,MAAS,eACLiB,EAAgBA,EAAgB,SAAS,CAAC,GAAG,SAAS,KACtDA,EAAgB,IAAI,CAACE,MAAQA,EAAI,KAAK,EAAE,KAAK;AAAA,CAAI,MAC9B;AAAA,EAC3B,GAAG,CAAC1B,GAASO,CAAI,CAAC,GAwBZoB,IAAkB,EAvBMJ,EAAM,QAAQ,MAAM;AAChD,UAAM7B,IAAa4B,EAAY,KAAA;AAC/B,QAAI,CAAC5B,EAAY,QAAO;AACxB,UAAMkC,IAAYlC,EAAW,MAAM,iCAAiC;AACpE,QAAI,CAACkC,EAAW,QAAO;AACvB,UAAMC,IAAQD,EAAU,CAAC,KAAK,IACxBE,IAAkBD,EAAM,MAAM,kCAAkC;AACtE,QAAIC,GAAiB;AACnB,YAAMC,IAAoBvC,EAAuBsC,EAAgB,CAAC,CAAC;AACnE,UAAIC,EAAmB,QAAOA;AAAA,IAChC;AAEA,UAAMC,IADiBH,EAAM,MAAM,iCAAiC,IAAI,CAAC,GAChC;AAAA,MACvC;AAAA,IAAA,IACE,CAAC;AACL,QAAIG,GAAkB;AACpB,YAAMC,IAAiBzC,EAAuBwC,CAAgB;AAC9D,UAAIC,EAAgB,QAAOA;AAAA,IAC7B;AACA,UAAMC,IAAiBL,EAAM,MAAM,iCAAiC,IAAI,CAAC;AACzE,WAAKK,IACEtC,EAAuCsC,CAAc,IADhC;AAAA,EAE9B,GAAG,CAACZ,CAAW,CAAC;AAEhB,EAAAa,EAAU,MAAM;AACd,QAAI5B,MAAS,cAAc;AACzB,MAAAc,EAAY,UAAUC;AACtB;AAAA,IACF;AACA,UAAMc,IAAOf,EAAY;AAEzB,IAAI,EADmBe,KAAQd,EAAY,WAAWc,CAAI,MACnCA,KACrBnB,EAAc,CAAC1B,MAAUA,IAAQ,CAAC,GAEpC8B,EAAY,UAAUC;AAAA,EACxB,GAAG,CAACA,GAAaf,CAAI,CAAC,GAEtB4B,EAAU,MAAM;AACd,UAAME,IAAS3B,EAAU;AACzB,QAAI,CAAC2B,EAAQ;AAEb,UAAMC,IAAMD,EAAO;AACnB,QAAI,CAACC,EAAK;AAEV,IAAAA,EAAI,KAAA,GACJA,EAAI,MAAM;AAAA,OACP/B,MAAS,eAAe,2BAA2B,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAapD,GACJ+B,EAAI,MAAA,GAEJ1B,EAAO,UAAU0B;AAEjB,UAAMC,IAASD,EAAI,eAAe,MAAM;AACxC,QAAI,CAACC,EAAQ;AAEb,UAAMC,IAAOC,EAAWF,CAAM;AAC9B,IAAA5B,EAAQ,UAAU6B;AAClB,QAAIE,IAAc;AAElB,UAAMC,IAAsB,CAC1BlD,GACAmD,MACG;AACH,YAAMlD,IAAaD,EAAM,KAAA,EAAO,YAAA;AAChC,UAAI,CAACC,EAAY,QAAO;AACxB,YAAMmD,IAAU,OAAO,WAAWnD,CAAU;AAC5C,aAAI,OAAO,MAAMmD,CAAO,IAAU,OAC9B,qBAAqB,KAAKnD,CAAU,IAC9BmD,IAAU,MAAOD,IAEvBlD,EAAW,SAAS,IAAI,KAAK,YAAY,KAAKA,CAAU,IACnDmD,IAEF;AAAA,IACT,GACMC,IAA2B,CAC/BxD,GACAsD,MACG;AACH,UAAI,CAACtD,EAAU,KAAA,EAAQ,QAAO;AAC9B,YAAMyD,IACJnD,EAAuCN,CAAS;AAClD,UAAIyD;AACF,eAAOJ,EAAoBI,GAAmBH,CAAoB;AAGpE,YAAM9C,IADmBT,EAA8BC,CAAS,EACxB;AAAA,QAAK,CAACC,MAC5C,qBAAqB,KAAKA,CAAK;AAAA,MAAA;AAEjC,UAAI,CAACO,EAAgB,QAAO;AAC5B,YAAMH,IAAUG,EAAe,MAAM,sBAAsB;AAC3D,UAAI,CAACH,EAAS,QAAO;AACrB,YAAMkD,IAAU,OAAO,WAAWlD,EAAQ,CAAC,CAAC;AAC5C,aAAI,OAAO,MAAMkD,CAAO,IAAU,OAC3BA;AAAA,IACT,GAEMG,IAAwB,MAAM;AAClC,UAAI,CAACtC,EAAU,WAAW,CAAC4B,EAAI,KAAM,QAAO;AAI5C,YAAMW,IAHUX,EAAI,KAAK;AAAA,QACvB;AAAA,MAAA,GAEyB;AAC3B,UAAI,CAACW,EAAW,QAAO;AACvB,YAAMC,IAAW,MAAM,KAAKD,EAAU,QAAQ;AAC9C,UAAIC,EAAS,WAAW,EAAG,QAAO;AAClC,YAAMC,IAASD,EAAS,CAAC,GACnBE,IAAcD,EAAO,MAAM,UAAUA,EAAO,aAAa,QAAQ,GACjEP,IACJlC,EAAU,QAAQ,eAAe,iBAAiB,gBAClD,OAAO,aACH2C,IAASD,IACXT,EAAoBS,GAAaR,CAAoB,IACrD;AACJ,UAAIS,MAAW;AACb,eAAO,KAAK,KAAKA,CAAM;AAEzB,YAAMC,IAAcR;AAAA,QAClBK,EAAO,aAAa,OAAO,KAAK;AAAA,QAChCP;AAAA,MAAA;AAEF,aAAOU,MAAgB,OAAO,KAAK,KAAKA,CAAW,IAAI;AAAA,IACzD,GAEMC,IAAe,MAAM;AACzB,UAAI,CAAC7C,EAAU,WAAW,CAAC4B,EAAI,KAAM;AACrC,YAAMkB,IAAWlB,EAAI,KAAK,sBAAA,GACpBmB,IAAWnB,EAAI,iBAAiB,sBAAA,GAChCoB,IAAaF,EAAS,QACtBG,IAAaF,GAAU,UAAU,GACjCG,IAAgB,KAAK,IAAIF,GAAYC,CAAU,GAC/CE,IAAiBb,EAAA,GACjBc,IAAa,KAAK;AAAA,QACtB;AAAA,QACAD,KAAkB,KAAK,KAAKD,CAAa;AAAA,MAAA;AAE3C,MAAA9C,EAAUgD,CAAU;AAAA,IACtB,GACMC,IAAuB,MAAM;AACjC,4BAAsB,MAAM;AAC1B,QAAIrB,KACJa,EAAA;AAAA,MACF,CAAC;AAAA,IACH;AACA,IAAA1C,EAAgB,UAAUkD,GAE1BR,EAAA,GACAQ,EAAA,GAKA5E,GAAA,EACG,KAAK,CAAC6E,MAAW;AAChB,MAAItB,MACJsB,EAAO1B,CAAG,GACVyB,EAAA;AAAA,IACF,CAAC,EACA,MAAM,MAAM;AACX,MAAIrB,KACJqB,EAAA;AAAA,IACF,CAAC;AAEH,UAAME,IAAiB,IAAI,eAAe,MAAMV,GAAc;AAC9D,WAAAU,EAAe,QAAQ3B,EAAI,IAAI,GAC3BC,KACF0B,EAAe,QAAQ1B,CAAM,GAGxB,MAAM;AACX,MAAAG,IAAc,IACduB,EAAe,WAAA,GAEf,WAAW,MAAM;AACf,QAAAzB,EAAK,QAAA,GACL7B,EAAQ,UAAU,MAClBC,EAAO,UAAU,MACjBC,EAAgB,UAAU,MAAM;AAAA,QAAC;AAAA,MACnC,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE,GAELsB,EAAU,MAAM;AACd,UAAM+B,IAAqB,MAAM;AAC/B,MAAA/C,EAAgB,EAAQ,SAAS,iBAAkB;AAAA,IACrD;AACA,oBAAS,iBAAiB,oBAAoB+C,CAAkB,GACzD,MACL,SAAS,oBAAoB,oBAAoBA,CAAkB;AAAA,EACvE,GAAG,CAAA,CAAE;AAEL,QAAMC,IAAmB,MAAM;AAC7B,UAAMhB,IAAS3C,EAAa,WAAWE,EAAU;AACjD,QAAKyC,GACL;AAAA,UAAI,SAAS,mBAAmB;AAC9B,iBAAS,iBAAiB,MAAM,MAAM;AAAA,QAAC,CAAC;AACxC;AAAA,MACF;AACA,MAAIA,EAAO,qBACTA,EAAO,oBAAoB,MAAM,MAAM;AAAA,MAAC,CAAC;AAAA;AAAA,EAE7C;AAEA,EAAAhB,EAAU,MAAM;AACd,UAAMK,IAAO7B,EAAQ;AACrB,IAAK6B,MAELA,EAAK;AAAA,MACH4B,gBAAAA,EAAAA;AAAAA,QAACC;AAAA,QAAA;AAAA,UACC,MAAM/C;AAAA,UACN,aAAApB;AAAA,UACA,kBAAAC;AAAA,UACA,mBAAAC;AAAA,UACA,sBAAAC;AAAA,UACA,gBAAAC;AAAA,UACA,YAAAU;AAAA,UACA,iBAAAW;AAAA,UACA,MAAApB;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GAEF,sBAAsB,MAAMM,EAAgB,WAAW;AAAA,EACzD,GAAG;AAAA,IACDb;AAAA,IACAsB;AAAA,IACApB;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAW;AAAA,IACAT;AAAA,EAAA,CACD;AACD,QAAM+D,IAAqB;AAAA,IACzB;AAAA,IACAlD,IACI,uCACA;AAAA,EAAA,EAEH,OAAO,OAAO,EACd,KAAK,GAAG;AAEX,SACEmD,gBAAAA,EAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAK/D;AAAA,MACL,gBAAcmB,IAAkB,SAAS;AAAA,MACzC,WAAW2C;AAAA,MAEV,UAAA;AAAA,QAAA,CAAChE,KACA8D,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAASD;AAAA,YACT,WACE;AAAA,YAGD,UAAAjD,IAAe,SAASb,KAAwB;AAAA,UAAA;AAAA,QAAA;AAAA,QAGpDE,MAAS,gBAAgBN,MAAS,aACjCmE,gBAAAA,EAAAA,IAACI,IAAA,EAAc,SAAAxE,GAAkB,IAEjCoE,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK1D;AAAA,YACL,SAAQ;AAAA,YACR,OAAM;AAAA,YACN,iBAAe;AAAA,YACf,WAAW,CAACpB,GAAW,qCAAqC,EACzD,OAAO,OAAO,EACd,KAAK,GAAG;AAAA,YACX,OAAO;AAAA,cACL,QAAQ;AAAA,cACR,QAAQ;AAAA,YAAA;AAAA,UACV;AAAA,QAAA;AAAA,MACF;AAAA,IAAA;AAAA,EAAA;AAIR;"}
1
+ {"version":3,"file":"IframeSandbox.es.js","sources":["../../../src/components/ContentRender/IframeSandbox.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport SandboxApp from \"./SandboxApp\";\nimport { splitContentSegments } from \"./utils/split-content\";\nimport ContentRender from \"./ContentRender\";\n// Lazy-load iframe vendor libraries and inject them into the sandbox document.\nconst loadBlackboardVendor = () =>\n import(\"./blackboard-vendor\").then((m) => m.injectBlackboardLibraries);\n\nconst COMPLETE_IMAGE_TAG_PATTERN = /<img\\b[^>]*>/i;\nconst POST_IMAGE_STREAM_DEBOUNCE_MS = 180;\nexport interface IframeSandboxProps {\n content: string;\n className?: string;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n fullScreenButtonText?: string;\n hideFullScreen?: boolean;\n mode?: \"content\" | \"blackboard\";\n type: \"sandbox\" | \"markdown\";\n}\n\nconst normalizeTailwindHeightTokens = (className: string) =>\n className\n .split(/\\s+/)\n .filter(Boolean)\n .map((token) => token.split(\":\").pop() || token);\n\nconst parseViewportHeightCss = (value: string) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const matched = normalized.match(/^([0-9.]+)(vh|dvh|svh|lvh)$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst extractViewportHeightFromTailwindClass = (className: string) => {\n if (!className.trim()) return null;\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n if (\n normalizedTokens.includes(\"h-screen\") ||\n normalizedTokens.includes(\"h-dvh\")\n ) {\n return \"100dvh\";\n }\n if (normalizedTokens.includes(\"h-svh\")) {\n return \"100svh\";\n }\n if (normalizedTokens.includes(\"h-lvh\")) {\n return \"100lvh\";\n }\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+(vh|dvh|svh|lvh)\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)(vh|dvh|svh|lvh)\\]$/i);\n if (!matched) return null;\n return `${matched[1]}${matched[2].toLowerCase()}`;\n};\n\nconst IframeSandbox: React.FC<IframeSandboxProps> = ({\n content,\n type,\n className,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n hideFullScreen = false,\n mode = \"content\",\n}) => {\n const containerRef = useRef<HTMLDivElement>(null);\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const rootRef = useRef<Root | null>(null);\n const docRef = useRef<Document | null>(null);\n const updateHeightRef = useRef<() => void>(() => {});\n const [, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const isBlackboardMode = mode === \"blackboard\";\n const prevHtmlRef = useRef<string>(\"\");\n const htmlContent = React.useMemo(() => {\n const segments = splitContentSegments(content);\n // console.log('segments=====', segments);\n const sandboxSegments = segments.filter((seg) => seg.type === \"sandbox\");\n const sandboxContent =\n mode === \"blackboard\"\n ? sandboxSegments[sandboxSegments.length - 1]?.value || \"\"\n : sandboxSegments.map((seg) => seg.value).join(\"\\n\");\n return sandboxContent || \"\";\n }, [content, mode]);\n const [renderHtmlContent, setRenderHtmlContent] = useState(htmlContent);\n const prevIncomingHtmlRef = useRef(htmlContent);\n const pendingHtmlRef = useRef(htmlContent);\n const deferRenderTimerRef = useRef<number | null>(null);\n\n const clearDeferredRenderTimer = () => {\n if (deferRenderTimerRef.current === null) return;\n window.clearTimeout(deferRenderTimerRef.current);\n deferRenderTimerRef.current = null;\n };\n\n useEffect(\n () => () => {\n clearDeferredRenderTimer();\n },\n []\n );\n\n useEffect(() => {\n const prevIncomingHtml = prevIncomingHtmlRef.current;\n prevIncomingHtmlRef.current = htmlContent;\n\n const isAppendOnlyStream =\n !!prevIncomingHtml &&\n htmlContent.length > prevIncomingHtml.length &&\n htmlContent.startsWith(prevIncomingHtml);\n const containsCompleteImage = COMPLETE_IMAGE_TAG_PATTERN.test(htmlContent);\n const shouldDeferRender = isAppendOnlyStream && containsCompleteImage;\n\n if (!shouldDeferRender) {\n clearDeferredRenderTimer();\n pendingHtmlRef.current = htmlContent;\n setRenderHtmlContent(htmlContent);\n return;\n }\n\n pendingHtmlRef.current = htmlContent;\n clearDeferredRenderTimer();\n deferRenderTimerRef.current = window.setTimeout(() => {\n setRenderHtmlContent(pendingHtmlRef.current);\n deferRenderTimerRef.current = null;\n }, POST_IMAGE_STREAM_DEBOUNCE_MS);\n }, [htmlContent]);\n\n const rootViewportHeightCss = React.useMemo(() => {\n const normalized = renderHtmlContent.trim();\n if (!normalized) return null;\n const rootMatch = normalized.match(/^<([a-zA-Z][\\w:-]*)(\\s[^>]*?)?>/);\n if (!rootMatch) return null;\n const attrs = rootMatch[2] || \"\";\n const heightAttrMatch = attrs.match(/\\bheight\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (heightAttrMatch) {\n const explicitHeightCss = parseViewportHeightCss(heightAttrMatch[1]);\n if (explicitHeightCss) return explicitHeightCss;\n }\n const styleAttrMatch = attrs.match(/\\bstyle\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n const styleHeightMatch = styleAttrMatch?.match(\n /\\bheight\\s*:\\s*([^;]+)/i\n )?.[1];\n if (styleHeightMatch) {\n const styleHeightCss = parseViewportHeightCss(styleHeightMatch);\n if (styleHeightCss) return styleHeightCss;\n }\n const classAttrMatch = attrs.match(/\\bclass\\s*=\\s*[\"']([^\"']+)[\"']/i)?.[1];\n if (!classAttrMatch) return null;\n return extractViewportHeightFromTailwindClass(classAttrMatch);\n }, [renderHtmlContent]);\n const hasRootVhHeight = Boolean(rootViewportHeightCss);\n useEffect(() => {\n if (mode !== \"blackboard\") {\n prevHtmlRef.current = htmlContent;\n return;\n }\n const prev = prevHtmlRef.current;\n const isContinuation = prev && htmlContent.startsWith(prev);\n if (!isContinuation && prev) {\n setResetToken((token) => token + 1);\n }\n prevHtmlRef.current = htmlContent;\n }, [htmlContent, mode]);\n\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe) return undefined;\n\n const doc = iframe.contentDocument;\n if (!doc) return undefined;\n\n doc.open();\n doc.write(`<!DOCTYPE html>\n<html${mode === \"blackboard\" ? ' style=\"height: 100%;\"' : \"\"}>\n <head>\n <meta charset=\"utf-8\" />\n <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n <style>\n html, body, #root { width: 100%; height: 100%; }\n html, body { margin: 0; padding: 0; overflow: auto; }\n *, *::before, *::after { box-sizing: border-box; }\n </style>\n </head>\n <body>\n <div id=\"root\"></div>\n </body>\n</html>`);\n doc.close();\n\n docRef.current = doc;\n\n const rootEl = doc.getElementById(\"root\");\n if (!rootEl) return undefined;\n\n const root = createRoot(rootEl);\n rootRef.current = root;\n let isDestroyed = false;\n\n const parseExplicitHeight = (\n value: string,\n parentViewportHeight: number\n ) => {\n const normalized = value.trim().toLowerCase();\n if (!normalized) return null;\n const numeric = Number.parseFloat(normalized);\n if (Number.isNaN(numeric)) return null;\n if (/(dvh|svh|lvh|vh)$/i.test(normalized)) {\n return (numeric / 100) * parentViewportHeight;\n }\n if (normalized.endsWith(\"px\") || /^[0-9.]+$/.test(normalized)) {\n return numeric;\n }\n return null;\n };\n const parseTailwindHeightClass = (\n className: string,\n parentViewportHeight: number\n ) => {\n if (!className.trim()) return null;\n const viewportHeightCss =\n extractViewportHeightFromTailwindClass(className);\n if (viewportHeightCss) {\n return parseExplicitHeight(viewportHeightCss, parentViewportHeight);\n }\n const normalizedTokens = normalizeTailwindHeightTokens(className);\n const arbitraryToken = normalizedTokens.find((token) =>\n /^h-\\[[0-9.]+px\\]$/i.test(token)\n );\n if (!arbitraryToken) return null;\n const matched = arbitraryToken.match(/^h-\\[([0-9.]+)px\\]$/i);\n if (!matched) return null;\n const numeric = Number.parseFloat(matched[1]);\n if (Number.isNaN(numeric)) return null;\n return numeric;\n };\n\n const resolveExplicitHeight = () => {\n if (!iframeRef.current || !doc.body) return null;\n const wrapper = doc.body.querySelector(\n \".sandbox-wrapper\"\n ) as HTMLElement | null;\n const container = wrapper?.firstElementChild as HTMLElement | null;\n if (!container) return null;\n const elements = Array.from(container.children) as HTMLElement[];\n if (elements.length !== 1) return null;\n const target = elements[0];\n const heightValue = target.style.height || target.getAttribute(\"height\");\n const parentViewportHeight =\n iframeRef.current.ownerDocument?.documentElement?.clientHeight ||\n window.innerHeight;\n const parsed = heightValue\n ? parseExplicitHeight(heightValue, parentViewportHeight)\n : null;\n if (parsed !== null) {\n return Math.ceil(parsed);\n }\n const classHeight = parseTailwindHeightClass(\n target.getAttribute(\"class\") || \"\",\n parentViewportHeight\n );\n return classHeight !== null ? Math.ceil(classHeight) : null;\n };\n\n const updateHeight = () => {\n if (!iframeRef.current || !doc.body) return;\n const bodyRect = doc.body.getBoundingClientRect();\n const htmlRect = doc.documentElement?.getBoundingClientRect();\n const bodyHeight = bodyRect.height;\n const htmlHeight = htmlRect?.height || 0;\n const contentHeight = Math.max(bodyHeight, htmlHeight);\n const explicitHeight = resolveExplicitHeight();\n const nextHeight = Math.max(\n 200,\n explicitHeight ?? Math.ceil(contentHeight)\n );\n setHeight(nextHeight);\n };\n const scheduleHeightUpdate = () => {\n requestAnimationFrame(() => {\n if (isDestroyed) return;\n updateHeight();\n });\n };\n updateHeightRef.current = scheduleHeightUpdate;\n\n updateHeight();\n scheduleHeightUpdate();\n\n // Inject Tailwind/DaisyUI/GSAP into iframe for all sandbox modes.\n // Dynamic import keeps ~3.3 MB of vendor libs out of the main bundle.\n // Tailwind's MutationObserver ensures styles apply even if content renders first.\n loadBlackboardVendor()\n .then((inject) => {\n if (isDestroyed) return;\n inject(doc);\n scheduleHeightUpdate();\n })\n .catch(() => {\n if (isDestroyed) return;\n scheduleHeightUpdate();\n });\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\n isDestroyed = true;\n resizeObserver.disconnect();\n // Defer unmount to avoid React warning when parent is mid-render\n setTimeout(() => {\n root.unmount();\n rootRef.current = null;\n docRef.current = null;\n updateHeightRef.current = () => {};\n }, 0);\n };\n }, []);\n\n useEffect(() => {\n const onFullscreenChange = () => {\n setIsFullscreen(Boolean(document.fullscreenElement));\n };\n document.addEventListener(\"fullscreenchange\", onFullscreenChange);\n return () =>\n document.removeEventListener(\"fullscreenchange\", onFullscreenChange);\n }, []);\n\n const toggleFullscreen = () => {\n const target = containerRef.current || iframeRef.current;\n if (!target) return;\n if (document.fullscreenElement) {\n document.exitFullscreen().catch(() => {});\n return;\n }\n if (target.requestFullscreen) {\n target.requestFullscreen().catch(() => {});\n }\n };\n\n useEffect(() => {\n const root = rootRef.current;\n if (!root) return;\n\n root.render(\n <SandboxApp\n html={renderHtmlContent}\n loadingText={loadingText}\n styleLoadingText={styleLoadingText}\n scriptLoadingText={scriptLoadingText}\n fullScreenButtonText={fullScreenButtonText}\n hideFullScreen={hideFullScreen}\n resetToken={resetToken}\n hasRootVhHeight={hasRootVhHeight}\n mode={mode}\n />\n );\n requestAnimationFrame(() => updateHeightRef.current?.());\n }, [\n renderHtmlContent,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n fullScreenButtonText,\n resetToken,\n mode,\n ]);\n const containerClassName = [\n \"w-full relative content-render-iframe-sandbox\",\n isBlackboardMode\n ? \"h-full overflow-auto flex flex-col\"\n : \"aspect-[16/9] overflow-hidden flex items-center justify-center\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n ref={containerRef}\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className={containerClassName}\n >\n {!hideFullScreen && (\n <button\n type=\"button\"\n onClick={toggleFullscreen}\n className={\n \"absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer\"\n }\n >\n {isFullscreen ? \"退出全屏\" : fullScreenButtonText || \"全屏浏览\"}\n </button>\n )}\n {mode === \"blackboard\" && type === \"markdown\" ? (\n <ContentRender content={content} />\n ) : (\n <iframe\n ref={iframeRef}\n sandbox=\"allow-scripts allow-same-origin allow-popups allow-popups-to-escape-sandbox\"\n allow=\"fullscreen\"\n allowFullScreen\n className={[className, \"w-full h-full mx-auto my-auto block\"]\n .filter(Boolean)\n .join(\" \")}\n style={{\n height: \"100%\",\n margin: \"auto\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["loadBlackboardVendor","m","COMPLETE_IMAGE_TAG_PATTERN","POST_IMAGE_STREAM_DEBOUNCE_MS","normalizeTailwindHeightTokens","className","token","parseViewportHeightCss","value","normalized","matched","extractViewportHeightFromTailwindClass","normalizedTokens","arbitraryToken","IframeSandbox","content","type","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","isBlackboardMode","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","renderHtmlContent","setRenderHtmlContent","prevIncomingHtmlRef","pendingHtmlRef","deferRenderTimerRef","clearDeferredRenderTimer","useEffect","prevIncomingHtml","isAppendOnlyStream","containsCompleteImage","hasRootVhHeight","rootMatch","attrs","heightAttrMatch","explicitHeightCss","styleHeightMatch","styleHeightCss","classAttrMatch","prev","iframe","doc","rootEl","root","createRoot","isDestroyed","parseExplicitHeight","parentViewportHeight","numeric","parseTailwindHeightClass","viewportHeightCss","resolveExplicitHeight","container","elements","target","heightValue","parsed","classHeight","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","explicitHeight","nextHeight","scheduleHeightUpdate","inject","resizeObserver","onFullscreenChange","toggleFullscreen","jsx","SandboxApp","containerClassName","jsxs","ContentRender"],"mappings":"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"}