markdown-flow-ui 0.1.87 → 0.1.88-beta.0

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 (92) 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.cjs.js.map +1 -1
  28. 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
  29. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/react-is@18.3.1/node_modules/react-is/index.cjs.js +1 -1
  30. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/react-is@18.3.1/node_modules/react-is/index.es.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/index.cjs.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/index.es.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-custom-variable.cjs.js +1 -1
  34. 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
  35. 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
  36. 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
  37. 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
  38. 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.map +1 -1
  39. 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
  40. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/unified@11.0.5/node_modules/unified/lib/index.cjs.js +1 -1
  41. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/unified@11.0.5/node_modules/unified/lib/index.es.js +1 -1
  42. package/dist/_virtual/index.cjs10.js +1 -1
  43. package/dist/_virtual/index.cjs11.js +1 -1
  44. package/dist/_virtual/index.cjs12.js +1 -1
  45. package/dist/_virtual/index.cjs2.js +1 -1
  46. package/dist/_virtual/index.cjs3.js +1 -1
  47. package/dist/_virtual/index.cjs4.js +1 -1
  48. package/dist/_virtual/index.cjs5.js +1 -1
  49. package/dist/_virtual/index.cjs6.js +1 -1
  50. package/dist/_virtual/index.cjs7.js +1 -1
  51. package/dist/_virtual/index.cjs8.js +1 -1
  52. package/dist/_virtual/index.cjs9.js +1 -1
  53. package/dist/_virtual/index.es10.js +2 -2
  54. package/dist/_virtual/index.es11.js +2 -3
  55. package/dist/_virtual/index.es11.js.map +1 -1
  56. package/dist/_virtual/index.es12.js +2 -2
  57. package/dist/_virtual/index.es2.js +5 -2
  58. package/dist/_virtual/index.es2.js.map +1 -1
  59. package/dist/_virtual/index.es3.js +2 -4
  60. package/dist/_virtual/index.es3.js.map +1 -1
  61. package/dist/_virtual/index.es4.js +4 -5
  62. package/dist/_virtual/index.es4.js.map +1 -1
  63. package/dist/_virtual/index.es5.js +2 -5
  64. package/dist/_virtual/index.es5.js.map +1 -1
  65. package/dist/_virtual/index.es6.js +4 -4
  66. package/dist/_virtual/index.es7.js +5 -2
  67. package/dist/_virtual/index.es7.js.map +1 -1
  68. package/dist/_virtual/index.es8.js +3 -3
  69. package/dist/_virtual/index.es9.js +3 -2
  70. package/dist/_virtual/index.es9.js.map +1 -1
  71. package/dist/components/ContentRender/ContentRender.cjs.js +2 -2
  72. package/dist/components/ContentRender/ContentRender.cjs.js.map +1 -1
  73. package/dist/components/ContentRender/ContentRender.es.js +151 -138
  74. package/dist/components/ContentRender/ContentRender.es.js.map +1 -1
  75. package/dist/components/ContentRender/IframeSandbox.cjs.js +3 -3
  76. package/dist/components/ContentRender/IframeSandbox.cjs.js.map +1 -1
  77. package/dist/components/ContentRender/IframeSandbox.es.js +102 -70
  78. package/dist/components/ContentRender/IframeSandbox.es.js.map +1 -1
  79. package/dist/components/ContentRender/SandboxApp.cjs.js +6 -2
  80. package/dist/components/ContentRender/SandboxApp.cjs.js.map +1 -1
  81. package/dist/components/ContentRender/SandboxApp.d.ts +1 -0
  82. package/dist/components/ContentRender/SandboxApp.es.js +51 -44
  83. package/dist/components/ContentRender/SandboxApp.es.js.map +1 -1
  84. package/dist/components/ContentRender/utils/sanitize-invalid-tag-name.cjs.js +2 -0
  85. package/dist/components/ContentRender/utils/sanitize-invalid-tag-name.cjs.js.map +1 -0
  86. package/dist/components/ContentRender/utils/sanitize-invalid-tag-name.d.ts +2 -0
  87. package/dist/components/ContentRender/utils/sanitize-invalid-tag-name.es.js +17 -0
  88. package/dist/components/ContentRender/utils/sanitize-invalid-tag-name.es.js.map +1 -0
  89. package/dist/components/ui/inputGroup/textarea.cjs.js +1 -1
  90. package/dist/components/ui/inputGroup/textarea.es.js +1 -1
  91. package/dist/markdown-flow-ui-lib.css +1 -1
  92. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"ContentRender.es.js","sources":["../../../src/components/ContentRender/ContentRender.tsx"],"sourcesContent":["import \"highlight.js/styles/github.css\";\nimport \"katex/dist/katex.min.css\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport rehypeHighlight from \"rehype-highlight\";\nimport rehypeKatex from \"rehype-katex\";\nimport rehypeRaw from \"rehype-raw\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkFlow from \"remark-flow\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkMath from \"remark-math\";\nimport { CustomRenderBarProps, OnSendContentParams } from \"../types\";\nimport \"./contentRender.css\";\nimport \"./github-markdown-light.css\";\nimport CodeBlock from \"./CodeBlock\";\nimport CustomButtonInputVariable, {\n ComponentsWithCustomVariable,\n} from \"./plugins/CustomVariable\";\nimport MermaidChart from \"./plugins/MermaidChart\";\nimport useTypewriterStateMachine from \"./useTypewriterStateMachine\";\nimport {\n preserveCustomVariableProperties,\n restoreCustomVariableProperties,\n} from \"./utils/custom-variable-props\";\nimport {\n highlightLanguages,\n subsetLanguages,\n} from \"./utils/highlight-languages\";\n// import { processMarkdownText } from \"./utils/process-markdown\";\nimport {\n parseMarkdownSegments,\n mermaidBlockIsComplete,\n} from \"./utils/mermaid-parse\";\nimport { normalizeInlineHtml } from \"./utils/normalize-inline-html\";\nimport IframeSandbox from \"./IframeSandbox\";\nimport {\n splitContentSegments,\n type RenderSegment,\n} from \"./utils/split-content\";\n// Define component Props type\nexport interface ContentRenderProps {\n content: string;\n /**\n+ * Callback invoked when the custom button after content is clicked.\n+ * This button is rendered via the `<custom-button-after-content>` tag in markdown content.\n+ * @example\n+ * ```tsx\n+ * <ContentRender\n+ * content=\"Hello <custom-button-after-content>Ask</custom-button-after-content>\"\n+ * onClickCustomButtonAfterContent={() => console.log('Button clicked')}\n+ * />\n+ * ```\n+ */\n customRenderBar?: CustomRenderBarProps;\n onClickCustomButtonAfterContent?: () => void;\n onSend?: (content: OnSendContentParams) => void;\n typingSpeed?: number;\n enableTypewriter?: boolean;\n defaultButtonText?: string;\n defaultInputText?: string; // Text input by user\n defaultSelectedValues?: string[]; // Default selected values for multi-select\n readonly?: boolean;\n onTypeFinished?: () => void;\n // Multi-select confirm button text (i18n support)\n confirmButtonText?: string;\n // Copy button text (i18n support)\n copyButtonText?: string;\n // Copied state text (i18n support)\n copiedButtonText?: string;\n // Dynamic interaction format for multi-select support\n dynamicInteractionFormat?: string;\n // Loading text before first HTML block renders inside iframe (i18n support)\n sandboxLoadingText?: string;\n // Loading text while styles are being generated inside iframe\n sandboxStyleLoadingText?: string;\n // Loading text while scripts are being cached/executed inside iframe\n sandboxScriptLoadingText?: string;\n // Fullscreen button text for iframe sandbox\n sandboxFullscreenButtonText?: string;\n // Sandbox render mode\n sandboxMode?: \"content\" | \"blackboard\";\n beforeSend?: (param: OnSendContentParams) => boolean;\n // tooltipMinLength?: number; // Control minimum character length for tooltip display, default 10\n}\n\n// Render svg string via Shadow DOM to avoid markdown wrapping\nconst SvgBlockInShadow: React.FC<{ svg: string }> = ({ svg }) => {\n const hostRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const shadowRoot = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n const styleId = \"content-render-svg-style\";\n let styleEl = shadowRoot.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = document.createElement(\"style\");\n styleEl.id = styleId;\n // Keep intrinsic SVG width so the wrapper can scroll horizontally when needed\n styleEl.textContent = `\n svg { height: auto; display: inline-block; }\n svg.content-render-svg-el--responsive { width: 100%; max-width: 100%; }\n svg.content-render-svg-el--fixed { max-width: none; }\n `;\n shadowRoot.appendChild(styleEl);\n }\n\n const nodesToRemove = Array.from(shadowRoot.childNodes).filter(\n (node) => node !== styleEl\n );\n nodesToRemove.forEach((node) => shadowRoot.removeChild(node));\n\n const template = document.createElement(\"template\");\n template.innerHTML = svg;\n shadowRoot.append(template.content.cloneNode(true));\n\n let hasResponsiveSvg = false;\n let hasFixedSvg = false;\n\n shadowRoot.querySelectorAll(\"svg\").forEach((svgEl) => {\n // Derive responsive sizing from viewBox so pure viewBox SVGs stay visible and fluid\n const viewBox = svgEl.getAttribute(\"viewBox\");\n if (!viewBox) return;\n\n const dimensions = viewBox\n .trim()\n .split(/[\\s,]+/)\n .map((value) => Number(value));\n\n if (dimensions.length !== 4 || dimensions.some(Number.isNaN)) return;\n\n const [, , viewBoxWidth, viewBoxHeight] = dimensions;\n const widthAttr = svgEl.getAttribute(\"width\");\n const heightAttr = svgEl.getAttribute(\"height\");\n const hasWidth = !!widthAttr && widthAttr !== \"0\";\n const hasHeight = !!heightAttr && heightAttr !== \"0\";\n const shouldUseResponsiveSize = !hasWidth && !hasHeight;\n\n if (shouldUseResponsiveSize) {\n hasResponsiveSvg = true;\n svgEl.classList.add(\"content-render-svg-el--responsive\");\n svgEl.classList.remove(\"content-render-svg-el--fixed\");\n svgEl.style.width = \"100%\";\n svgEl.style.height = \"auto\";\n if (!svgEl.style.aspectRatio && viewBoxHeight > 0) {\n svgEl.style.aspectRatio = `${viewBoxWidth} / ${viewBoxHeight}`;\n }\n return;\n }\n\n hasFixedSvg = true;\n svgEl.classList.add(\"content-render-svg-el--fixed\");\n svgEl.classList.remove(\"content-render-svg-el--responsive\");\n if (!hasWidth && viewBoxWidth > 0) {\n svgEl.setAttribute(\"width\", `${viewBoxWidth}`);\n }\n if (!hasHeight && viewBoxHeight > 0) {\n svgEl.setAttribute(\"height\", `${viewBoxHeight}`);\n }\n });\n\n const hostResponsive = hasResponsiveSvg && !hasFixedSvg;\n host.classList.toggle(\"content-render-svg--responsive\", hostResponsive);\n host.classList.toggle(\"content-render-svg--fixed\", !hostResponsive);\n }, [svg]);\n\n return (\n <div className=\"content-render-svg-scroll\">\n <div className=\"content-render-svg\" ref={hostRef} />\n </div>\n );\n};\n\n// Extended component interface\ntype CustomComponents = ComponentsWithCustomVariable & {\n \"custom-button-after-content\"?: React.ComponentType<{\n children: React.ReactNode;\n }>;\n};\n\nconst remarkPlugins = [remarkGfm, remarkMath, remarkFlow, remarkBreaks];\n\nconst rehypePlugins = [\n preserveCustomVariableProperties,\n rehypeRaw,\n restoreCustomVariableProperties,\n [rehypeHighlight, { languages: highlightLanguages, subset: subsetLanguages }],\n rehypeKatex,\n];\n\nexport const MarkdownRenderer: React.FC<{\n content: string;\n components: CustomComponents;\n}> = ({ content: markdownContent, components }) => (\n <div className=\"markdown-renderer\">\n <ReactMarkdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={rehypePlugins}\n components={components}\n >\n {markdownContent}\n </ReactMarkdown>\n </div>\n);\n\nconst mergeNonSandboxSegments = (segments: RenderSegment[]) => {\n if (segments.length <= 1) return segments;\n const merged: RenderSegment[] = [];\n\n segments.forEach((segment) => {\n if (segment.type === \"sandbox\") {\n merged.push(segment);\n return;\n }\n\n const last = merged[merged.length - 1];\n if (last && last.type !== \"sandbox\") {\n merged[merged.length - 1] = {\n type: \"markdown\",\n value: `${last.value}${segment.value}`,\n };\n return;\n }\n\n merged.push({ type: \"markdown\", value: segment.value });\n });\n\n return merged;\n};\n\nconst ContentRender: React.FC<ContentRenderProps> = ({\n content,\n customRenderBar,\n onSend,\n typingSpeed = 30,\n enableTypewriter = false,\n defaultButtonText,\n defaultInputText,\n defaultSelectedValues,\n readonly = false,\n onTypeFinished,\n confirmButtonText,\n copyButtonText,\n copiedButtonText,\n sandboxLoadingText,\n sandboxStyleLoadingText,\n sandboxScriptLoadingText,\n sandboxFullscreenButtonText,\n sandboxMode = \"content\",\n onClickCustomButtonAfterContent,\n beforeSend,\n // tooltipMinLength,\n}) => {\n const normalizedContent = useMemo(\n () => normalizeInlineHtml(content),\n [content]\n );\n\n // Use custom Hook to handle typewriter effect\n const components: CustomComponents = {\n \"custom-button-after-content\": ({\n children,\n }: {\n children: React.ReactNode;\n }) => {\n return (\n <button\n className=\"content-render-custom-button-after-content\"\n onClick={onClickCustomButtonAfterContent}\n >\n <span className=\"content-render-custom-button-after-content-inner\">\n {children}\n </span>\n </button>\n );\n },\n \"custom-variable\": (props) => (\n <CustomButtonInputVariable\n {...props}\n readonly={readonly}\n defaultButtonText={defaultButtonText}\n defaultInputText={defaultInputText}\n defaultSelectedValues={defaultSelectedValues}\n onSend={onSend}\n beforeSend={beforeSend}\n confirmButtonText={confirmButtonText}\n // tooltipMinLength={tooltipMinLength}\n />\n ),\n code: (props) => {\n const { className, children, ...rest } = props as {\n className?: string;\n children?: React.ReactNode;\n };\n const match = /language-(\\w+)/.exec(className || \"\");\n const language = match?.[1];\n if (language === \"mermaid\") {\n const chartContent = children?.toString().replace(/\\n$/, \"\") || \"\";\n const frozen = mermaidBlockIsComplete(content, chartContent);\n return <MermaidChart chart={chartContent} frozen={frozen} />;\n }\n\n return (\n <code className={className} {...rest}>\n {children}\n </code>\n );\n },\n table: ({ ...props }) => (\n <div className=\"content-render-table-container\">\n <table className=\"content-render-table\" {...props} />\n </div>\n ),\n th: ({ ...props }) => <th className=\"content-render-th\" {...props} />,\n td: ({ ...props }) => <td className=\"content-render-td\" {...props} />,\n tr: ({ ...props }) => <tr className=\"content-render-tr\" {...props} />,\n li: ({ node, ...props }) => {\n const className = node?.properties?.className;\n const hasTaskListItem =\n (typeof className === \"string\" &&\n className.includes(\"task-list-item\")) ||\n (Array.isArray(className) && className.includes(\"task-list-item\"));\n if (hasTaskListItem) {\n return <li className=\"content-render-task-list-item\" {...props} />;\n }\n return <li {...props} />;\n },\n ol: ({ ...props }) => <ol className=\"content-render-ol\" {...props} />,\n ul: ({ ...props }) => <ul className=\"content-render-ul\" {...props} />,\n input: ({ ...props }) => {\n if (props.type === \"checkbox\") {\n return (\n <input\n type=\"checkbox\"\n className=\"content-render-checkbox\"\n disabled\n {...props}\n />\n );\n }\n return <input {...props} />;\n },\n a: ({ children, ...props }) => (\n <a target=\"_blank\" rel=\"noopener noreferrer\" {...props}>\n {children}\n </a>\n ),\n pre: (props) => (\n <CodeBlock\n {...props}\n copyButtonText={copyButtonText}\n copiedButtonText={copiedButtonText}\n />\n ),\n };\n\n const { displayContent, isComplete } = useTypewriterStateMachine({\n // processMarkdownText will let code block printf(\"You win!\\n\") become printf(\"You win!<br/>\");\n // content: processMarkdownText(content),\n content: normalizedContent,\n typingSpeed,\n disabled: !enableTypewriter,\n });\n\n const renderSegments = useMemo(\n () => splitContentSegments(content, true),\n [content]\n );\n\n const hasSandbox = renderSegments.some(\n (segment) => segment.type === \"sandbox\"\n );\n const mergedRenderSegments = useMemo(\n () => mergeNonSandboxSegments(renderSegments),\n [renderSegments]\n );\n\n const segments = useMemo(\n () => parseMarkdownSegments(displayContent),\n [displayContent]\n );\n\n const hasCompleted = useRef(false);\n\n useEffect(() => {\n if (hasSandbox) return;\n if (isComplete && !hasCompleted.current) {\n hasCompleted.current = true; // Mark as completed\n onTypeFinished?.(); // Call the passed callback\n }\n }, [hasSandbox, isComplete, onTypeFinished]);\n\n useEffect(() => {\n if (hasSandbox) return;\n hasCompleted.current = false; // Reset completion status when content changes\n }, [hasSandbox, content]);\n\n if (hasSandbox) {\n return (\n <div className=\"content-render markdown-body\">\n {mergedRenderSegments.map((segment, idx) =>\n segment.type === \"sandbox\" ? (\n <IframeSandbox\n key={`sandbox-${idx}`}\n hideFullScreen\n content={segment.value}\n className=\"content-render-iframe\"\n loadingText={sandboxLoadingText}\n styleLoadingText={sandboxStyleLoadingText}\n scriptLoadingText={sandboxScriptLoadingText}\n fullScreenButtonText={sandboxFullscreenButtonText}\n mode={sandboxMode}\n />\n ) : (\n <MarkdownRenderer\n key={`md-${idx}`}\n components={components}\n content={normalizeInlineHtml(segment.value)}\n />\n )\n )}\n </div>\n );\n }\n\n return (\n <div className=\"content-render markdown-body\">\n {segments.map((seg, index) => {\n if (seg.type === \"text\") {\n return (\n <MarkdownRenderer\n key={index}\n components={components}\n content={seg.value}\n />\n );\n }\n\n if (seg.type === \"mermaid\") {\n return (\n <MermaidChart\n key={index}\n chart={seg.value}\n frozen={!seg.complete}\n />\n );\n }\n\n if (seg.type === \"svg\") {\n return <SvgBlockInShadow key={index} svg={seg.value} />;\n }\n })}\n\n {customRenderBar && (\n <div className=\"content-render-custom-bar\">\n {React.createElement(customRenderBar, {\n content,\n displayContent,\n onSend,\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default ContentRender;\n"],"names":["SvgBlockInShadow","svg","hostRef","useRef","useEffect","host","shadowRoot","styleId","styleEl","node","template","hasResponsiveSvg","hasFixedSvg","svgEl","viewBox","dimensions","value","viewBoxWidth","viewBoxHeight","widthAttr","heightAttr","hasWidth","hasHeight","hostResponsive","jsx","remarkPlugins","remarkGfm","remarkMath","remarkFlow","remarkBreaks","rehypePlugins","preserveCustomVariableProperties","rehypeRaw","restoreCustomVariableProperties","rehypeHighlight","highlightLanguages","subsetLanguages","rehypeKatex","MarkdownRenderer","markdownContent","components","ReactMarkdown","mergeNonSandboxSegments","segments","merged","segment","last","ContentRender","content","customRenderBar","onSend","typingSpeed","enableTypewriter","defaultButtonText","defaultInputText","defaultSelectedValues","readonly","onTypeFinished","confirmButtonText","copyButtonText","copiedButtonText","sandboxLoadingText","sandboxStyleLoadingText","sandboxScriptLoadingText","sandboxFullscreenButtonText","sandboxMode","onClickCustomButtonAfterContent","beforeSend","normalizedContent","useMemo","normalizeInlineHtml","children","props","CustomButtonInputVariable","className","rest","chartContent","frozen","mermaidBlockIsComplete","MermaidChart","CodeBlock","displayContent","isComplete","useTypewriterStateMachine","renderSegments","splitContentSegments","hasSandbox","mergedRenderSegments","parseMarkdownSegments","hasCompleted","idx","IframeSandbox","jsxs","seg","index","React"],"mappings":"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"}
1
+ {"version":3,"file":"ContentRender.es.js","sources":["../../../src/components/ContentRender/ContentRender.tsx"],"sourcesContent":["import \"highlight.js/styles/github.css\";\nimport \"katex/dist/katex.min.css\";\nimport React, { useEffect, useMemo, useRef } from \"react\";\nimport ReactMarkdown from \"react-markdown\";\nimport rehypeHighlight from \"rehype-highlight\";\nimport rehypeKatex from \"rehype-katex\";\nimport rehypeRaw from \"rehype-raw\";\nimport remarkBreaks from \"remark-breaks\";\nimport remarkFlow from \"remark-flow\";\nimport remarkGfm from \"remark-gfm\";\nimport remarkMath from \"remark-math\";\nimport { CustomRenderBarProps, OnSendContentParams } from \"../types\";\nimport { sanitizeInvalidTagName } from \"./utils/sanitize-invalid-tag-name\";\nimport \"./contentRender.css\";\nimport \"./github-markdown-light.css\";\nimport CodeBlock from \"./CodeBlock\";\nimport CustomButtonInputVariable, {\n ComponentsWithCustomVariable,\n} from \"./plugins/CustomVariable\";\nimport MermaidChart from \"./plugins/MermaidChart\";\nimport useTypewriterStateMachine from \"./useTypewriterStateMachine\";\nimport {\n preserveCustomVariableProperties,\n restoreCustomVariableProperties,\n} from \"./utils/custom-variable-props\";\nimport {\n highlightLanguages,\n subsetLanguages,\n} from \"./utils/highlight-languages\";\n// import { processMarkdownText } from \"./utils/process-markdown\";\nimport {\n parseMarkdownSegments,\n mermaidBlockIsComplete,\n} from \"./utils/mermaid-parse\";\nimport { normalizeInlineHtml } from \"./utils/normalize-inline-html\";\nimport IframeSandbox from \"./IframeSandbox\";\nimport {\n splitContentSegments,\n type RenderSegment,\n} from \"./utils/split-content\";\n// Define component Props type\nexport interface ContentRenderProps {\n content: string;\n /**\n+ * Callback invoked when the custom button after content is clicked.\n+ * This button is rendered via the `<custom-button-after-content>` tag in markdown content.\n+ * @example\n+ * ```tsx\n+ * <ContentRender\n+ * content=\"Hello <custom-button-after-content>Ask</custom-button-after-content>\"\n+ * onClickCustomButtonAfterContent={() => console.log('Button clicked')}\n+ * />\n+ * ```\n+ */\n customRenderBar?: CustomRenderBarProps;\n onClickCustomButtonAfterContent?: () => void;\n onSend?: (content: OnSendContentParams) => void;\n typingSpeed?: number;\n enableTypewriter?: boolean;\n defaultButtonText?: string;\n defaultInputText?: string; // Text input by user\n defaultSelectedValues?: string[]; // Default selected values for multi-select\n readonly?: boolean;\n onTypeFinished?: () => void;\n // Multi-select confirm button text (i18n support)\n confirmButtonText?: string;\n // Copy button text (i18n support)\n copyButtonText?: string;\n // Copied state text (i18n support)\n copiedButtonText?: string;\n // Dynamic interaction format for multi-select support\n dynamicInteractionFormat?: string;\n // Loading text before first HTML block renders inside iframe (i18n support)\n sandboxLoadingText?: string;\n // Loading text while styles are being generated inside iframe\n sandboxStyleLoadingText?: string;\n // Loading text while scripts are being cached/executed inside iframe\n sandboxScriptLoadingText?: string;\n // Fullscreen button text for iframe sandbox\n sandboxFullscreenButtonText?: string;\n // Sandbox render mode\n sandboxMode?: \"content\" | \"blackboard\";\n beforeSend?: (param: OnSendContentParams) => boolean;\n // tooltipMinLength?: number; // Control minimum character length for tooltip display, default 10\n}\n\n// Render svg string via Shadow DOM to avoid markdown wrapping\nconst SvgBlockInShadow: React.FC<{ svg: string }> = ({ svg }) => {\n const hostRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const host = hostRef.current;\n if (!host) return;\n const shadowRoot = host.shadowRoot ?? host.attachShadow({ mode: \"open\" });\n const styleId = \"content-render-svg-style\";\n let styleEl = shadowRoot.getElementById(styleId) as HTMLStyleElement | null;\n\n if (!styleEl) {\n styleEl = document.createElement(\"style\");\n styleEl.id = styleId;\n // Keep intrinsic SVG width so the wrapper can scroll horizontally when needed\n styleEl.textContent = `\n svg { height: auto; display: inline-block; }\n svg.content-render-svg-el--responsive { width: 100%; max-width: 100%; }\n svg.content-render-svg-el--fixed { max-width: none; }\n `;\n shadowRoot.appendChild(styleEl);\n }\n\n const nodesToRemove = Array.from(shadowRoot.childNodes).filter(\n (node) => node !== styleEl\n );\n nodesToRemove.forEach((node) => shadowRoot.removeChild(node));\n\n const template = document.createElement(\"template\");\n template.innerHTML = svg;\n shadowRoot.append(template.content.cloneNode(true));\n\n let hasResponsiveSvg = false;\n let hasFixedSvg = false;\n\n shadowRoot.querySelectorAll(\"svg\").forEach((svgEl) => {\n // Derive responsive sizing from viewBox so pure viewBox SVGs stay visible and fluid\n const viewBox = svgEl.getAttribute(\"viewBox\");\n if (!viewBox) return;\n\n const dimensions = viewBox\n .trim()\n .split(/[\\s,]+/)\n .map((value) => Number(value));\n\n if (dimensions.length !== 4 || dimensions.some(Number.isNaN)) return;\n\n const [, , viewBoxWidth, viewBoxHeight] = dimensions;\n const widthAttr = svgEl.getAttribute(\"width\");\n const heightAttr = svgEl.getAttribute(\"height\");\n const isRelativeLength = (value?: string | null) => {\n if (!value) return false;\n const normalized = value.trim().toLowerCase();\n return normalized === \"auto\" || normalized.endsWith(\"%\");\n };\n const toNumericLength = (value?: string | null) => {\n if (!value) return null;\n const normalized = value.trim().toLowerCase();\n if (normalized === \"auto\" || normalized.endsWith(\"%\")) {\n return null;\n }\n const parsed = Number.parseFloat(normalized);\n return Number.isNaN(parsed) ? null : parsed;\n };\n // Treat percentage/auto sizing as responsive so viewBox drives the layout\n const isWidthRelative = isRelativeLength(widthAttr);\n const isHeightRelative = isRelativeLength(heightAttr);\n const widthMissing = !widthAttr || widthAttr === \"0\";\n const heightMissing = !heightAttr || heightAttr === \"0\";\n const numericWidth = toNumericLength(widthAttr);\n const numericHeight = toNumericLength(heightAttr);\n const matchesViewBox =\n numericWidth === viewBoxWidth && numericHeight === viewBoxHeight;\n\n // Prefer responsive layout when sizing is relative or matches the viewBox\n const shouldUseResponsiveSize =\n isWidthRelative ||\n isHeightRelative ||\n (widthMissing && heightMissing) ||\n matchesViewBox;\n\n if (shouldUseResponsiveSize) {\n hasResponsiveSvg = true;\n svgEl.classList.add(\"content-render-svg-el--responsive\");\n svgEl.classList.remove(\"content-render-svg-el--fixed\");\n svgEl.style.width = \"100%\";\n svgEl.style.height = \"auto\";\n if (!svgEl.style.aspectRatio && viewBoxHeight > 0) {\n svgEl.style.aspectRatio = `${viewBoxWidth} / ${viewBoxHeight}`;\n }\n return;\n }\n\n hasFixedSvg = true;\n svgEl.classList.add(\"content-render-svg-el--fixed\");\n svgEl.classList.remove(\"content-render-svg-el--responsive\");\n if (widthMissing && viewBoxWidth > 0) {\n svgEl.setAttribute(\"width\", `${viewBoxWidth}`);\n }\n if (heightMissing && viewBoxHeight > 0) {\n svgEl.setAttribute(\"height\", `${viewBoxHeight}`);\n }\n });\n\n const hostResponsive = hasResponsiveSvg && !hasFixedSvg;\n host.classList.toggle(\"content-render-svg--responsive\", hostResponsive);\n host.classList.toggle(\"content-render-svg--fixed\", !hostResponsive);\n }, [svg]);\n\n return (\n <div className=\"content-render-svg-scroll\">\n <div className=\"content-render-svg\" ref={hostRef} />\n </div>\n );\n};\n\n// Extended component interface\ntype CustomComponents = ComponentsWithCustomVariable & {\n \"custom-button-after-content\"?: React.ComponentType<{\n children: React.ReactNode;\n }>;\n};\n\nconst remarkPlugins = [remarkGfm, remarkMath, remarkFlow, remarkBreaks];\n\nconst rehypePlugins = [\n preserveCustomVariableProperties,\n rehypeRaw,\n sanitizeInvalidTagName,\n restoreCustomVariableProperties,\n [rehypeHighlight, { languages: highlightLanguages, subset: subsetLanguages }],\n rehypeKatex,\n];\n\nexport const MarkdownRenderer: React.FC<{\n content: string;\n components: CustomComponents;\n}> = ({ content: markdownContent, components }) => (\n <div className=\"markdown-renderer\">\n <ReactMarkdown\n remarkPlugins={remarkPlugins}\n rehypePlugins={rehypePlugins}\n components={components}\n >\n {markdownContent}\n </ReactMarkdown>\n </div>\n);\n\nconst mergeNonSandboxSegments = (segments: RenderSegment[]) => {\n if (segments.length <= 1) return segments;\n const merged: RenderSegment[] = [];\n\n segments.forEach((segment) => {\n if (segment.type === \"sandbox\") {\n merged.push(segment);\n return;\n }\n\n const last = merged[merged.length - 1];\n if (last && last.type !== \"sandbox\") {\n merged[merged.length - 1] = {\n type: \"markdown\",\n value: `${last.value}${segment.value}`,\n };\n return;\n }\n\n merged.push({ type: \"markdown\", value: segment.value });\n });\n\n return merged;\n};\n\nconst ContentRender: React.FC<ContentRenderProps> = ({\n content,\n customRenderBar,\n onSend,\n typingSpeed = 30,\n enableTypewriter = false,\n defaultButtonText,\n defaultInputText,\n defaultSelectedValues,\n readonly = false,\n onTypeFinished,\n confirmButtonText,\n copyButtonText,\n copiedButtonText,\n sandboxLoadingText,\n sandboxStyleLoadingText,\n sandboxScriptLoadingText,\n sandboxFullscreenButtonText,\n sandboxMode = \"content\",\n onClickCustomButtonAfterContent,\n beforeSend,\n // tooltipMinLength,\n}) => {\n const normalizedContent = useMemo(\n () => normalizeInlineHtml(content),\n [content]\n );\n\n // Use custom Hook to handle typewriter effect\n const components: CustomComponents = {\n \"custom-button-after-content\": ({\n children,\n }: {\n children: React.ReactNode;\n }) => {\n return (\n <button\n className=\"content-render-custom-button-after-content\"\n onClick={onClickCustomButtonAfterContent}\n >\n <span className=\"content-render-custom-button-after-content-inner\">\n {children}\n </span>\n </button>\n );\n },\n \"custom-variable\": (props) => (\n <CustomButtonInputVariable\n {...props}\n readonly={readonly}\n defaultButtonText={defaultButtonText}\n defaultInputText={defaultInputText}\n defaultSelectedValues={defaultSelectedValues}\n onSend={onSend}\n beforeSend={beforeSend}\n confirmButtonText={confirmButtonText}\n // tooltipMinLength={tooltipMinLength}\n />\n ),\n code: (props) => {\n const { className, children, ...rest } = props as {\n className?: string;\n children?: React.ReactNode;\n };\n const match = /language-(\\w+)/.exec(className || \"\");\n const language = match?.[1];\n if (language === \"mermaid\") {\n const chartContent = children?.toString().replace(/\\n$/, \"\") || \"\";\n const frozen = mermaidBlockIsComplete(content, chartContent);\n return <MermaidChart chart={chartContent} frozen={frozen} />;\n }\n\n return (\n <code className={className} {...rest}>\n {children}\n </code>\n );\n },\n table: ({ ...props }) => (\n <div className=\"content-render-table-container\">\n <table className=\"content-render-table\" {...props} />\n </div>\n ),\n th: ({ ...props }) => <th className=\"content-render-th\" {...props} />,\n td: ({ ...props }) => <td className=\"content-render-td\" {...props} />,\n tr: ({ ...props }) => <tr className=\"content-render-tr\" {...props} />,\n li: ({ node, ...props }) => {\n const className = node?.properties?.className;\n const hasTaskListItem =\n (typeof className === \"string\" &&\n className.includes(\"task-list-item\")) ||\n (Array.isArray(className) && className.includes(\"task-list-item\"));\n if (hasTaskListItem) {\n return <li className=\"content-render-task-list-item\" {...props} />;\n }\n return <li {...props} />;\n },\n ol: ({ ...props }) => <ol className=\"content-render-ol\" {...props} />,\n ul: ({ ...props }) => <ul className=\"content-render-ul\" {...props} />,\n input: ({ ...props }) => {\n if (props.type === \"checkbox\") {\n return (\n <input\n type=\"checkbox\"\n className=\"content-render-checkbox\"\n disabled\n {...props}\n />\n );\n }\n return <input {...props} />;\n },\n a: ({ children, ...props }) => (\n <a target=\"_blank\" rel=\"noopener noreferrer\" {...props}>\n {children}\n </a>\n ),\n pre: (props) => (\n <CodeBlock\n {...props}\n copyButtonText={copyButtonText}\n copiedButtonText={copiedButtonText}\n />\n ),\n };\n\n const { displayContent, isComplete } = useTypewriterStateMachine({\n // processMarkdownText will let code block printf(\"You win!\\n\") become printf(\"You win!<br/>\");\n // content: processMarkdownText(content),\n content: normalizedContent,\n typingSpeed,\n disabled: !enableTypewriter,\n });\n\n const renderSegments = useMemo(\n () => splitContentSegments(content, true),\n [content]\n );\n\n const hasSandbox = renderSegments.some(\n (segment) => segment.type === \"sandbox\"\n );\n const mergedRenderSegments = useMemo(\n () => mergeNonSandboxSegments(renderSegments),\n [renderSegments]\n );\n\n const segments = useMemo(\n () => parseMarkdownSegments(displayContent),\n [displayContent]\n );\n\n const hasCompleted = useRef(false);\n\n useEffect(() => {\n if (hasSandbox) return;\n if (isComplete && !hasCompleted.current) {\n hasCompleted.current = true; // Mark as completed\n onTypeFinished?.(); // Call the passed callback\n }\n }, [hasSandbox, isComplete, onTypeFinished]);\n\n useEffect(() => {\n if (hasSandbox) return;\n hasCompleted.current = false; // Reset completion status when content changes\n }, [hasSandbox, content]);\n\n if (hasSandbox) {\n return (\n <div className=\"content-render markdown-body\">\n {mergedRenderSegments.map((segment, idx) =>\n segment.type === \"sandbox\" ? (\n <IframeSandbox\n key={`sandbox-${idx}`}\n hideFullScreen\n content={segment.value}\n className=\"content-render-iframe\"\n loadingText={sandboxLoadingText}\n styleLoadingText={sandboxStyleLoadingText}\n scriptLoadingText={sandboxScriptLoadingText}\n fullScreenButtonText={sandboxFullscreenButtonText}\n mode={sandboxMode}\n />\n ) : (\n <MarkdownRenderer\n key={`md-${idx}`}\n components={components}\n content={normalizeInlineHtml(segment.value)}\n />\n )\n )}\n </div>\n );\n }\n\n return (\n <div className=\"content-render markdown-body\">\n {segments.map((seg, index) => {\n if (seg.type === \"text\") {\n return (\n <MarkdownRenderer\n key={index}\n components={components}\n content={seg.value}\n />\n );\n }\n\n if (seg.type === \"mermaid\") {\n return (\n <MermaidChart\n key={index}\n chart={seg.value}\n frozen={!seg.complete}\n />\n );\n }\n\n if (seg.type === \"svg\") {\n return <SvgBlockInShadow key={index} svg={seg.value} />;\n }\n })}\n\n {customRenderBar && (\n <div className=\"content-render-custom-bar\">\n {React.createElement(customRenderBar, {\n content,\n displayContent,\n onSend,\n })}\n </div>\n )}\n </div>\n );\n};\n\nexport default ContentRender;\n"],"names":["SvgBlockInShadow","svg","hostRef","useRef","useEffect","host","shadowRoot","styleId","styleEl","node","template","hasResponsiveSvg","hasFixedSvg","svgEl","viewBox","dimensions","value","viewBoxWidth","viewBoxHeight","widthAttr","heightAttr","isRelativeLength","normalized","toNumericLength","parsed","isWidthRelative","isHeightRelative","widthMissing","heightMissing","numericWidth","numericHeight","hostResponsive","jsx","remarkPlugins","remarkGfm","remarkMath","remarkFlow","remarkBreaks","rehypePlugins","preserveCustomVariableProperties","rehypeRaw","sanitizeInvalidTagName","restoreCustomVariableProperties","rehypeHighlight","highlightLanguages","subsetLanguages","rehypeKatex","MarkdownRenderer","markdownContent","components","ReactMarkdown","mergeNonSandboxSegments","segments","merged","segment","last","ContentRender","content","customRenderBar","onSend","typingSpeed","enableTypewriter","defaultButtonText","defaultInputText","defaultSelectedValues","readonly","onTypeFinished","confirmButtonText","copyButtonText","copiedButtonText","sandboxLoadingText","sandboxStyleLoadingText","sandboxScriptLoadingText","sandboxFullscreenButtonText","sandboxMode","onClickCustomButtonAfterContent","beforeSend","normalizedContent","useMemo","normalizeInlineHtml","children","props","CustomButtonInputVariable","className","rest","chartContent","frozen","mermaidBlockIsComplete","MermaidChart","CodeBlock","displayContent","isComplete","useTypewriterStateMachine","renderSegments","splitContentSegments","hasSandbox","mergedRenderSegments","parseMarkdownSegments","hasCompleted","idx","IframeSandbox","jsxs","seg","index","React"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const u=require("../../_virtual/jsx-runtime.cjs.js"),n=require("react"),A=require("react-dom/client"),B=require("./SandboxApp.cjs.js"),D=require("./utils/split-content.cjs.js"),P=require("./ContentRender.cjs.js"),_=({content:l,type:j,className:W,loadingText:g,styleLoadingText:x,scriptLoadingText:R,fullScreenButtonText:a,hideFullScreen:p=!1,mode:r="content"})=>{const v=n.useRef(null),i=n.useRef(null),f=n.useRef(null),E=n.useRef(null),d=n.useRef(()=>{}),[C,w]=n.useState(480),[y,k]=n.useState(0),[q,F]=n.useState(!1),m=n.useRef(""),o=n.useMemo(()=>{const t=D.splitContentSegments(l).filter(c=>c.type==="sandbox");return(r==="blackboard"?t[t.length-1]?.value||"":t.map(c=>c.value).join(`
2
- `))||""},[l,r]);n.useEffect(()=>{if(r!=="blackboard"){m.current=o;return}const e=m.current;!(e&&o.startsWith(e))&&e&&k(s=>s+1),m.current=o},[o,r]),n.useEffect(()=>{const e=i.current;if(!e)return;const t=e.contentDocument;if(!t)return;t.open(),t.write(`<!DOCTYPE html>
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const h=require("../../_virtual/jsx-runtime.cjs.js"),n=require("react"),$=require("react-dom/client"),B=require("./SandboxApp.cjs.js"),P=require("./utils/split-content.cjs.js"),_=require("./ContentRender.cjs.js"),L=({content:f,type:S,className:Y,loadingText:C,styleLoadingText:H,scriptLoadingText:j,fullScreenButtonText:p,hideFullScreen:M=!1,mode:c="content"})=>{const q=n.useRef(null),l=n.useRef(null),x=n.useRef(null),k=n.useRef(null),R=n.useRef(()=>{}),[z,A]=n.useState(480),[F,O]=n.useState(0),[D,I]=n.useState(!1),v=n.useRef(""),u=n.useMemo(()=>{const t=P.splitContentSegments(f).filter(o=>o.type==="sandbox");return(c==="blackboard"?t[t.length-1]?.value||"":t.map(o=>o.value).join(`
2
+ `))||""},[f,c]),N=n.useMemo(()=>{const e=u.trim();if(!e)return!1;const t=e.match(/^<([a-zA-Z][\w:-]*)(\s[^>]*?)?>/);if(!t)return!1;const r=t[2]||"",o=r.match(/\bheight\s*=\s*["']([^"']+)["']/i);if(o&&/vh$/i.test(o[1].trim()))return!0;const d=r.match(/\bstyle\s*=\s*["']([^"']+)["']/i);return d?/height\s*:\s*[^;]*vh\b/i.test(d[1]):!1},[u]);n.useEffect(()=>{if(c!=="blackboard"){v.current=u;return}const e=v.current;!(e&&u.startsWith(e))&&e&&O(r=>r+1),v.current=u},[u,c]),n.useEffect(()=>{const e=l.current;if(!e)return;const t=e.contentDocument;if(!t)return;t.open(),t.write(`<!DOCTYPE html>
3
3
  <html>
4
4
  <head>
5
5
  <meta charset="utf-8" />
@@ -11,5 +11,5 @@
11
11
  <body>
12
12
  <div id="root"></div>
13
13
  </body>
14
- </html>`),t.close(),E.current=t;const s=t.getElementById("root");if(!s)return;const c=A.createRoot(s);f.current=c;const h=()=>{if(!i.current||!t.body)return;const S=t.body.getBoundingClientRect(),M=t.documentElement?.getBoundingClientRect(),z=S.height,N=M?.height||0,O=Math.max(z,N),I=Math.max(200,Math.ceil(O));w(I)};d.current=h,h();const b=new ResizeObserver(()=>h());return b.observe(t.body),s&&b.observe(s),()=>{b.disconnect(),setTimeout(()=>{c.unmount(),f.current=null,E.current=null,d.current=()=>{}},0)}},[]),n.useEffect(()=>{const e=()=>{F(!!document.fullscreenElement)};return document.addEventListener("fullscreenchange",e),()=>document.removeEventListener("fullscreenchange",e)},[]);const H=()=>{const e=v.current||i.current;if(e){if(document.fullscreenElement){document.exitFullscreen().catch(()=>{});return}e.requestFullscreen&&e.requestFullscreen().catch(()=>{})}};return n.useEffect(()=>{const e=f.current;e&&(e.render(u.jsxRuntimeExports.jsx(B.default,{html:o,loadingText:g,styleLoadingText:x,scriptLoadingText:R,fullScreenButtonText:a,hideFullScreen:p,resetToken:y,mode:r})),requestAnimationFrame(()=>d.current?.()))},[l,o,g,x,R,a,y,r]),u.jsxRuntimeExports.jsxs("div",{ref:v,className:"w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox",children:[!p&&u.jsxRuntimeExports.jsx("button",{type:"button",onClick:H,className:"absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer",children:q?"退出全屏":a||"全屏浏览"}),r==="blackboard"&&j==="markdown"?u.jsxRuntimeExports.jsx(P.default,{content:l}):u.jsxRuntimeExports.jsx("iframe",{ref:i,sandbox:"allow-scripts allow-same-origin",allow:"fullscreen",allowFullScreen:!0,className:"w-full",style:{height:r==="blackboard"?"100%":`${C}px`}})]})};exports.default=_;
14
+ </html>`),t.close(),k.current=t;const r=t.getElementById("root");if(!r)return;const o=$.createRoot(r);x.current=o;const d=(m,a)=>{const s=m.trim().toLowerCase();if(!s)return null;const i=Number.parseFloat(s);return Number.isNaN(i)?null:s.endsWith("vh")?i/100*a:s.endsWith("px")||/^[0-9.]+$/.test(s)?i:null},W=()=>{if(!l.current||!t.body)return null;const a=t.body.querySelector(".sandbox-wrapper")?.firstElementChild;if(!a)return null;const s=Array.from(a.children);if(s.length!==1)return null;const i=s[0],b=i.style.height||i.getAttribute("height");if(!b)return null;const w=l.current.ownerDocument?.documentElement?.clientHeight||window.innerHeight,g=d(b,w);return g?Math.ceil(g):null},y=()=>{if(!l.current||!t.body)return;const m=t.body.getBoundingClientRect(),a=t.documentElement?.getBoundingClientRect(),s=m.height,i=a?.height||0,b=Math.max(s,i),w=W(),g=Math.max(200,w??Math.ceil(b));A(g)};R.current=y,y();const E=new ResizeObserver(()=>y());return E.observe(t.body),r&&E.observe(r),()=>{E.disconnect(),setTimeout(()=>{o.unmount(),x.current=null,k.current=null,R.current=()=>{}},0)}},[]),n.useEffect(()=>{const e=()=>{I(!!document.fullscreenElement)};return document.addEventListener("fullscreenchange",e),()=>document.removeEventListener("fullscreenchange",e)},[]);const V=()=>{const e=q.current||l.current;if(e){if(document.fullscreenElement){document.exitFullscreen().catch(()=>{});return}e.requestFullscreen&&e.requestFullscreen().catch(()=>{})}};return n.useEffect(()=>{const e=x.current;e&&(e.render(h.jsxRuntimeExports.jsx(B.default,{html:u,loadingText:C,styleLoadingText:H,scriptLoadingText:j,fullScreenButtonText:p,hideFullScreen:M,resetToken:F,hasRootVhHeight:N,mode:c})),requestAnimationFrame(()=>R.current?.()))},[f,u,C,H,j,p,F,c]),h.jsxRuntimeExports.jsxs("div",{ref:q,"data-root-vh":N?"true":"false",className:"w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox",children:[!M&&h.jsxRuntimeExports.jsx("button",{type:"button",onClick:V,className:"absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer",children:D?"退出全屏":p||"全屏浏览"}),c==="blackboard"&&S==="markdown"?h.jsxRuntimeExports.jsx(_.default,{content:f}):h.jsxRuntimeExports.jsx("iframe",{ref:l,sandbox:"allow-scripts allow-same-origin",allow:"fullscreen",allowFullScreen:!0,className:"w-full",style:{height:c==="blackboard"?"100%":`${z}px`}})]})};exports.default=L;
15
15
  //# sourceMappingURL=IframeSandbox.cjs.js.map
@@ -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\";\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 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 [height, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\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\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>\n <head>\n <meta charset=\"utf-8\" />\n <style>\n html, body { margin: 0; padding: 0; }\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 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\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 nextHeight = Math.max(200, Math.ceil(contentHeight));\n setHeight(nextHeight);\n };\n updateHeightRef.current = updateHeight;\n\n updateHeight();\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\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 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\n return (\n <div\n ref={containerRef}\n className={\n \"w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox\"\n }\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\"\n allow=\"fullscreen\"\n allowFullScreen\n className={(className, \"w-full\")}\n style={{\n height: mode === \"blackboard\" ? \"100%\" : `${height}px`,\n // height: `${height}px`,\n // margin: \"16px 0\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["IframeSandbox","content","type","className","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","height","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","useEffect","prev","token","iframe","doc","rootEl","root","createRoot","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","nextHeight","resizeObserver","onFullscreenChange","toggleFullscreen","target","jsx","SandboxApp","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\";\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 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 [height, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\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 hasRootVhHeight = React.useMemo(() => {\n const normalized = htmlContent.trim();\n if (!normalized) return false;\n const rootMatch = normalized.match(/^<([a-zA-Z][\\w:-]*)(\\s[^>]*?)?>/);\n if (!rootMatch) return false;\n const attrs = rootMatch[2] || \"\";\n const heightAttrMatch = attrs.match(/\\bheight\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (heightAttrMatch && /vh$/i.test(heightAttrMatch[1].trim())) {\n return true;\n }\n const styleAttrMatch = attrs.match(/\\bstyle\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (!styleAttrMatch) return false;\n return /height\\s*:\\s*[^;]*vh\\b/i.test(styleAttrMatch[1]);\n }, [htmlContent]);\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>\n <head>\n <meta charset=\"utf-8\" />\n <style>\n html, body { margin: 0; padding: 0; }\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 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\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 (normalized.endsWith(\"vh\")) {\n return (numeric / 100) * parentViewportHeight;\n }\n if (normalized.endsWith(\"px\") || /^[0-9.]+$/.test(normalized)) {\n return numeric;\n }\n return null;\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 if (!heightValue) return null;\n const parentViewportHeight =\n iframeRef.current.ownerDocument?.documentElement?.clientHeight ||\n window.innerHeight;\n const parsed = parseExplicitHeight(heightValue, parentViewportHeight);\n return parsed ? Math.ceil(parsed) : 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 updateHeightRef.current = updateHeight;\n\n updateHeight();\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\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\n return (\n <div\n ref={containerRef}\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className={\n \"w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox\"\n }\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\"\n allow=\"fullscreen\"\n allowFullScreen\n className={(className, \"w-full\")}\n style={{\n height: mode === \"blackboard\" ? \"100%\" : `${height}px`,\n // height: `${height}px`,\n // margin: \"16px 0\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["IframeSandbox","content","type","className","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","height","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","hasRootVhHeight","normalized","rootMatch","attrs","heightAttrMatch","styleAttrMatch","useEffect","prev","token","iframe","doc","rootEl","root","createRoot","parseExplicitHeight","value","parentViewportHeight","numeric","resolveExplicitHeight","container","elements","target","heightValue","parsed","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","explicitHeight","nextHeight","resizeObserver","onFullscreenChange","toggleFullscreen","jsx","SandboxApp","jsxs","ContentRender"],"mappings":"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"}
@@ -1,34 +1,44 @@
1
- import { j as l } from "../../_virtual/jsx-runtime.es.js";
2
- import D, { useRef as c, useState as p, useEffect as a } from "react";
3
- import { createRoot as _ } from "react-dom/client";
1
+ import { j as h } from "../../_virtual/jsx-runtime.es.js";
2
+ import S, { useRef as a, useState as E, useEffect as p } from "react";
3
+ import { createRoot as L } from "react-dom/client";
4
4
  import P from "./SandboxApp.es.js";
5
- import { splitContentSegments as W } from "./utils/split-content.es.js";
6
- import Y from "./ContentRender.es.js";
7
- const V = ({
8
- content: u,
9
- type: j,
10
- className: $,
11
- loadingText: x,
12
- styleLoadingText: v,
13
- scriptLoadingText: R,
14
- fullScreenButtonText: m,
15
- hideFullScreen: y = !1,
16
- mode: n = "content"
5
+ import { splitContentSegments as Y } from "./utils/split-content.es.js";
6
+ import Z from "./ContentRender.es.js";
7
+ const ee = ({
8
+ content: m,
9
+ type: D,
10
+ className: G,
11
+ loadingText: j,
12
+ styleLoadingText: k,
13
+ scriptLoadingText: F,
14
+ fullScreenButtonText: x,
15
+ hideFullScreen: M = !1,
16
+ mode: s = "content"
17
17
  }) => {
18
- const C = c(null), i = c(null), f = c(null), w = c(null), d = c(() => {
19
- }), [k, F] = p(480), [E, H] = p(0), [z, M] = p(!1), h = c(""), o = D.useMemo(() => {
20
- const t = W(u).filter((s) => s.type === "sandbox");
21
- return (n === "blackboard" ? t[t.length - 1]?.value || "" : t.map((s) => s.value).join(`
18
+ const N = a(null), i = a(null), v = a(null), z = a(null), y = a(() => {
19
+ }), [I, O] = E(480), [A, V] = E(0), [W, $] = E(!1), w = a(""), c = S.useMemo(() => {
20
+ const t = Y(m).filter((o) => o.type === "sandbox");
21
+ return (s === "blackboard" ? t[t.length - 1]?.value || "" : t.map((o) => o.value).join(`
22
22
  `)) || "";
23
- }, [u, n]);
24
- a(() => {
25
- if (n !== "blackboard") {
26
- h.current = o;
23
+ }, [m, s]), q = S.useMemo(() => {
24
+ const e = c.trim();
25
+ if (!e) return !1;
26
+ const t = e.match(/^<([a-zA-Z][\w:-]*)(\s[^>]*?)?>/);
27
+ if (!t) return !1;
28
+ const n = t[2] || "", o = n.match(/\bheight\s*=\s*["']([^"']+)["']/i);
29
+ if (o && /vh$/i.test(o[1].trim()))
30
+ return !0;
31
+ const f = n.match(/\bstyle\s*=\s*["']([^"']+)["']/i);
32
+ return f ? /height\s*:\s*[^;]*vh\b/i.test(f[1]) : !1;
33
+ }, [c]);
34
+ p(() => {
35
+ if (s !== "blackboard") {
36
+ w.current = c;
27
37
  return;
28
38
  }
29
- const e = h.current;
30
- !(e && o.startsWith(e)) && e && H((r) => r + 1), h.current = o;
31
- }, [o, n]), a(() => {
39
+ const e = w.current;
40
+ !(e && c.startsWith(e)) && e && V((n) => n + 1), w.current = c;
41
+ }, [c, s]), p(() => {
32
42
  const e = i.current;
33
43
  if (!e) return;
34
44
  const t = e.contentDocument;
@@ -45,32 +55,52 @@ const V = ({
45
55
  <body>
46
56
  <div id="root"></div>
47
57
  </body>
48
- </html>`), t.close(), w.current = t;
49
- const r = t.getElementById("root");
50
- if (!r) return;
51
- const s = _(r);
52
- f.current = s;
53
- const b = () => {
58
+ </html>`), t.close(), z.current = t;
59
+ const n = t.getElementById("root");
60
+ if (!n) return;
61
+ const o = L(n);
62
+ v.current = o;
63
+ const f = (d, u) => {
64
+ const r = d.trim().toLowerCase();
65
+ if (!r) return null;
66
+ const l = Number.parseFloat(r);
67
+ return Number.isNaN(l) ? null : r.endsWith("vh") ? l / 100 * u : r.endsWith("px") || /^[0-9.]+$/.test(r) ? l : null;
68
+ }, _ = () => {
69
+ if (!i.current || !t.body) return null;
70
+ const u = t.body.querySelector(
71
+ ".sandbox-wrapper"
72
+ )?.firstElementChild;
73
+ if (!u) return null;
74
+ const r = Array.from(u.children);
75
+ if (r.length !== 1) return null;
76
+ const l = r[0], b = l.style.height || l.getAttribute("height");
77
+ if (!b) return null;
78
+ const C = i.current.ownerDocument?.documentElement?.clientHeight || window.innerHeight, g = f(b, C);
79
+ return g ? Math.ceil(g) : null;
80
+ }, R = () => {
54
81
  if (!i.current || !t.body) return;
55
- const q = t.body.getBoundingClientRect(), I = t.documentElement?.getBoundingClientRect(), O = q.height, S = I?.height || 0, A = Math.max(O, S), B = Math.max(200, Math.ceil(A));
56
- F(B);
82
+ const d = t.body.getBoundingClientRect(), u = t.documentElement?.getBoundingClientRect(), r = d.height, l = u?.height || 0, b = Math.max(r, l), C = _(), g = Math.max(
83
+ 200,
84
+ C ?? Math.ceil(b)
85
+ );
86
+ O(g);
57
87
  };
58
- d.current = b, b();
59
- const g = new ResizeObserver(() => b());
60
- return g.observe(t.body), r && g.observe(r), () => {
61
- g.disconnect(), setTimeout(() => {
62
- s.unmount(), f.current = null, w.current = null, d.current = () => {
88
+ y.current = R, R();
89
+ const H = new ResizeObserver(() => R());
90
+ return H.observe(t.body), n && H.observe(n), () => {
91
+ H.disconnect(), setTimeout(() => {
92
+ o.unmount(), v.current = null, z.current = null, y.current = () => {
63
93
  };
64
94
  }, 0);
65
95
  };
66
- }, []), a(() => {
96
+ }, []), p(() => {
67
97
  const e = () => {
68
- M(!!document.fullscreenElement);
98
+ $(!!document.fullscreenElement);
69
99
  };
70
100
  return document.addEventListener("fullscreenchange", e), () => document.removeEventListener("fullscreenchange", e);
71
101
  }, []);
72
- const N = () => {
73
- const e = C.current || i.current;
102
+ const B = () => {
103
+ const e = N.current || i.current;
74
104
  if (e) {
75
105
  if (document.fullscreenElement) {
76
106
  document.exitFullscreen().catch(() => {
@@ -81,48 +111,50 @@ const V = ({
81
111
  });
82
112
  }
83
113
  };
84
- return a(() => {
85
- const e = f.current;
114
+ return p(() => {
115
+ const e = v.current;
86
116
  e && (e.render(
87
- /* @__PURE__ */ l.jsx(
117
+ /* @__PURE__ */ h.jsx(
88
118
  P,
89
119
  {
90
- html: o,
91
- loadingText: x,
92
- styleLoadingText: v,
93
- scriptLoadingText: R,
94
- fullScreenButtonText: m,
95
- hideFullScreen: y,
96
- resetToken: E,
97
- mode: n
120
+ html: c,
121
+ loadingText: j,
122
+ styleLoadingText: k,
123
+ scriptLoadingText: F,
124
+ fullScreenButtonText: x,
125
+ hideFullScreen: M,
126
+ resetToken: A,
127
+ hasRootVhHeight: q,
128
+ mode: s
98
129
  }
99
130
  )
100
- ), requestAnimationFrame(() => d.current?.()));
131
+ ), requestAnimationFrame(() => y.current?.()));
101
132
  }, [
102
- u,
103
- o,
104
- x,
105
- v,
106
- R,
107
133
  m,
108
- E,
109
- n
110
- ]), /* @__PURE__ */ l.jsxs(
134
+ c,
135
+ j,
136
+ k,
137
+ F,
138
+ x,
139
+ A,
140
+ s
141
+ ]), /* @__PURE__ */ h.jsxs(
111
142
  "div",
112
143
  {
113
- ref: C,
144
+ ref: N,
145
+ "data-root-vh": q ? "true" : "false",
114
146
  className: "w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox",
115
147
  children: [
116
- !y && /* @__PURE__ */ l.jsx(
148
+ !M && /* @__PURE__ */ h.jsx(
117
149
  "button",
118
150
  {
119
151
  type: "button",
120
- onClick: N,
152
+ onClick: B,
121
153
  className: "absolute top-2 right-2 z-50 p-1.5 bg-black/75 text-white rounded-md cursor-pointer",
122
- children: z ? "退出全屏" : m || "全屏浏览"
154
+ children: W ? "退出全屏" : x || "全屏浏览"
123
155
  }
124
156
  ),
125
- n === "blackboard" && j === "markdown" ? /* @__PURE__ */ l.jsx(Y, { content: u }) : /* @__PURE__ */ l.jsx(
157
+ s === "blackboard" && D === "markdown" ? /* @__PURE__ */ h.jsx(Z, { content: m }) : /* @__PURE__ */ h.jsx(
126
158
  "iframe",
127
159
  {
128
160
  ref: i,
@@ -131,7 +163,7 @@ const V = ({
131
163
  allowFullScreen: !0,
132
164
  className: "w-full",
133
165
  style: {
134
- height: n === "blackboard" ? "100%" : `${k}px`
166
+ height: s === "blackboard" ? "100%" : `${I}px`
135
167
  // height: `${height}px`,
136
168
  // margin: "16px 0",
137
169
  }
@@ -142,6 +174,6 @@ const V = ({
142
174
  );
143
175
  };
144
176
  export {
145
- V as default
177
+ ee as default
146
178
  };
147
179
  //# 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\";\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 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 [height, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\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\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>\n <head>\n <meta charset=\"utf-8\" />\n <style>\n html, body { margin: 0; padding: 0; }\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 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\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 nextHeight = Math.max(200, Math.ceil(contentHeight));\n setHeight(nextHeight);\n };\n updateHeightRef.current = updateHeight;\n\n updateHeight();\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\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 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\n return (\n <div\n ref={containerRef}\n className={\n \"w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox\"\n }\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\"\n allow=\"fullscreen\"\n allowFullScreen\n className={(className, \"w-full\")}\n style={{\n height: mode === \"blackboard\" ? \"100%\" : `${height}px`,\n // height: `${height}px`,\n // margin: \"16px 0\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["IframeSandbox","content","type","className","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","height","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","useEffect","prev","token","iframe","doc","rootEl","root","createRoot","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","nextHeight","resizeObserver","onFullscreenChange","toggleFullscreen","target","jsx","SandboxApp","jsxs","ContentRender"],"mappings":";;;;;;AAiBA,MAAMA,IAA8C,CAAC;AAAA,EACnD,SAAAC;AAAA,EACA,MAAAC;AAAA,EACA,WAAAC;AAAA,EACA,aAAAC;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,CAACK,GAAQC,CAAS,IAAIC,EAAS,GAAG,GAClC,CAACC,GAAYC,CAAa,IAAIF,EAAS,CAAC,GACxC,CAACG,GAAcC,CAAe,IAAIJ,EAAS,EAAK,GAChDK,IAAcZ,EAAe,EAAE,GAC/Ba,IAAcC,EAAM,QAAQ,MAAM;AAGtC,UAAMC,IAFWC,EAAqB1B,CAAO,EAEZ,OAAO,CAAC2B,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,CAAC3B,GAASQ,CAAI,CAAC;AAElB,EAAAoB,EAAU,MAAM;AACd,QAAIpB,MAAS,cAAc;AACzB,MAAAc,EAAY,UAAUC;AACtB;AAAA,IACF;AACA,UAAMM,IAAOP,EAAY;AAEzB,IAAI,EADmBO,KAAQN,EAAY,WAAWM,CAAI,MACnCA,KACrBV,EAAc,CAACW,MAAUA,IAAQ,CAAC,GAEpCR,EAAY,UAAUC;AAAA,EACxB,GAAG,CAACA,GAAaf,CAAI,CAAC,GAEtBoB,EAAU,MAAM;AACd,UAAMG,IAASpB,EAAU;AACzB,QAAI,CAACoB,EAAQ;AAEb,UAAMC,IAAMD,EAAO;AACnB,QAAI,CAACC,EAAK;AAEV,IAAAA,EAAI,KAAA,GACJA,EAAI,MAAM;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,QAYN,GACJA,EAAI,MAAA,GACJnB,EAAO,UAAUmB;AAEjB,UAAMC,IAASD,EAAI,eAAe,MAAM;AACxC,QAAI,CAACC,EAAQ;AAEb,UAAMC,IAAOC,EAAWF,CAAM;AAC9B,IAAArB,EAAQ,UAAUsB;AAElB,UAAME,IAAe,MAAM;AACzB,UAAI,CAACzB,EAAU,WAAW,CAACqB,EAAI,KAAM;AACrC,YAAMK,IAAWL,EAAI,KAAK,sBAAA,GACpBM,IAAWN,EAAI,iBAAiB,sBAAA,GAChCO,IAAaF,EAAS,QACtBG,IAAaF,GAAU,UAAU,GACjCG,IAAgB,KAAK,IAAIF,GAAYC,CAAU,GAC/CE,IAAa,KAAK,IAAI,KAAK,KAAK,KAAKD,CAAa,CAAC;AACzD,MAAAzB,EAAU0B,CAAU;AAAA,IACtB;AACA,IAAA5B,EAAgB,UAAUsB,GAE1BA,EAAA;AAEA,UAAMO,IAAiB,IAAI,eAAe,MAAMP,GAAc;AAC9D,WAAAO,EAAe,QAAQX,EAAI,IAAI,GAC3BC,KACFU,EAAe,QAAQV,CAAM,GAGxB,MAAM;AACX,MAAAU,EAAe,WAAA,GAEf,WAAW,MAAM;AACf,QAAAT,EAAK,QAAA,GACLtB,EAAQ,UAAU,MAClBC,EAAO,UAAU,MACjBC,EAAgB,UAAU,MAAM;AAAA,QAAC;AAAA,MACnC,GAAG,CAAC;AAAA,IACN;AAAA,EACF,GAAG,CAAA,CAAE,GAELc,EAAU,MAAM;AACd,UAAMgB,IAAqB,MAAM;AAC/B,MAAAvB,EAAgB,EAAQ,SAAS,iBAAkB;AAAA,IACrD;AACA,oBAAS,iBAAiB,oBAAoBuB,CAAkB,GACzD,MACL,SAAS,oBAAoB,oBAAoBA,CAAkB;AAAA,EACvE,GAAG,CAAA,CAAE;AAEL,QAAMC,IAAmB,MAAM;AAC7B,UAAMC,IAASrC,EAAa,WAAWE,EAAU;AACjD,QAAKmC,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,SAAAlB,EAAU,MAAM;AACd,UAAMM,IAAOtB,EAAQ;AACrB,IAAKsB,MAELA,EAAK;AAAA,MACHa,gBAAAA,EAAAA;AAAAA,QAACC;AAAA,QAAA;AAAA,UACC,MAAMzB;AAAA,UACN,aAAApB;AAAA,UACA,kBAAAC;AAAA,UACA,mBAAAC;AAAA,UACA,sBAAAC;AAAA,UACA,gBAAAC;AAAA,UACA,YAAAW;AAAA,UACA,MAAAV;AAAA,QAAA;AAAA,MAAA;AAAA,IACF,GAEF,sBAAsB,MAAMM,EAAgB,WAAW;AAAA,EACzD,GAAG;AAAA,IACDd;AAAA,IACAuB;AAAA,IACApB;AAAA,IACAC;AAAA,IACAC;AAAA,IACAC;AAAA,IACAY;AAAA,IACAV;AAAA,EAAA,CACD,GAGCyC,gBAAAA,EAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,KAAKxC;AAAA,MACL,WACE;AAAA,MAGD,UAAA;AAAA,QAAA,CAACF,KACAwC,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,MAAK;AAAA,YACL,SAASF;AAAA,YACT,WACE;AAAA,YAGD,UAAAzB,IAAe,SAASd,KAAwB;AAAA,UAAA;AAAA,QAAA;AAAA,QAGpDE,MAAS,gBAAgBP,MAAS,aACjC8C,gBAAAA,EAAAA,IAACG,GAAA,EAAc,SAAAlD,GAAkB,IAEjC+C,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAKpC;AAAA,YACL,SAAQ;AAAA,YACR,OAAM;AAAA,YACN,iBAAe;AAAA,YACf,WAAuB;AAAA,YACvB,OAAO;AAAA,cACL,QAAQH,MAAS,eAAe,SAAS,GAAGO,CAAM;AAAA;AAAA;AAAA,YAAA;AAAA,UAGpD;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\";\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 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 [height, setHeight] = useState(480);\n const [resetToken, setResetToken] = useState(0);\n const [isFullscreen, setIsFullscreen] = useState(false);\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 hasRootVhHeight = React.useMemo(() => {\n const normalized = htmlContent.trim();\n if (!normalized) return false;\n const rootMatch = normalized.match(/^<([a-zA-Z][\\w:-]*)(\\s[^>]*?)?>/);\n if (!rootMatch) return false;\n const attrs = rootMatch[2] || \"\";\n const heightAttrMatch = attrs.match(/\\bheight\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (heightAttrMatch && /vh$/i.test(heightAttrMatch[1].trim())) {\n return true;\n }\n const styleAttrMatch = attrs.match(/\\bstyle\\s*=\\s*[\"']([^\"']+)[\"']/i);\n if (!styleAttrMatch) return false;\n return /height\\s*:\\s*[^;]*vh\\b/i.test(styleAttrMatch[1]);\n }, [htmlContent]);\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>\n <head>\n <meta charset=\"utf-8\" />\n <style>\n html, body { margin: 0; padding: 0; }\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 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\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 (normalized.endsWith(\"vh\")) {\n return (numeric / 100) * parentViewportHeight;\n }\n if (normalized.endsWith(\"px\") || /^[0-9.]+$/.test(normalized)) {\n return numeric;\n }\n return null;\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 if (!heightValue) return null;\n const parentViewportHeight =\n iframeRef.current.ownerDocument?.documentElement?.clientHeight ||\n window.innerHeight;\n const parsed = parseExplicitHeight(heightValue, parentViewportHeight);\n return parsed ? Math.ceil(parsed) : 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 updateHeightRef.current = updateHeight;\n\n updateHeight();\n\n const resizeObserver = new ResizeObserver(() => updateHeight());\n resizeObserver.observe(doc.body);\n if (rootEl) {\n resizeObserver.observe(rootEl);\n }\n\n return () => {\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\n return (\n <div\n ref={containerRef}\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className={\n \"w-full h-full overflow-auto relative flex flex-col justify-center content-render-iframe-sandbox\"\n }\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\"\n allow=\"fullscreen\"\n allowFullScreen\n className={(className, \"w-full\")}\n style={{\n height: mode === \"blackboard\" ? \"100%\" : `${height}px`,\n // height: `${height}px`,\n // margin: \"16px 0\",\n }}\n />\n )}\n </div>\n );\n};\n\nexport default IframeSandbox;\n"],"names":["IframeSandbox","content","type","className","loadingText","styleLoadingText","scriptLoadingText","fullScreenButtonText","hideFullScreen","mode","containerRef","useRef","iframeRef","rootRef","docRef","updateHeightRef","height","setHeight","useState","resetToken","setResetToken","isFullscreen","setIsFullscreen","prevHtmlRef","htmlContent","React","sandboxSegments","splitContentSegments","seg","hasRootVhHeight","normalized","rootMatch","attrs","heightAttrMatch","styleAttrMatch","useEffect","prev","token","iframe","doc","rootEl","root","createRoot","parseExplicitHeight","value","parentViewportHeight","numeric","resolveExplicitHeight","container","elements","target","heightValue","parsed","updateHeight","bodyRect","htmlRect","bodyHeight","htmlHeight","contentHeight","explicitHeight","nextHeight","resizeObserver","onFullscreenChange","toggleFullscreen","jsx","SandboxApp","jsxs","ContentRender"],"mappings":"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"}
@@ -1,8 +1,12 @@
1
- "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const R=require("../../_virtual/jsx-runtime.cjs.js"),t=require("react"),P=require("../../Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/lucide-react@0.525.0_react@19.0.1/node_modules/lucide-react/dist/esm/icons/loader-circle.cjs.js"),Q=({html:l,styleLoadingText:q,scriptLoadingText:B,resetToken:f=0,mode:G="content"})=>{const F=t.useRef(null),S=t.useRef(null),[,j]=t.useState(!0),[_,E]=t.useState(!1),[z,v]=t.useState(!1),b=t.useRef([]),C=t.useRef([]),A=t.useRef(0),N=t.useRef(0),p=t.useRef(null),d=t.useRef(null),m=t.useRef(!1),h=t.useRef(!1),w=t.useRef(!1),I=t.useRef(f),W=200,o=r=>{r.current&&(clearTimeout(r.current),r.current=null)},M=(r,n,c,x)=>{const i=performance.now()-c.current,g=Math.max(0,W-i);o(n),n.current=window.setTimeout(()=>{r(!1),x?.(),n.current=null},g)};t.useEffect(()=>{const r=S.current?.ownerDocument;if(!r)return;const n="sandbox-spinner-style";let c=r.getElementById(n);c||(c=r.createElement("style"),c.id=n,r.head?.appendChild(c)),c.textContent=`
1
+ "use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const b=require("../../_virtual/jsx-runtime.cjs.js"),t=require("react"),Q=require("../../Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/lucide-react@0.525.0_react@19.0.1/node_modules/lucide-react/dist/esm/icons/loader-circle.cjs.js"),J=({html:l,styleLoadingText:q,scriptLoadingText:B,resetToken:f=0,mode:G="content",hasRootVhHeight:F=!1})=>{const _=t.useRef(null),R=t.useRef(null),[,j]=t.useState(!0),[k,v]=t.useState(!1),[z,S]=t.useState(!1),E=t.useRef([]),w=t.useRef([]),N=t.useRef(0),A=t.useRef(0),d=t.useRef(null),p=t.useRef(null),m=t.useRef(!1),h=t.useRef(!1),C=t.useRef(!1),I=t.useRef(f),W=200,o=r=>{r.current&&(clearTimeout(r.current),r.current=null)},M=(r,s,a,x)=>{const i=performance.now()-a.current,g=Math.max(0,W-i);o(s),s.current=window.setTimeout(()=>{r(!1),x?.(),s.current=null},g)};t.useEffect(()=>{const r=R.current?.ownerDocument;if(!r)return;const s="sandbox-spinner-style";let a=r.getElementById(s);a||(a=r.createElement("style"),a.id=s,r.head?.appendChild(a)),a.textContent=`
2
2
  @keyframes sandbox-spin { from { transform: rotate(0deg);} to { transform: rotate(360deg);} }
3
3
  .sandbox-wrapper { align-items: center; }
4
+ .sandbox-container { width: 100%; }
5
+ .sandbox-container svg,
6
+ .sandbox-container img { display: block; margin-left: auto; margin-right: auto; }
4
7
  @media (max-width: 640px) {
5
8
  .sandbox-wrapper { align-items: stretch; }
9
+ .sandbox-wrapper[data-root-vh="true"] .sandbox-container > :first-child { height: auto !important; }
6
10
  }
7
- `},[]),t.useEffect(()=>{f!==I.current&&(w.current=!1,I.current=f),o(p),o(d),m.current=!1,h.current=!1;const r=S.current;if(!r)return;const n=r.ownerDocument,c=n?.body;if(!c)return;b.current.forEach(e=>e.remove()),b.current=[],C.current.forEach(e=>e.remove()),C.current=[];const x=w.current;j(!x),E(!1),v(!1),r.innerHTML="";const i=document.createElement("div");i.innerHTML=l;const g=(l.match(/<script[\s>]/gi)||[]).length,H=(l.match(/<\/script>/gi)||[]).length,O=g>0&&g===H,u=[];Array.from(i.querySelectorAll("style, script")).forEach(e=>{if(e.tagName.toLowerCase()==="style"){const s=n.createElement("style");s.textContent=e.textContent||"",Array.from(e.attributes).forEach(a=>{s.setAttribute(a.name,a.value)}),u.push(s)}else{const s=n.createElement("script");Array.from(e.attributes).forEach(a=>{s.setAttribute(a.name,a.value)}),s.textContent=e.textContent||"",u.push(s)}e.remove()});const L=u.some(e=>e.tagName.toLowerCase()==="style"),T=u.some(e=>e.tagName.toLowerCase()==="script");L&&(m.current=!0,A.current=performance.now(),o(p),E(!0)),T&&(h.current=!0,N.current=performance.now(),o(d),v(!0));const D=!!i.firstElementChild;j(!D&&!x),D&&(w.current=!0);const V=Array.from(i.childNodes);r.append(...V),u.forEach(e=>{if(e.tagName.toLowerCase()==="style"){n.head?.appendChild(e),b.current.push(e);return}if(O){const s=e,a=s.textContent||"";if(!s.src)try{new Function(a)}catch{s.remove();return}try{c.appendChild(s),C.current.push(s)}catch{s.remove()}}else e.remove()}),requestAnimationFrame(()=>{L&&M(E,p,A,()=>{m.current=!1}),T&&M(v,d,N,()=>{h.current=!1})})},[l,f]),t.useEffect(()=>()=>{o(p),o(d)},[]);const y=z||h.current?B||"Building scripts cache...":_||m.current?q||"Building styles...":null,k=G==="blackboard";return R.jsxRuntimeExports.jsxs("div",{ref:F,className:"sandbox-wrapper",style:{position:"relative",width:"100%",height:k?"100vh":void 0,display:"flex",flexDirection:"column",justifyContent:"space-around"},"aria-busy":!!y,children:[R.jsxRuntimeExports.jsx("div",{ref:S,style:{pointerEvents:y?"none":void 0}}),y&&R.jsxRuntimeExports.jsxs("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(51, 51, 51, 0.80)",color:"#ffffff",fontSize:16,fontWeight:700,gap:10,pointerEvents:"auto",zIndex:20},children:[R.jsxRuntimeExports.jsx(P.default,{"aria-hidden":!0,size:20,style:{animation:"sandbox-spin 1s linear infinite"}}),y]})]})};exports.default=Q;
11
+ `},[]),t.useEffect(()=>{f!==I.current&&(C.current=!1,I.current=f),o(d),o(p),m.current=!1,h.current=!1;const r=R.current;if(!r)return;const s=r.ownerDocument,a=s?.body;if(!a)return;E.current.forEach(e=>e.remove()),E.current=[],w.current.forEach(e=>e.remove()),w.current=[];const x=C.current;j(!x),v(!1),S(!1),r.innerHTML="";const i=document.createElement("div");i.innerHTML=l;const g=(l.match(/<script[\s>]/gi)||[]).length,O=(l.match(/<\/script>/gi)||[]).length,V=g>0&&g===O,u=[];Array.from(i.querySelectorAll("style, script")).forEach(e=>{if(e.tagName.toLowerCase()==="style"){const n=s.createElement("style");n.textContent=e.textContent||"",Array.from(e.attributes).forEach(c=>{n.setAttribute(c.name,c.value)}),u.push(n)}else{const n=s.createElement("script");Array.from(e.attributes).forEach(c=>{n.setAttribute(c.name,c.value)}),n.textContent=e.textContent||"",u.push(n)}e.remove()});const L=u.some(e=>e.tagName.toLowerCase()==="style"),T=u.some(e=>e.tagName.toLowerCase()==="script");L&&(m.current=!0,N.current=performance.now(),o(d),v(!0)),T&&(h.current=!0,A.current=performance.now(),o(p),S(!0));const D=!!i.firstElementChild;j(!D&&!x),D&&(C.current=!0);const P=Array.from(i.childNodes);r.append(...P),u.forEach(e=>{if(e.tagName.toLowerCase()==="style"){s.head?.appendChild(e),E.current.push(e);return}if(V){const n=e,c=n.textContent||"";if(!n.src)try{new Function(c)}catch{n.remove();return}try{a.appendChild(n),w.current.push(n)}catch{n.remove()}}else e.remove()}),requestAnimationFrame(()=>{L&&M(v,d,N,()=>{m.current=!1}),T&&M(S,p,A,()=>{h.current=!1})})},[l,f]),t.useEffect(()=>()=>{o(d),o(p)},[]);const y=z||h.current?B||"Building scripts cache...":k||m.current?q||"Building styles...":null,H=G==="blackboard";return b.jsxRuntimeExports.jsxs("div",{ref:_,"data-root-vh":F?"true":"false",className:"sandbox-wrapper",style:{position:"relative",width:"100%",height:H?"100vh":void 0,display:"flex",flexDirection:"column",justifyContent:"space-around"},"aria-busy":!!y,children:[b.jsxRuntimeExports.jsx("div",{ref:R,className:"sandbox-container",style:{pointerEvents:y?"none":void 0}}),y&&b.jsxRuntimeExports.jsxs("div",{style:{position:"absolute",inset:0,display:"flex",alignItems:"center",justifyContent:"center",background:"rgba(51, 51, 51, 0.80)",color:"#ffffff",fontSize:16,fontWeight:700,gap:10,pointerEvents:"auto",zIndex:20},children:[b.jsxRuntimeExports.jsx(Q.default,{"aria-hidden":!0,size:20,style:{animation:"sandbox-spin 1s linear infinite"}}),y]})]})};exports.default=J;
8
12
  //# sourceMappingURL=SandboxApp.cjs.js.map