markdown-flow-ui 0.1.89 → 0.1.90-beta.1

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 (61) hide show
  1. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.cjs.js +1 -1
  2. package/dist/Documents/ai-shifu/markdown-flow-ui/node_modules/.pnpm/classnames@2.5.1/node_modules/classnames/index.es.js +1 -1
  3. 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
  4. 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
  5. 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
  6. 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
  7. 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
  8. 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
  9. 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
  10. 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
  11. 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
  12. 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
  13. 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
  14. 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
  15. 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
  16. 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
  17. 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
  18. 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
  19. 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
  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/index.es.js +1 -1
  21. 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
  22. 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
  23. package/dist/_virtual/index.cjs2.js +1 -1
  24. package/dist/_virtual/index.cjs3.js +1 -1
  25. package/dist/_virtual/index.cjs7.js +1 -1
  26. package/dist/_virtual/index.cjs8.js +1 -1
  27. package/dist/_virtual/index.es2.js +5 -2
  28. package/dist/_virtual/index.es2.js.map +1 -1
  29. package/dist/_virtual/index.es3.js +2 -5
  30. package/dist/_virtual/index.es3.js.map +1 -1
  31. package/dist/_virtual/index.es7.js +2 -3
  32. package/dist/_virtual/index.es7.js.map +1 -1
  33. package/dist/_virtual/index.es8.js +3 -2
  34. package/dist/_virtual/index.es8.js.map +1 -1
  35. package/dist/components/ContentRender/ContentRender.cjs.js +4 -2
  36. package/dist/components/ContentRender/ContentRender.cjs.js.map +1 -1
  37. package/dist/components/ContentRender/ContentRender.es.js +187 -173
  38. package/dist/components/ContentRender/ContentRender.es.js.map +1 -1
  39. package/dist/components/ContentRender/IframeSandbox.cjs.js +1 -1
  40. package/dist/components/ContentRender/IframeSandbox.cjs.js.map +1 -1
  41. package/dist/components/ContentRender/IframeSandbox.es.js +1 -1
  42. package/dist/components/ContentRender/IframeSandbox.es.js.map +1 -1
  43. package/dist/components/ContentRender/SplitContent.stories.d.ts +16 -0
  44. package/dist/components/ContentRender/utils/mermaid-parse.cjs.js +5 -3
  45. package/dist/components/ContentRender/utils/mermaid-parse.cjs.js.map +1 -1
  46. package/dist/components/ContentRender/utils/mermaid-parse.es.js +83 -58
  47. package/dist/components/ContentRender/utils/mermaid-parse.es.js.map +1 -1
  48. package/dist/components/ContentRender/utils/mermaid-parse.test.d.ts +1 -0
  49. package/dist/components/ContentRender/utils/split-content.cjs.js +5 -3
  50. package/dist/components/ContentRender/utils/split-content.cjs.js.map +1 -1
  51. package/dist/components/ContentRender/utils/split-content.es.js +97 -63
  52. package/dist/components/ContentRender/utils/split-content.es.js.map +1 -1
  53. package/dist/components/ContentRender/utils/strip-svg-text-line-breaks.cjs.js +2 -0
  54. package/dist/components/ContentRender/utils/strip-svg-text-line-breaks.cjs.js.map +1 -0
  55. package/dist/components/ContentRender/utils/strip-svg-text-line-breaks.d.ts +1 -0
  56. package/dist/components/ContentRender/utils/strip-svg-text-line-breaks.es.js +10 -0
  57. package/dist/components/ContentRender/utils/strip-svg-text-line-breaks.es.js.map +1 -0
  58. package/dist/components/ui/inputGroup/textarea.cjs.js +1 -1
  59. package/dist/components/ui/inputGroup/textarea.es.js +1 -1
  60. package/dist/markdown-flow-ui-lib.css +1 -1
  61. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"IframeSandbox.cjs.js","sources":["../../../src/components/ContentRender/IframeSandbox.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport { createRoot, Root } from \"react-dom/client\";\nimport SandboxApp from \"./SandboxApp\";\nimport { splitContentSegments } from \"./utils/split-content\";\nimport ContentRender from \"./ContentRender\";\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
+ {"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, true);\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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"}
@@ -17,7 +17,7 @@ const ee = ({
17
17
  }) => {
18
18
  const N = a(null), i = a(null), v = a(null), z = a(null), y = a(() => {
19
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");
20
+ const t = Y(m, !0).filter((o) => o.type === "sandbox");
21
21
  return (s === "blackboard" ? t[t.length - 1]?.value || "" : t.map((o) => o.value).join(`
22
22
  `)) || "";
23
23
  }, [m, s]), q = S.useMemo(() => {
@@ -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 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
+ {"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, true);\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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"}
@@ -0,0 +1,16 @@
1
+ import { StoryObj } from '@storybook/nextjs-vite';
2
+ declare const meta: {
3
+ title: string;
4
+ component: import('react').FC<import('./ContentRender').ContentRenderProps>;
5
+ parameters: {
6
+ layout: string;
7
+ };
8
+ tags: string[];
9
+ args: {
10
+ content: string;
11
+ enableTypewriter: false;
12
+ };
13
+ };
14
+ export default meta;
15
+ type Story = StoryObj<typeof meta>;
16
+ export declare const SplitContentValidation: Story;
@@ -1,4 +1,6 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function h(e){const t=[],n=/```[\s\S]*?```|<svg[\s\S]*?<\/svg>/g;let s=0,i;for(;(i=n.exec(e))!==null;){const u=i.index,f=n.lastIndex,p=i[0];if(u>s&&t.push({type:"text",value:e.slice(s,u)}),p.startsWith("```mermaid")){const m=p.replace(/^```mermaid/,"").replace(/```$/,"").trim();t.push({type:"mermaid",value:m,complete:!0})}else p.startsWith("```")?t.push({type:"text",value:p}):t.push({type:"svg",value:p,complete:!0});s=f}let l=e.lastIndexOf("<svg");const c=e.lastIndexOf("</svg>");(l===-1||c!==-1&&c>l)&&(e.endsWith("<sv")?l=e.length-3:e.endsWith("<s")&&(l=e.length-2));const o=e.indexOf("```",s);if(!(o!==-1&&o<l)&&l!==-1&&(c===-1||c<l)&&l>=s)return l>s&&t.push({type:"text",value:e.slice(s,l)}),t.push({type:"svg",value:e.slice(l),complete:!1}),t;const r=e.lastIndexOf("```mermaid");if(r!==-1&&r>=s&&r===o){const u=e.slice(r+10);return t.push({type:"mermaid",value:u.trim(),complete:!1}),r>s&&t.push({type:"text",value:e.slice(s,r)}),t}return s<e.length&&t.push({type:"text",value:e.slice(s)}),t}function a(e){const t=[],n=e.split(/\r?\n/);let s=!1,i=[],l=0;return n.forEach((c,o)=>{if(!s){c.trim().startsWith("```mermaid")&&(s=!0,l=o,i=[]);return}if(c.trim()==="```"){t.push({code:i.join(`
2
- `).trim(),startLine:l,endLine:o,complete:!0}),s=!1;return}i.push(c)}),s&&t.push({code:i.join(`
3
- `).trim(),startLine:l,endLine:null,complete:!1}),t}function d(e,t){const n=t.trim(),i=a(e).find(l=>l.code===n);return i?i.complete:!1}exports.mermaidBlockIsComplete=d;exports.parseMarkdownSegments=h;exports.parseMermaidBlocks=a;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});function C(e){const t=[],p=/```[\s\S]*?```|<svg[\s\S]*?<\/svg>/g,r=n=>{const o=e.lastIndexOf(`
2
+ `,n-1)+1,l=e.indexOf(`
3
+ `,n),u=l===-1?e.length:l;return{lineStart:o,lineEnd:u}},c=n=>{const{lineStart:o,lineEnd:l}=r(n);return e.slice(o,l).trimStart().startsWith("|")},a=n=>{const{lineStart:o,lineEnd:l}=r(n),u=e.slice(o,l),B=n-o;let d=0,v=!1,x=0;for(;d<B;){if(u[d]!=="`"){d+=1;continue}let g=d;for(;g<u.length&&u[g]==="`";)g+=1;const S=g-d;v?S===x&&(v=!1,x=0):(v=!0,x=S),d=g}return v};let s=0,f;for(;(f=p.exec(e))!==null;){const n=f.index,o=p.lastIndex,l=f[0];if(!(l.startsWith("<svg")&&(c(n)||a(n)))){if(n>s&&t.push({type:"text",value:e.slice(s,n)}),l.startsWith("```mermaid")){const u=l.replace(/^```mermaid/,"").replace(/```$/,"").trim();t.push({type:"mermaid",value:u,complete:!0})}else l.startsWith("```")?t.push({type:"text",value:l}):t.push({type:"svg",value:l,complete:!0});s=o}}let i=e.lastIndexOf("<svg");const m=e.lastIndexOf("</svg>");(i===-1||m!==-1&&m>i)&&(e.endsWith("<sv")?i=e.length-3:e.endsWith("<s")&&(i=e.length-2));const I=e.indexOf("```",s);if(!(I!==-1&&I<i)&&i!==-1&&(m===-1||m<i)&&i>=s)return i>s&&t.push({type:"text",value:e.slice(s,i)}),t.push({type:"svg",value:e.slice(i),complete:!1}),t;const h=e.lastIndexOf("```mermaid");if(h!==-1&&h>=s&&h===I){const n=e.slice(h+10);return t.push({type:"mermaid",value:n.trim(),complete:!1}),h>s&&t.push({type:"text",value:e.slice(s,h)}),t}return s<e.length&&t.push({type:"text",value:e.slice(s)}),t}function y(e){const t=[],p=e.split(/\r?\n/);let r=!1,c=[],a=0;return p.forEach((s,f)=>{if(!r){s.trim().startsWith("```mermaid")&&(r=!0,a=f,c=[]);return}if(s.trim()==="```"){t.push({code:c.join(`
4
+ `).trim(),startLine:a,endLine:f,complete:!0}),r=!1;return}c.push(s)}),r&&t.push({code:c.join(`
5
+ `).trim(),startLine:a,endLine:null,complete:!1}),t}function b(e,t){const p=t.trim(),c=y(e).find(a=>a.code===p);return c?c.complete:!1}exports.mermaidBlockIsComplete=b;exports.parseMarkdownSegments=C;exports.parseMermaidBlocks=y;
4
6
  //# sourceMappingURL=mermaid-parse.cjs.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mermaid-parse.cjs.js","sources":["../../../../src/components/ContentRender/utils/mermaid-parse.ts"],"sourcesContent":["export function parseMarkdownSegments(markdown: string) {\n const segments: Array<\n | { type: \"text\"; value: string }\n | { type: \"mermaid\"; value: string; complete: boolean }\n | { type: \"svg\"; value: string; complete: boolean }\n > = [];\n\n // Match:\n // 1. Generic code blocks (including mermaid): ``` ... ```\n // 2. SVG blocks: <svg ... </svg>\n const regex = /```[\\s\\S]*?```|<svg[\\s\\S]*?<\\/svg>/g;\n\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(markdown)) !== null) {\n const start = match.index;\n const end = regex.lastIndex;\n const rawMatch = match[0];\n\n // Preceding plain text\n if (start > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, start),\n });\n }\n\n // Complete mermaid block, generic code block, or svg block\n if (rawMatch.startsWith(\"```mermaid\")) {\n const code = rawMatch\n .replace(/^```mermaid/, \"\")\n .replace(/```$/, \"\")\n .trim();\n segments.push({\n type: \"mermaid\",\n value: code,\n complete: true,\n });\n } else if (rawMatch.startsWith(\"```\")) {\n // Generic code block - treat as text so ReactMarkdown renders it\n segments.push({\n type: \"text\",\n value: rawMatch,\n });\n } else {\n // SVG block\n segments.push({\n type: \"svg\",\n value: rawMatch,\n complete: true,\n });\n }\n\n lastIndex = end;\n }\n\n // Handle unfinished svg block to avoid leaking raw tags while streaming\n let incompleteSvgStart = markdown.lastIndexOf(\"<svg\");\n const lastSvgClose = markdown.lastIndexOf(\"</svg>\");\n\n // If we haven't found an open <svg tag (or the last one is closed),\n // check if the string ends with a partial <svg tag (<s, <sv).\n if (\n incompleteSvgStart === -1 ||\n (lastSvgClose !== -1 && lastSvgClose > incompleteSvgStart)\n ) {\n if (markdown.endsWith(\"<sv\")) {\n incompleteSvgStart = markdown.length - 3;\n } else if (markdown.endsWith(\"<s\")) {\n incompleteSvgStart = markdown.length - 2;\n }\n }\n\n // Check if we are inside an unclosed code block\n // If an unclosed code block starts AFTER the last complete segment (lastIndex),\n // and BEFORE the potential SVG start, then the SVG is inside the code block.\n const incompleteCodeBlockStart = markdown.indexOf(\"```\", lastIndex);\n const isInsideCodeBlock =\n incompleteCodeBlockStart !== -1 &&\n incompleteCodeBlockStart < incompleteSvgStart;\n\n const hasIncompleteSvg =\n !isInsideCodeBlock &&\n incompleteSvgStart !== -1 &&\n (lastSvgClose === -1 || lastSvgClose < incompleteSvgStart) &&\n incompleteSvgStart >= lastIndex;\n\n if (hasIncompleteSvg) {\n if (incompleteSvgStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteSvgStart),\n });\n }\n\n segments.push({\n type: \"svg\",\n value: markdown.slice(incompleteSvgStart),\n complete: false,\n });\n return segments;\n }\n\n // Check whether there is an unfinished mermaid block\n // Only if we are NOT inside a generic incomplete code block that started earlier\n // Actually, standard mermaid block starts with ```mermaid, so it IS a code block start.\n // We just need to check if it's specifically mermaid.\n const incompleteStart = markdown.lastIndexOf(\"```mermaid\");\n if (\n incompleteStart !== -1 &&\n incompleteStart >= lastIndex &&\n // Ensure this mermaid block isn't inside another code block (unlikely but safe to check)\n // Actually, incompleteCodeBlockStart would capture this \"```mermaid\" as just \"```\"\n // so we need to be careful.\n // If incompleteCodeBlockStart points to THIS mermaid block, we process it as mermaid.\n incompleteStart === incompleteCodeBlockStart\n ) {\n const code = markdown.slice(incompleteStart + 10);\n segments.push({\n type: \"mermaid\",\n value: code.trim(),\n complete: false,\n });\n\n if (incompleteStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteStart),\n });\n }\n\n return segments;\n }\n\n // Remaining text\n if (lastIndex < markdown.length) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex),\n });\n }\n\n return segments;\n}\n\nexport function parseMermaidBlocks(fullMarkdown: string) {\n const blocks = [];\n const lines: string[] = fullMarkdown.split(/\\r?\\n/);\n\n let inside = false;\n let current: string[] = [];\n let startLine = 0;\n\n lines.forEach((line, index) => {\n if (!inside) {\n if (line.trim().startsWith(\"```mermaid\")) {\n inside = true;\n startLine = index;\n current = [];\n }\n return;\n }\n\n // inside mermaid mode\n if (line.trim() === \"```\") {\n // block complete\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: index,\n complete: true,\n });\n inside = false;\n return;\n }\n\n current.push(line);\n });\n\n // if still inside → incomplete block\n if (inside) {\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: null,\n complete: false,\n });\n }\n\n return blocks;\n}\n\n/**\n * Determine whether the mermaid block for the given codeString is fully closed.\n * @param fullMarkdown Full markdown text (grows over time in streaming scenarios)\n * @param codeString Mermaid block code passed to the renderer (children)\n */\nexport function mermaidBlockIsComplete(\n fullMarkdown: string,\n codeString: string\n) {\n const cleaned = codeString.trim();\n const blocks = parseMermaidBlocks(fullMarkdown);\n // Locate the block that matches the current codeString\n const block = blocks.find((b) => b.code === cleaned);\n\n if (!block) return false;\n\n // Block is complete when the closing fence already exists\n return block.complete;\n}\n"],"names":["parseMarkdownSegments","markdown","segments","regex","lastIndex","match","start","end","rawMatch","code","incompleteSvgStart","lastSvgClose","incompleteCodeBlockStart","incompleteStart","parseMermaidBlocks","fullMarkdown","blocks","lines","inside","current","startLine","line","index","mermaidBlockIsComplete","codeString","cleaned","block","b"],"mappings":"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"}
1
+ {"version":3,"file":"mermaid-parse.cjs.js","sources":["../../../../src/components/ContentRender/utils/mermaid-parse.ts"],"sourcesContent":["export function parseMarkdownSegments(markdown: string) {\n const segments: Array<\n | { type: \"text\"; value: string }\n | { type: \"mermaid\"; value: string; complete: boolean }\n | { type: \"svg\"; value: string; complete: boolean }\n > = [];\n\n // Match:\n // 1. Generic code blocks (including mermaid): ``` ... ```\n // 2. SVG blocks: <svg ... </svg>\n const regex = /```[\\s\\S]*?```|<svg[\\s\\S]*?<\\/svg>/g;\n\n const getLineRange = (index: number) => {\n const lineStart = markdown.lastIndexOf(\"\\n\", index - 1) + 1;\n const nextBreak = markdown.indexOf(\"\\n\", index);\n const lineEnd = nextBreak === -1 ? markdown.length : nextBreak;\n return { lineStart, lineEnd };\n };\n\n const isIndexInsideMarkdownTableLine = (index: number) => {\n const { lineStart, lineEnd } = getLineRange(index);\n const line = markdown.slice(lineStart, lineEnd).trimStart();\n return line.startsWith(\"|\");\n };\n\n const isIndexInsideInlineCode = (index: number) => {\n const { lineStart, lineEnd } = getLineRange(index);\n const line = markdown.slice(lineStart, lineEnd);\n const relativeIndex = index - lineStart;\n let cursor = 0;\n let inCode = false;\n let delimiterLength = 0;\n\n while (cursor < relativeIndex) {\n if (line[cursor] !== \"`\") {\n cursor += 1;\n continue;\n }\n\n let runEnd = cursor;\n while (runEnd < line.length && line[runEnd] === \"`\") {\n runEnd += 1;\n }\n const runLength = runEnd - cursor;\n\n if (!inCode) {\n inCode = true;\n delimiterLength = runLength;\n } else if (runLength === delimiterLength) {\n inCode = false;\n delimiterLength = 0;\n }\n\n cursor = runEnd;\n }\n\n return inCode;\n };\n\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(markdown)) !== null) {\n const start = match.index;\n const end = regex.lastIndex;\n const rawMatch = match[0];\n\n if (\n rawMatch.startsWith(\"<svg\") &&\n (isIndexInsideMarkdownTableLine(start) || isIndexInsideInlineCode(start))\n ) {\n continue;\n }\n\n // Preceding plain text\n if (start > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, start),\n });\n }\n\n // Complete mermaid block, generic code block, or svg block\n if (rawMatch.startsWith(\"```mermaid\")) {\n const code = rawMatch\n .replace(/^```mermaid/, \"\")\n .replace(/```$/, \"\")\n .trim();\n segments.push({\n type: \"mermaid\",\n value: code,\n complete: true,\n });\n } else if (rawMatch.startsWith(\"```\")) {\n // Generic code block - treat as text so ReactMarkdown renders it\n segments.push({\n type: \"text\",\n value: rawMatch,\n });\n } else {\n // SVG block\n segments.push({\n type: \"svg\",\n value: rawMatch,\n complete: true,\n });\n }\n\n lastIndex = end;\n }\n\n // Handle unfinished svg block to avoid leaking raw tags while streaming\n let incompleteSvgStart = markdown.lastIndexOf(\"<svg\");\n const lastSvgClose = markdown.lastIndexOf(\"</svg>\");\n\n // If we haven't found an open <svg tag (or the last one is closed),\n // check if the string ends with a partial <svg tag (<s, <sv).\n if (\n incompleteSvgStart === -1 ||\n (lastSvgClose !== -1 && lastSvgClose > incompleteSvgStart)\n ) {\n if (markdown.endsWith(\"<sv\")) {\n incompleteSvgStart = markdown.length - 3;\n } else if (markdown.endsWith(\"<s\")) {\n incompleteSvgStart = markdown.length - 2;\n }\n }\n\n // Check if we are inside an unclosed code block\n // If an unclosed code block starts AFTER the last complete segment (lastIndex),\n // and BEFORE the potential SVG start, then the SVG is inside the code block.\n const incompleteCodeBlockStart = markdown.indexOf(\"```\", lastIndex);\n const isInsideCodeBlock =\n incompleteCodeBlockStart !== -1 &&\n incompleteCodeBlockStart < incompleteSvgStart;\n\n const hasIncompleteSvg =\n !isInsideCodeBlock &&\n incompleteSvgStart !== -1 &&\n (lastSvgClose === -1 || lastSvgClose < incompleteSvgStart) &&\n incompleteSvgStart >= lastIndex;\n\n if (hasIncompleteSvg) {\n if (incompleteSvgStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteSvgStart),\n });\n }\n\n segments.push({\n type: \"svg\",\n value: markdown.slice(incompleteSvgStart),\n complete: false,\n });\n return segments;\n }\n\n // Check whether there is an unfinished mermaid block\n // Only if we are NOT inside a generic incomplete code block that started earlier\n // Actually, standard mermaid block starts with ```mermaid, so it IS a code block start.\n // We just need to check if it's specifically mermaid.\n const incompleteStart = markdown.lastIndexOf(\"```mermaid\");\n if (\n incompleteStart !== -1 &&\n incompleteStart >= lastIndex &&\n // Ensure this mermaid block isn't inside another code block (unlikely but safe to check)\n // Actually, incompleteCodeBlockStart would capture this \"```mermaid\" as just \"```\"\n // so we need to be careful.\n // If incompleteCodeBlockStart points to THIS mermaid block, we process it as mermaid.\n incompleteStart === incompleteCodeBlockStart\n ) {\n const code = markdown.slice(incompleteStart + 10);\n segments.push({\n type: \"mermaid\",\n value: code.trim(),\n complete: false,\n });\n\n if (incompleteStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteStart),\n });\n }\n\n return segments;\n }\n\n // Remaining text\n if (lastIndex < markdown.length) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex),\n });\n }\n\n return segments;\n}\n\nexport function parseMermaidBlocks(fullMarkdown: string) {\n const blocks = [];\n const lines: string[] = fullMarkdown.split(/\\r?\\n/);\n\n let inside = false;\n let current: string[] = [];\n let startLine = 0;\n\n lines.forEach((line, index) => {\n if (!inside) {\n if (line.trim().startsWith(\"```mermaid\")) {\n inside = true;\n startLine = index;\n current = [];\n }\n return;\n }\n\n // inside mermaid mode\n if (line.trim() === \"```\") {\n // block complete\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: index,\n complete: true,\n });\n inside = false;\n return;\n }\n\n current.push(line);\n });\n\n // if still inside → incomplete block\n if (inside) {\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: null,\n complete: false,\n });\n }\n\n return blocks;\n}\n\n/**\n * Determine whether the mermaid block for the given codeString is fully closed.\n * @param fullMarkdown Full markdown text (grows over time in streaming scenarios)\n * @param codeString Mermaid block code passed to the renderer (children)\n */\nexport function mermaidBlockIsComplete(\n fullMarkdown: string,\n codeString: string\n) {\n const cleaned = codeString.trim();\n const blocks = parseMermaidBlocks(fullMarkdown);\n // Locate the block that matches the current codeString\n const block = blocks.find((b) => b.code === cleaned);\n\n if (!block) return false;\n\n // Block is complete when the closing fence already exists\n return block.complete;\n}\n"],"names":["parseMarkdownSegments","markdown","segments","regex","getLineRange","index","lineStart","nextBreak","lineEnd","isIndexInsideMarkdownTableLine","isIndexInsideInlineCode","line","relativeIndex","cursor","inCode","delimiterLength","runEnd","runLength","lastIndex","match","start","end","rawMatch","code","incompleteSvgStart","lastSvgClose","incompleteCodeBlockStart","incompleteStart","parseMermaidBlocks","fullMarkdown","blocks","lines","inside","current","startLine","mermaidBlockIsComplete","codeString","cleaned","block","b"],"mappings":"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"}
@@ -1,55 +1,80 @@
1
- function d(e) {
2
- const t = [], n = /```[\s\S]*?```|<svg[\s\S]*?<\/svg>/g;
3
- let s = 0, i;
4
- for (; (i = n.exec(e)) !== null; ) {
5
- const u = i.index, a = n.lastIndex, r = i[0];
6
- if (u > s && t.push({
7
- type: "text",
8
- value: e.slice(s, u)
9
- }), r.startsWith("```mermaid")) {
10
- const f = r.replace(/^```mermaid/, "").replace(/```$/, "").trim();
11
- t.push({
12
- type: "mermaid",
13
- value: f,
1
+ function B(e) {
2
+ const t = [], p = /```[\s\S]*?```|<svg[\s\S]*?<\/svg>/g, r = (n) => {
3
+ const o = e.lastIndexOf(`
4
+ `, n - 1) + 1, l = e.indexOf(`
5
+ `, n), u = l === -1 ? e.length : l;
6
+ return { lineStart: o, lineEnd: u };
7
+ }, c = (n) => {
8
+ const { lineStart: o, lineEnd: l } = r(n);
9
+ return e.slice(o, l).trimStart().startsWith("|");
10
+ }, a = (n) => {
11
+ const { lineStart: o, lineEnd: l } = r(n), u = e.slice(o, l), y = n - o;
12
+ let d = 0, m = !1, I = 0;
13
+ for (; d < y; ) {
14
+ if (u[d] !== "`") {
15
+ d += 1;
16
+ continue;
17
+ }
18
+ let g = d;
19
+ for (; g < u.length && u[g] === "`"; )
20
+ g += 1;
21
+ const S = g - d;
22
+ m ? S === I && (m = !1, I = 0) : (m = !0, I = S), d = g;
23
+ }
24
+ return m;
25
+ };
26
+ let s = 0, f;
27
+ for (; (f = p.exec(e)) !== null; ) {
28
+ const n = f.index, o = p.lastIndex, l = f[0];
29
+ if (!(l.startsWith("<svg") && (c(n) || a(n)))) {
30
+ if (n > s && t.push({
31
+ type: "text",
32
+ value: e.slice(s, n)
33
+ }), l.startsWith("```mermaid")) {
34
+ const u = l.replace(/^```mermaid/, "").replace(/```$/, "").trim();
35
+ t.push({
36
+ type: "mermaid",
37
+ value: u,
38
+ complete: !0
39
+ });
40
+ } else l.startsWith("```") ? t.push({
41
+ type: "text",
42
+ value: l
43
+ }) : t.push({
44
+ type: "svg",
45
+ value: l,
14
46
  complete: !0
15
47
  });
16
- } else r.startsWith("```") ? t.push({
17
- type: "text",
18
- value: r
19
- }) : t.push({
20
- type: "svg",
21
- value: r,
22
- complete: !0
23
- });
24
- s = a;
48
+ s = o;
49
+ }
25
50
  }
26
- let l = e.lastIndexOf("<svg");
27
- const c = e.lastIndexOf("</svg>");
28
- (l === -1 || c !== -1 && c > l) && (e.endsWith("<sv") ? l = e.length - 3 : e.endsWith("<s") && (l = e.length - 2));
29
- const o = e.indexOf("```", s);
30
- if (!(o !== -1 && o < l) && l !== -1 && (c === -1 || c < l) && l >= s)
31
- return l > s && t.push({
51
+ let i = e.lastIndexOf("<svg");
52
+ const v = e.lastIndexOf("</svg>");
53
+ (i === -1 || v !== -1 && v > i) && (e.endsWith("<sv") ? i = e.length - 3 : e.endsWith("<s") && (i = e.length - 2));
54
+ const x = e.indexOf("```", s);
55
+ if (!(x !== -1 && x < i) && i !== -1 && (v === -1 || v < i) && i >= s)
56
+ return i > s && t.push({
32
57
  type: "text",
33
- value: e.slice(s, l)
58
+ value: e.slice(s, i)
34
59
  }), t.push({
35
60
  type: "svg",
36
- value: e.slice(l),
61
+ value: e.slice(i),
37
62
  complete: !1
38
63
  }), t;
39
- const p = e.lastIndexOf("```mermaid");
40
- if (p !== -1 && p >= s && // Ensure this mermaid block isn't inside another code block (unlikely but safe to check)
64
+ const h = e.lastIndexOf("```mermaid");
65
+ if (h !== -1 && h >= s && // Ensure this mermaid block isn't inside another code block (unlikely but safe to check)
41
66
  // Actually, incompleteCodeBlockStart would capture this "```mermaid" as just "```"
42
67
  // so we need to be careful.
43
68
  // If incompleteCodeBlockStart points to THIS mermaid block, we process it as mermaid.
44
- p === o) {
45
- const u = e.slice(p + 10);
69
+ h === x) {
70
+ const n = e.slice(h + 10);
46
71
  return t.push({
47
72
  type: "mermaid",
48
- value: u.trim(),
73
+ value: n.trim(),
49
74
  complete: !1
50
- }), p > s && t.push({
75
+ }), h > s && t.push({
51
76
  type: "text",
52
- value: e.slice(s, p)
77
+ value: e.slice(s, h)
53
78
  }), t;
54
79
  }
55
80
  return s < e.length && t.push({
@@ -57,40 +82,40 @@ function d(e) {
57
82
  value: e.slice(s)
58
83
  }), t;
59
84
  }
60
- function h(e) {
61
- const t = [], n = e.split(/\r?\n/);
62
- let s = !1, i = [], l = 0;
63
- return n.forEach((c, o) => {
64
- if (!s) {
65
- c.trim().startsWith("```mermaid") && (s = !0, l = o, i = []);
85
+ function C(e) {
86
+ const t = [], p = e.split(/\r?\n/);
87
+ let r = !1, c = [], a = 0;
88
+ return p.forEach((s, f) => {
89
+ if (!r) {
90
+ s.trim().startsWith("```mermaid") && (r = !0, a = f, c = []);
66
91
  return;
67
92
  }
68
- if (c.trim() === "```") {
93
+ if (s.trim() === "```") {
69
94
  t.push({
70
- code: i.join(`
95
+ code: c.join(`
71
96
  `).trim(),
72
- startLine: l,
73
- endLine: o,
97
+ startLine: a,
98
+ endLine: f,
74
99
  complete: !0
75
- }), s = !1;
100
+ }), r = !1;
76
101
  return;
77
102
  }
78
- i.push(c);
79
- }), s && t.push({
80
- code: i.join(`
103
+ c.push(s);
104
+ }), r && t.push({
105
+ code: c.join(`
81
106
  `).trim(),
82
- startLine: l,
107
+ startLine: a,
83
108
  endLine: null,
84
109
  complete: !1
85
110
  }), t;
86
111
  }
87
- function g(e, t) {
88
- const n = t.trim(), i = h(e).find((l) => l.code === n);
89
- return i ? i.complete : !1;
112
+ function O(e, t) {
113
+ const p = t.trim(), c = C(e).find((a) => a.code === p);
114
+ return c ? c.complete : !1;
90
115
  }
91
116
  export {
92
- g as mermaidBlockIsComplete,
93
- d as parseMarkdownSegments,
94
- h as parseMermaidBlocks
117
+ O as mermaidBlockIsComplete,
118
+ B as parseMarkdownSegments,
119
+ C as parseMermaidBlocks
95
120
  };
96
121
  //# sourceMappingURL=mermaid-parse.es.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"mermaid-parse.es.js","sources":["../../../../src/components/ContentRender/utils/mermaid-parse.ts"],"sourcesContent":["export function parseMarkdownSegments(markdown: string) {\n const segments: Array<\n | { type: \"text\"; value: string }\n | { type: \"mermaid\"; value: string; complete: boolean }\n | { type: \"svg\"; value: string; complete: boolean }\n > = [];\n\n // Match:\n // 1. Generic code blocks (including mermaid): ``` ... ```\n // 2. SVG blocks: <svg ... </svg>\n const regex = /```[\\s\\S]*?```|<svg[\\s\\S]*?<\\/svg>/g;\n\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(markdown)) !== null) {\n const start = match.index;\n const end = regex.lastIndex;\n const rawMatch = match[0];\n\n // Preceding plain text\n if (start > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, start),\n });\n }\n\n // Complete mermaid block, generic code block, or svg block\n if (rawMatch.startsWith(\"```mermaid\")) {\n const code = rawMatch\n .replace(/^```mermaid/, \"\")\n .replace(/```$/, \"\")\n .trim();\n segments.push({\n type: \"mermaid\",\n value: code,\n complete: true,\n });\n } else if (rawMatch.startsWith(\"```\")) {\n // Generic code block - treat as text so ReactMarkdown renders it\n segments.push({\n type: \"text\",\n value: rawMatch,\n });\n } else {\n // SVG block\n segments.push({\n type: \"svg\",\n value: rawMatch,\n complete: true,\n });\n }\n\n lastIndex = end;\n }\n\n // Handle unfinished svg block to avoid leaking raw tags while streaming\n let incompleteSvgStart = markdown.lastIndexOf(\"<svg\");\n const lastSvgClose = markdown.lastIndexOf(\"</svg>\");\n\n // If we haven't found an open <svg tag (or the last one is closed),\n // check if the string ends with a partial <svg tag (<s, <sv).\n if (\n incompleteSvgStart === -1 ||\n (lastSvgClose !== -1 && lastSvgClose > incompleteSvgStart)\n ) {\n if (markdown.endsWith(\"<sv\")) {\n incompleteSvgStart = markdown.length - 3;\n } else if (markdown.endsWith(\"<s\")) {\n incompleteSvgStart = markdown.length - 2;\n }\n }\n\n // Check if we are inside an unclosed code block\n // If an unclosed code block starts AFTER the last complete segment (lastIndex),\n // and BEFORE the potential SVG start, then the SVG is inside the code block.\n const incompleteCodeBlockStart = markdown.indexOf(\"```\", lastIndex);\n const isInsideCodeBlock =\n incompleteCodeBlockStart !== -1 &&\n incompleteCodeBlockStart < incompleteSvgStart;\n\n const hasIncompleteSvg =\n !isInsideCodeBlock &&\n incompleteSvgStart !== -1 &&\n (lastSvgClose === -1 || lastSvgClose < incompleteSvgStart) &&\n incompleteSvgStart >= lastIndex;\n\n if (hasIncompleteSvg) {\n if (incompleteSvgStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteSvgStart),\n });\n }\n\n segments.push({\n type: \"svg\",\n value: markdown.slice(incompleteSvgStart),\n complete: false,\n });\n return segments;\n }\n\n // Check whether there is an unfinished mermaid block\n // Only if we are NOT inside a generic incomplete code block that started earlier\n // Actually, standard mermaid block starts with ```mermaid, so it IS a code block start.\n // We just need to check if it's specifically mermaid.\n const incompleteStart = markdown.lastIndexOf(\"```mermaid\");\n if (\n incompleteStart !== -1 &&\n incompleteStart >= lastIndex &&\n // Ensure this mermaid block isn't inside another code block (unlikely but safe to check)\n // Actually, incompleteCodeBlockStart would capture this \"```mermaid\" as just \"```\"\n // so we need to be careful.\n // If incompleteCodeBlockStart points to THIS mermaid block, we process it as mermaid.\n incompleteStart === incompleteCodeBlockStart\n ) {\n const code = markdown.slice(incompleteStart + 10);\n segments.push({\n type: \"mermaid\",\n value: code.trim(),\n complete: false,\n });\n\n if (incompleteStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteStart),\n });\n }\n\n return segments;\n }\n\n // Remaining text\n if (lastIndex < markdown.length) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex),\n });\n }\n\n return segments;\n}\n\nexport function parseMermaidBlocks(fullMarkdown: string) {\n const blocks = [];\n const lines: string[] = fullMarkdown.split(/\\r?\\n/);\n\n let inside = false;\n let current: string[] = [];\n let startLine = 0;\n\n lines.forEach((line, index) => {\n if (!inside) {\n if (line.trim().startsWith(\"```mermaid\")) {\n inside = true;\n startLine = index;\n current = [];\n }\n return;\n }\n\n // inside mermaid mode\n if (line.trim() === \"```\") {\n // block complete\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: index,\n complete: true,\n });\n inside = false;\n return;\n }\n\n current.push(line);\n });\n\n // if still inside → incomplete block\n if (inside) {\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: null,\n complete: false,\n });\n }\n\n return blocks;\n}\n\n/**\n * Determine whether the mermaid block for the given codeString is fully closed.\n * @param fullMarkdown Full markdown text (grows over time in streaming scenarios)\n * @param codeString Mermaid block code passed to the renderer (children)\n */\nexport function mermaidBlockIsComplete(\n fullMarkdown: string,\n codeString: string\n) {\n const cleaned = codeString.trim();\n const blocks = parseMermaidBlocks(fullMarkdown);\n // Locate the block that matches the current codeString\n const block = blocks.find((b) => b.code === cleaned);\n\n if (!block) return false;\n\n // Block is complete when the closing fence already exists\n return block.complete;\n}\n"],"names":["parseMarkdownSegments","markdown","segments","regex","lastIndex","match","start","end","rawMatch","code","incompleteSvgStart","lastSvgClose","incompleteCodeBlockStart","incompleteStart","parseMermaidBlocks","fullMarkdown","blocks","lines","inside","current","startLine","line","index","mermaidBlockIsComplete","codeString","cleaned","block","b"],"mappings":"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"}
1
+ {"version":3,"file":"mermaid-parse.es.js","sources":["../../../../src/components/ContentRender/utils/mermaid-parse.ts"],"sourcesContent":["export function parseMarkdownSegments(markdown: string) {\n const segments: Array<\n | { type: \"text\"; value: string }\n | { type: \"mermaid\"; value: string; complete: boolean }\n | { type: \"svg\"; value: string; complete: boolean }\n > = [];\n\n // Match:\n // 1. Generic code blocks (including mermaid): ``` ... ```\n // 2. SVG blocks: <svg ... </svg>\n const regex = /```[\\s\\S]*?```|<svg[\\s\\S]*?<\\/svg>/g;\n\n const getLineRange = (index: number) => {\n const lineStart = markdown.lastIndexOf(\"\\n\", index - 1) + 1;\n const nextBreak = markdown.indexOf(\"\\n\", index);\n const lineEnd = nextBreak === -1 ? markdown.length : nextBreak;\n return { lineStart, lineEnd };\n };\n\n const isIndexInsideMarkdownTableLine = (index: number) => {\n const { lineStart, lineEnd } = getLineRange(index);\n const line = markdown.slice(lineStart, lineEnd).trimStart();\n return line.startsWith(\"|\");\n };\n\n const isIndexInsideInlineCode = (index: number) => {\n const { lineStart, lineEnd } = getLineRange(index);\n const line = markdown.slice(lineStart, lineEnd);\n const relativeIndex = index - lineStart;\n let cursor = 0;\n let inCode = false;\n let delimiterLength = 0;\n\n while (cursor < relativeIndex) {\n if (line[cursor] !== \"`\") {\n cursor += 1;\n continue;\n }\n\n let runEnd = cursor;\n while (runEnd < line.length && line[runEnd] === \"`\") {\n runEnd += 1;\n }\n const runLength = runEnd - cursor;\n\n if (!inCode) {\n inCode = true;\n delimiterLength = runLength;\n } else if (runLength === delimiterLength) {\n inCode = false;\n delimiterLength = 0;\n }\n\n cursor = runEnd;\n }\n\n return inCode;\n };\n\n let lastIndex = 0;\n let match;\n\n while ((match = regex.exec(markdown)) !== null) {\n const start = match.index;\n const end = regex.lastIndex;\n const rawMatch = match[0];\n\n if (\n rawMatch.startsWith(\"<svg\") &&\n (isIndexInsideMarkdownTableLine(start) || isIndexInsideInlineCode(start))\n ) {\n continue;\n }\n\n // Preceding plain text\n if (start > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, start),\n });\n }\n\n // Complete mermaid block, generic code block, or svg block\n if (rawMatch.startsWith(\"```mermaid\")) {\n const code = rawMatch\n .replace(/^```mermaid/, \"\")\n .replace(/```$/, \"\")\n .trim();\n segments.push({\n type: \"mermaid\",\n value: code,\n complete: true,\n });\n } else if (rawMatch.startsWith(\"```\")) {\n // Generic code block - treat as text so ReactMarkdown renders it\n segments.push({\n type: \"text\",\n value: rawMatch,\n });\n } else {\n // SVG block\n segments.push({\n type: \"svg\",\n value: rawMatch,\n complete: true,\n });\n }\n\n lastIndex = end;\n }\n\n // Handle unfinished svg block to avoid leaking raw tags while streaming\n let incompleteSvgStart = markdown.lastIndexOf(\"<svg\");\n const lastSvgClose = markdown.lastIndexOf(\"</svg>\");\n\n // If we haven't found an open <svg tag (or the last one is closed),\n // check if the string ends with a partial <svg tag (<s, <sv).\n if (\n incompleteSvgStart === -1 ||\n (lastSvgClose !== -1 && lastSvgClose > incompleteSvgStart)\n ) {\n if (markdown.endsWith(\"<sv\")) {\n incompleteSvgStart = markdown.length - 3;\n } else if (markdown.endsWith(\"<s\")) {\n incompleteSvgStart = markdown.length - 2;\n }\n }\n\n // Check if we are inside an unclosed code block\n // If an unclosed code block starts AFTER the last complete segment (lastIndex),\n // and BEFORE the potential SVG start, then the SVG is inside the code block.\n const incompleteCodeBlockStart = markdown.indexOf(\"```\", lastIndex);\n const isInsideCodeBlock =\n incompleteCodeBlockStart !== -1 &&\n incompleteCodeBlockStart < incompleteSvgStart;\n\n const hasIncompleteSvg =\n !isInsideCodeBlock &&\n incompleteSvgStart !== -1 &&\n (lastSvgClose === -1 || lastSvgClose < incompleteSvgStart) &&\n incompleteSvgStart >= lastIndex;\n\n if (hasIncompleteSvg) {\n if (incompleteSvgStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteSvgStart),\n });\n }\n\n segments.push({\n type: \"svg\",\n value: markdown.slice(incompleteSvgStart),\n complete: false,\n });\n return segments;\n }\n\n // Check whether there is an unfinished mermaid block\n // Only if we are NOT inside a generic incomplete code block that started earlier\n // Actually, standard mermaid block starts with ```mermaid, so it IS a code block start.\n // We just need to check if it's specifically mermaid.\n const incompleteStart = markdown.lastIndexOf(\"```mermaid\");\n if (\n incompleteStart !== -1 &&\n incompleteStart >= lastIndex &&\n // Ensure this mermaid block isn't inside another code block (unlikely but safe to check)\n // Actually, incompleteCodeBlockStart would capture this \"```mermaid\" as just \"```\"\n // so we need to be careful.\n // If incompleteCodeBlockStart points to THIS mermaid block, we process it as mermaid.\n incompleteStart === incompleteCodeBlockStart\n ) {\n const code = markdown.slice(incompleteStart + 10);\n segments.push({\n type: \"mermaid\",\n value: code.trim(),\n complete: false,\n });\n\n if (incompleteStart > lastIndex) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex, incompleteStart),\n });\n }\n\n return segments;\n }\n\n // Remaining text\n if (lastIndex < markdown.length) {\n segments.push({\n type: \"text\",\n value: markdown.slice(lastIndex),\n });\n }\n\n return segments;\n}\n\nexport function parseMermaidBlocks(fullMarkdown: string) {\n const blocks = [];\n const lines: string[] = fullMarkdown.split(/\\r?\\n/);\n\n let inside = false;\n let current: string[] = [];\n let startLine = 0;\n\n lines.forEach((line, index) => {\n if (!inside) {\n if (line.trim().startsWith(\"```mermaid\")) {\n inside = true;\n startLine = index;\n current = [];\n }\n return;\n }\n\n // inside mermaid mode\n if (line.trim() === \"```\") {\n // block complete\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: index,\n complete: true,\n });\n inside = false;\n return;\n }\n\n current.push(line);\n });\n\n // if still inside → incomplete block\n if (inside) {\n blocks.push({\n code: current.join(\"\\n\").trim(),\n startLine,\n endLine: null,\n complete: false,\n });\n }\n\n return blocks;\n}\n\n/**\n * Determine whether the mermaid block for the given codeString is fully closed.\n * @param fullMarkdown Full markdown text (grows over time in streaming scenarios)\n * @param codeString Mermaid block code passed to the renderer (children)\n */\nexport function mermaidBlockIsComplete(\n fullMarkdown: string,\n codeString: string\n) {\n const cleaned = codeString.trim();\n const blocks = parseMermaidBlocks(fullMarkdown);\n // Locate the block that matches the current codeString\n const block = blocks.find((b) => b.code === cleaned);\n\n if (!block) return false;\n\n // Block is complete when the closing fence already exists\n return block.complete;\n}\n"],"names":["parseMarkdownSegments","markdown","segments","regex","getLineRange","index","lineStart","nextBreak","lineEnd","isIndexInsideMarkdownTableLine","isIndexInsideInlineCode","line","relativeIndex","cursor","inCode","delimiterLength","runEnd","runLength","lastIndex","match","start","end","rawMatch","code","incompleteSvgStart","lastSvgClose","incompleteCodeBlockStart","incompleteStart","parseMermaidBlocks","fullMarkdown","blocks","lines","inside","current","startLine","mermaidBlockIsComplete","codeString","cleaned","block","b"],"mappings":"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"}
@@ -1,4 +1,6 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const N=/<(script|style|link|iframe|html|head|body|meta|title|base|template|div|section|article|main)[\s>]/i,R=[/<svg[\s\S]*?<\/svg>/i,/<img\b[^>]*?>/i,/```mermaid[\s\S]*?```/i,/```[a-zA-Z0-9]+[\s\S]*?```/i],M=/!\[[^\]]*]\([^\s)\n]+(?:\s+"[^"]*")?\)/i,B=/<\/[a-z][^>]*>\s*\n(?=[^\s<])/gi,O=/<custom-button-after-content\b[\s\S]*?<\/custom-button-after-content>/gi,T=t=>{const e=[],n=/```/g;let s;for(;(s=n.exec(t))!==null;){const o=s.index,c=n.exec(t);if(!c){e.push({start:o,end:t.length});break}e.push({start:o,end:c.index+3})}return e},_=(t,e)=>e.some(({start:n,end:s})=>t>=n&&t<s),y=(t,e,n)=>{const s=e.flags.includes("g")?e.flags:`${e.flags}g`,o=new RegExp(e.source,s);let c;for(;(c=o.exec(t))!==null;)if(!_(c.index,n))return c.index;return-1},P=(t,e)=>{let n=t.length,s;for(B.lastIndex=0;s=B.exec(t);)if(!(s.index<=e)){n=s.index+s[0].length;break}return n},F=t=>{if(!t.length)return t;const e=[];return t.forEach(n=>{if(n.type!=="sandbox"){e.push(n);return}O.lastIndex=0;let s=0,o;for(;(o=O.exec(n.value))!==null;){const i=n.value.slice(s,o.index);i.trim()&&e.push({type:"sandbox",value:i}),e.push({type:"markdown",value:o[0]}),s=o.index+o[0].length}const c=n.value.slice(s);c.trim()&&e.push({type:"sandbox",value:c})}),e},C=t=>{let e=null;return R.forEach(n=>{const s=n.exec(t);if(!s||typeof s.index!="number")return;const o=s.index,c=s.index+s[0].length;(!e||o<e.start)&&(e={start:o,end:c})}),e},z=(t,e)=>{const n=y(t,M,e);if(n===-1)return null;const s=t.slice(n).match(M);return s?{start:n,end:n+s[0].length}:null},D=t=>{const e=t.match(/^\s*\|.+\|\s*$/m);if(!e||typeof e.index!="number")return null;const n=e[0].match(/^\s*/)?.[0].length??0,s=e.index+n,o=t.slice(s).split(`
2
- `),c=[];for(const h of o){if(!h.trim().startsWith("|"))break;c.push(h)}const i=c.join(`
3
- `);return{start:s,block:i,end:s+i.length}},f=(t,e=!1)=>{const n=l=>F(l),s=t.indexOf("```");if(e&&s!==-1&&t.indexOf("```",s+3)===-1)return n([{type:"markdown",value:t}]);const o=T(t),c=y(t,N,o),i=y(t,/<svg\b/i,o),h=c!==-1&&i!==-1&&c<i;if(i!==-1&&!h){const l=t.slice(0,i),r=t.indexOf("</svg>",i),a=r===-1?`${t.slice(i)}</svg>`:t.slice(i,r+6),b=r===-1?"":t.slice(r+6);if(e){const p=[];return l.trim()&&p.push({type:"text",value:l}),p.push({type:"markdown",value:a}),b.trim()&&p.push(...f(b,!0)),n(p)}if(r===-1)return n([{type:"markdown",value:a}])}const u=D(t);if(u){const l=[],r=t.slice(0,u.start);e&&r.trim()&&l.push({type:"text",value:r}),l.push({type:"markdown",value:u.block});const a=t.slice(u.end),b=a.length<t.length;return a.trim()&&b&&l.push(...e?f(a,!0):f(a)),n(l)}const m=C(t),g=z(t,o),d=m&&g?m.start<=g.start?m:g:m??g;if(c===-1&&!d)return e&&t.trim()?n([{type:"text",value:t}]):[];const v=!!d&&(c===-1||d.start<c),S=v?d.start:c,I=v?d.end:P(t,S),x=[],k=t.slice(0,S),A=t.slice(S,I),E=t.slice(I);return e&&k.trim()&&x.push({type:"text",value:k}),x.push({type:v?"markdown":"sandbox",value:A}),E.trim()&&x.push(...f(E,e)),n(x)};exports.splitContentSegments=f;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const H=/<(script|style|link|iframe|html|head|body|meta|title|base|template|div|section|article|main)[\s>]/i,C=[/<svg[\s\S]*?<\/svg>/i,/<img\b[^>]*?>/i,/```mermaid[\s\S]*?```/i,/```[a-zA-Z0-9]+[\s\S]*?```/i],E=/!\[[^\]]*]\([^\s)\n]+(?:\s+"[^"]*")?\)/i,T=/^(?: {0,3})#{1,6}[ \t]+[^\n]+$/m,A=/<iframe\b[^>]*\bdata-tag\s*=\s*(["'])video\1[^>]*>[\s\S]*?<\/iframe>/i,O=/<\/[a-z][^>]*>\s*\n(?=[^\s<])/gi,N=/<custom-button-after-content\b[\s\S]*?<\/custom-button-after-content>/gi,W=t=>{const n=[],e=/```/g;let s;for(;(s=e.exec(t))!==null;){const c=s.index,i=e.exec(t);if(!i){n.push({start:c,end:t.length});break}n.push({start:c,end:i.index+3})}return n},L=(t,n)=>n.some(({start:e,end:s})=>t>=e&&t<s),m=(t,n,e)=>{const s=n.flags.includes("g")?n.flags:`${n.flags}g`,c=new RegExp(n.source,s);let i;for(;(i=c.exec(t))!==null;)if(!L(i.index,e))return i.index;return-1},V=(t,n)=>{let e=t.length,s;for(O.lastIndex=0;s=O.exec(t);)if(!(s.index<=n)){e=s.index+s[0].length;break}return e},$=t=>{if(!t.length)return t;const n=[];return t.forEach(e=>{if(e.type!=="sandbox"){n.push(e);return}N.lastIndex=0;let s=0,c;for(;(c=N.exec(e.value))!==null;){const o=e.value.slice(s,c.index);o.trim()&&n.push({type:"sandbox",value:o}),n.push({type:"markdown",value:c[0]}),s=c.index+c[0].length}const i=e.value.slice(s);i.trim()&&n.push({type:"sandbox",value:i})}),n},z=t=>{let n=null;return C.forEach(e=>{const s=e.exec(t);if(!s||typeof s.index!="number")return;const c=s.index,i=s[0].trimStart();if(/^<(svg|img)\b/i.test(i)&&R(t,c))return;const u=s.index+s[0].length;(!n||c<n.start)&&(n={start:c,end:u})}),n},K=(...t)=>t.reduce((n,e)=>e&&(!n||e.start<n.start)?e:n,null),U=(t,n)=>{const e=m(t,E,n);if(e===-1)return null;const s=t.slice(e).match(E);return s?{start:e,end:e+s[0].length}:null},j=(t,n)=>{const e=m(t,T,n);if(e===-1)return null;const s=t.slice(e).match(T);return s?{start:e,end:e+s[0].length}:null},G=(t,n)=>{const e=m(t,A,n);if(e===-1)return null;const s=t.slice(e).match(A);return s?{start:e,end:e+s[0].length}:null},R=(t,n)=>{const e=t.lastIndexOf(`
2
+ `,n-1)+1,s=t.indexOf(`
3
+ `,n),c=s===-1?t.length:s;return t.slice(e,c).trimStart().startsWith("|")},X=t=>{const n=t.match(/^\s*\|.+\|\s*$/m);if(!n||typeof n.index!="number")return null;const e=n[0].match(/^\s*/)?.[0].length??0,s=n.index+e,c=t.slice(s).split(`
4
+ `),i=[];for(const u of c){if(!u.trim().startsWith("|"))break;i.push(u)}const o=i.join(`
5
+ `);return{start:s,block:o,end:s+o.length}},f=(t,n=!0)=>{const e=l=>$(l),s=t.indexOf("```");if(n&&s!==-1&&t.indexOf("```",s+3)===-1)return e([{type:"markdown",value:t}]);const c=W(t),i=m(t,H,c),o=m(t,/<svg\b/i,c),u=o!==-1&&R(t,o),S=i!==-1&&o!==-1&&i<o;if(o!==-1&&!S&&!u){const l=t.slice(0,o),r=t.indexOf("</svg>",o),a=r===-1?`${t.slice(o)}</svg>`:t.slice(o,r+6),b=r===-1?"":t.slice(r+6);if(n){const x=[];return l.trim()&&x.push(...f(l,!0)),x.push({type:"markdown",value:a}),b.trim()&&x.push(...f(b,!0)),e(x)}if(r===-1)return e([{type:"markdown",value:a}])}const d=X(t),p=j(t,c),_=!!d&&(!p||d.start<p.start);if(d&&_){const l=[],r=t.slice(0,d.start);n&&r.trim()&&l.push({type:"text",value:r}),l.push({type:"markdown",value:d.block});const a=t.slice(d.end),b=a.length<t.length;return a.trim()&&b&&l.push(...n?f(a,!0):f(a)),e(l)}const B=z(t),P=U(t,c),D=G(t,c),h=K(B,P,p,D);if(i===-1&&!h)return n&&t.trim()?e([{type:"text",value:t}]):[];const v=!!h&&(i===-1||h.start<=i),I=v?h.start:i,M=v?h.end:V(t,I),g=[],k=t.slice(0,I),F=t.slice(I,M),y=t.slice(M);return n&&k.trim()&&g.push({type:"text",value:k}),g.push({type:v?"markdown":"sandbox",value:F}),y.trim()&&g.push(...f(y,n)),e(g)};exports.splitContentSegments=f;
4
6
  //# sourceMappingURL=split-content.cjs.js.map