markdown-flow-ui 0.2.3-dev.2 → 0.2.3-dev.3
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.
- package/dist/components/ContentRender/SandboxApp.cjs.js +2 -2
- package/dist/components/ContentRender/SandboxApp.cjs.js.map +1 -1
- package/dist/components/ContentRender/SandboxApp.es.js +106 -95
- package/dist/components/ContentRender/SandboxApp.es.js.map +1 -1
- package/dist/components/ContentRender/utils/link-targets.cjs.js +2 -0
- package/dist/components/ContentRender/utils/link-targets.cjs.js.map +1 -0
- package/dist/components/ContentRender/utils/link-targets.d.ts +6 -0
- package/dist/components/ContentRender/utils/link-targets.es.js +26 -0
- package/dist/components/ContentRender/utils/link-targets.es.js.map +1 -0
- package/dist/components/ContentRender/utils/link-targets.test.d.ts +1 -0
- package/dist/components/Slide/Slide.cjs.js +1 -1
- package/dist/components/Slide/Slide.cjs.js.map +1 -1
- package/dist/components/Slide/Slide.es.js +798 -712
- package/dist/components/Slide/Slide.es.js.map +1 -1
- package/dist/components/Slide/Slide.stories.d.ts +4 -4
- package/dist/components/Slide/constants.cjs.js +2 -0
- package/dist/components/Slide/constants.cjs.js.map +1 -0
- package/dist/components/Slide/constants.d.ts +1 -0
- package/dist/components/Slide/constants.es.js +5 -0
- package/dist/components/Slide/constants.es.js.map +1 -0
- package/dist/components/Slide/utils/imageOnlyStepVisualReady.cjs.js +2 -0
- package/dist/components/Slide/utils/imageOnlyStepVisualReady.cjs.js.map +1 -0
- package/dist/components/Slide/utils/imageOnlyStepVisualReady.d.ts +13 -0
- package/dist/components/Slide/utils/imageOnlyStepVisualReady.es.js +12 -0
- package/dist/components/Slide/utils/imageOnlyStepVisualReady.es.js.map +1 -0
- package/dist/components/Slide/utils/silentStepAutoAdvance.cjs.js +2 -0
- package/dist/components/Slide/utils/silentStepAutoAdvance.cjs.js.map +1 -0
- package/dist/components/Slide/utils/silentStepAutoAdvance.d.ts +14 -0
- package/dist/components/Slide/utils/silentStepAutoAdvance.es.js +37 -0
- package/dist/components/Slide/utils/silentStepAutoAdvance.es.js.map +1 -0
- package/dist/components/Slide/utils/silentStepAutoAdvance.test.d.ts +1 -0
- package/package.json +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const T=require("../../_virtual/jsx-runtime.cjs.js"),n=require("react"),oe=require("../ui/loading-overlay-card.cjs.js"),ce=require("./contentRenderI18n.cjs.js"),p=require("./utils/link-targets.cjs.js"),ae=["src","srcset","sizes","alt","class","width","height","style","loading","decoding","crossorigin","referrerpolicy","fetchpriority"],Q=c=>ae.map(s=>`${s}:${c.getAttribute(s)||""}`).join("|"),ie=c=>{const s=new Map;return c.querySelectorAll("img").forEach(l=>{const u=l,o=Q(u),i=s.get(o)||[];i.push(u),s.set(o,i)}),s},le=(c,s)=>{const l=Array.from(c.attributes),u=new Set(l.map(o=>o.name));Array.from(s.attributes).forEach(o=>{u.has(o.name)||s.removeAttribute(o.name)}),l.forEach(o=>{s.setAttribute(o.name,o.value)})},ue=(c,s)=>{s.size&&c.querySelectorAll("img").forEach(l=>{const u=l,o=Q(u),i=s.get(o),y=i?.shift();y&&(le(u,y),u.replaceWith(y),i&&i.length===0&&s.delete(o))})},fe=({html:c,locale:s,loadingText:l,styleLoadingText:u,scriptLoadingText:o,resetToken:i=0,mode:y="content",hasRootVhHeight:J=!1,stretchRootHeight:X=!1,enableScaling:F=!1,disableLoadingOverlay:j=!1})=>{const N=ce.getContentRenderLocaleTexts(s),k=n.useRef(null),[Y,R]=n.useState(!1),[Z,S]=n.useState(!1),[ee,L]=n.useState(!1),_=n.useRef([]),q=n.useRef([]),G=n.useRef(0),V=n.useRef(0),g=n.useRef(null),b=n.useRef(null),x=n.useRef(!1),v=n.useRef(!1),z=n.useRef(i),te=200,d=t=>{t.current&&(clearTimeout(t.current),t.current=null)},U=(t,a,f,H)=>{const h=performance.now()-f.current,I=Math.max(0,te-h);d(a),a.current=window.setTimeout(()=>{t(!1),H?.(),a.current=null},I)};n.useEffect(()=>{const t=k.current?.ownerDocument;if(!t)return;const a="sandbox-spinner-style";let f=t.getElementById(a);f||(f=t.createElement("style"),f.id=a,t.head?.appendChild(f)),f.textContent=`
|
|
2
2
|
.sandbox-wrapper { align-items: center; }
|
|
3
3
|
.sandbox-container { position: relative; width: 100%; }
|
|
4
4
|
.sandbox-container svg,
|
|
@@ -17,5 +17,5 @@
|
|
|
17
17
|
justify-content: safe center;
|
|
18
18
|
}
|
|
19
19
|
.overflow-y-auto { overflow-y: visible !important; }
|
|
20
|
-
`},[]),n.useEffect(()=>{i!==
|
|
20
|
+
`},[]),n.useEffect(()=>{i!==z.current&&(L(!1),z.current=i),d(g),d(b),x.current=!1,v.current=!1;const t=k.current;if(!t)return;const a=t.ownerDocument,f=a?.body;if(!f)return;const H=ie(t);_.current.forEach(e=>e.remove()),_.current=[],q.current.forEach(e=>e.remove()),q.current=[],R(!1),S(!1);const h=a.createElement("div");h.innerHTML=c,h.querySelectorAll("a[href]").forEach(e=>{p.isAnchorElement(e)&&p.shouldForceAnchorHrefToNewTab(e.getAttribute("href"))&&(e.setAttribute("target","_blank"),e.setAttribute("rel",p.mergeAnchorRelValue(e.getAttribute("rel"))))});const I=(c.match(/<script[\s>]/gi)||[]).length,se=(c.match(/<\/script>/gi)||[]).length,W=I>0&&I===se,w=[];Array.from(h.querySelectorAll("style, script")).forEach(e=>{if(e.tagName.toLowerCase()==="style"){const r=a.createElement("style");r.textContent=e.textContent||"",Array.from(e.attributes).forEach(m=>{r.setAttribute(m.name,m.value)}),w.push(r)}else{const r=a.createElement("script");Array.from(e.attributes).forEach(m=>{r.setAttribute(m.name,m.value)}),r.textContent=e.textContent||"",w.push(r)}e.remove()}),ue(h,H);const B=w.some(e=>e.tagName.toLowerCase()==="style"),D=w.some(e=>e.tagName.toLowerCase()==="script");B&&(x.current=!0,G.current=performance.now(),d(g),R(!0)),D&&(v.current=!0,V.current=performance.now(),d(b),S(!0));const O=Array.from(h.childNodes),$=O.length>0;L($),t.replaceChildren(...O);const K=e=>{const r=p.resolveClosestAnchor(e.target);r&&p.shouldForceAnchorHrefToNewTab(r.getAttribute("href"))&&(r.setAttribute("target","_blank"),r.setAttribute("rel",p.mergeAnchorRelValue(r.getAttribute("rel"))))};t.addEventListener("click",K,!0);let A=null;const P=()=>t.childNodes.length===0?!1:(L(!0),A?.disconnect(),A=null,!0);return!$&&D&&W&&(A=new MutationObserver(()=>{P()}),A.observe(t,{childList:!0,subtree:!0})),w.forEach(e=>{if(e.tagName.toLowerCase()==="style"){a.head?.appendChild(e),_.current.push(e);return}if(W){const r=e,m=r.textContent||"";if(!r.src)try{new Function(m)}catch{r.remove();return}try{f.appendChild(r),q.current.push(r)}catch{r.remove()}}else e.remove()}),P(),F&&t.ownerDocument?.defaultView?.__mdf_triggerFitContent?.(),requestAnimationFrame(()=>{B&&U(R,g,G,()=>{x.current=!1}),D&&U(S,b,V,()=>{v.current=!1})}),()=>{A?.disconnect(),t.removeEventListener("click",K,!0)}},[c,i,F]),n.useEffect(()=>()=>{d(g),d(b)},[]),n.useEffect(()=>{j&&(d(g),d(b),x.current=!1,v.current=!1,R(!1),S(!1))},[j]);const E=j?null:Z||v.current?o||l||N.sandboxScriptLoadingText:Y||x.current?u||l||N.sandboxStyleLoadingText:ee?null:l||N.sandboxLoadingText,M=y==="blackboard",C=M&&X,re={position:"relative",width:"100%",height:"100%",display:"flex",flexDirection:"column",justifyContent:C?"flex-start":M?"space-around":"flex-start"},ne={pointerEvents:E?"none":void 0,margin:M?void 0:"auto 0",width:"100%",height:C?"100%":void 0,minHeight:C?0:void 0,flex:C?"1 1 auto":void 0};return T.jsxRuntimeExports.jsxs("div",{"data-root-vh":J?"true":"false",className:"sandbox-wrapper",style:re,"aria-busy":!!E,children:[T.jsxRuntimeExports.jsx("div",{ref:k,className:"sandbox-container",style:ne}),E&&T.jsxRuntimeExports.jsx("div",{style:{position:"absolute",inset:0,zIndex:20,display:"flex",alignItems:"center",justifyContent:"center",pointerEvents:"none"},children:T.jsxRuntimeExports.jsx(oe.default,{message:E})})]})};exports.default=fe;
|
|
21
21
|
//# sourceMappingURL=SandboxApp.cjs.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxApp.cjs.js","sources":["../../../src/components/ContentRender/SandboxApp.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport LoadingOverlayCard from \"../ui/loading-overlay-card\";\nimport type { MarkdownFlowLocale } from \"../../lib/locale\";\nimport { getContentRenderLocaleTexts } from \"./contentRenderI18n\";\nimport type { ScalingWindow } from \"./utils/iframe-scaling\";\n\nexport interface SandboxAppProps {\n html: string;\n locale?: MarkdownFlowLocale;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n resetToken?: number;\n mode?: \"content\" | \"blackboard\";\n hasRootVhHeight?: boolean;\n stretchRootHeight?: boolean;\n enableScaling?: boolean;\n disableLoadingOverlay?: boolean;\n}\n\nconst IMAGE_REUSE_ATTRIBUTES = [\n \"src\",\n \"srcset\",\n \"sizes\",\n \"alt\",\n \"class\",\n \"width\",\n \"height\",\n \"style\",\n \"loading\",\n \"decoding\",\n \"crossorigin\",\n \"referrerpolicy\",\n \"fetchpriority\",\n];\n\nconst getImageReuseKey = (image: HTMLImageElement) =>\n IMAGE_REUSE_ATTRIBUTES.map(\n (attribute) => `${attribute}:${image.getAttribute(attribute) || \"\"}`\n ).join(\"|\");\n\nconst collectReusableImages = (root: ParentNode) => {\n const imageMap = new Map<string, HTMLImageElement[]>();\n root.querySelectorAll(\"img\").forEach((node) => {\n const image = node as HTMLImageElement;\n const key = getImageReuseKey(image);\n const bucket = imageMap.get(key) || [];\n bucket.push(image);\n imageMap.set(key, bucket);\n });\n return imageMap;\n};\n\nconst syncImageAttributes = (\n sourceImage: HTMLImageElement,\n targetImage: HTMLImageElement\n) => {\n const sourceAttributes = Array.from(sourceImage.attributes);\n const sourceAttributeNames = new Set(\n sourceAttributes.map((attribute) => attribute.name)\n );\n\n Array.from(targetImage.attributes).forEach((attribute) => {\n if (!sourceAttributeNames.has(attribute.name)) {\n targetImage.removeAttribute(attribute.name);\n }\n });\n\n sourceAttributes.forEach((attribute) => {\n targetImage.setAttribute(attribute.name, attribute.value);\n });\n};\n\nconst reuseRenderedImages = (\n root: ParentNode,\n imageMap: Map<string, HTMLImageElement[]>\n) => {\n if (!imageMap.size) return;\n\n root.querySelectorAll(\"img\").forEach((node) => {\n const nextImage = node as HTMLImageElement;\n const key = getImageReuseKey(nextImage);\n const bucket = imageMap.get(key);\n const preservedImage = bucket?.shift();\n if (!preservedImage) return;\n\n syncImageAttributes(nextImage, preservedImage);\n nextImage.replaceWith(preservedImage);\n\n if (bucket && bucket.length === 0) {\n imageMap.delete(key);\n }\n });\n};\n\nconst SandboxApp: React.FC<SandboxAppProps> = ({\n html,\n locale,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n resetToken = 0,\n mode = \"content\",\n hasRootVhHeight = false,\n stretchRootHeight = false,\n enableScaling = false,\n disableLoadingOverlay = false,\n}) => {\n const localeTexts = getContentRenderLocaleTexts(locale);\n const containerRef = useRef<HTMLDivElement>(null);\n const [isGeneratingStyles, setIsGeneratingStyles] = useState(false);\n const [isGeneratingScripts, setIsGeneratingScripts] = useState(false);\n const [hasRenderedContent, setHasRenderedContent] = useState(false);\n const appendedStylesRef = useRef<HTMLStyleElement[]>([]);\n const appendedScriptsRef = useRef<HTMLScriptElement[]>([]);\n const styleStartRef = useRef(0);\n const scriptStartRef = useRef(0);\n const styleTimerRef = useRef<number | null>(null);\n const scriptTimerRef = useRef<number | null>(null);\n const hasStylesRef = useRef(false);\n const hasScriptsRef = useRef(false);\n const prevResetTokenRef = useRef(resetToken);\n const MIN_LOADING_MS = 200;\n\n const clearTimer = (timerRef: React.MutableRefObject<number | null>) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const settleStateWithMinimumDelay = (\n setter: React.Dispatch<React.SetStateAction<boolean>>,\n timerRef: React.MutableRefObject<number | null>,\n startRef: React.MutableRefObject<number>,\n onDone?: () => void\n ) => {\n const elapsed = performance.now() - startRef.current;\n const delay = Math.max(0, MIN_LOADING_MS - elapsed);\n clearTimer(timerRef);\n timerRef.current = window.setTimeout(() => {\n setter(false);\n onDone?.();\n timerRef.current = null;\n }, delay);\n };\n\n useEffect(() => {\n const doc = containerRef.current?.ownerDocument;\n if (!doc) return;\n const styleId = \"sandbox-spinner-style\";\n let styleEl = doc.getElementById(styleId) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = doc.createElement(\"style\");\n styleEl.id = styleId;\n doc.head?.appendChild(styleEl);\n }\n styleEl.textContent = `\n .sandbox-wrapper { align-items: center; }\n .sandbox-container { position: relative; width: 100%; }\n .sandbox-container svg,\n .sandbox-container img { display: block; margin-left: auto; margin-right: auto; }\n /* Hide scroll UI without removing scroll semantics. Content roots with\n inline style=\"height:100vh; overflow-y:auto\" are the signal that\n IframeSandbox.getInnerScrollableHeight relies on to grow the iframe\n when content exceeds the viewport. We must not change overflow-y,\n but sub-pixel rounding (1px below the +1 detection threshold) can\n still surface a phantom scrollbar on content that visually fits.\n Hide the scrollbar UI globally; when content truly overflows the\n iframe is resized and no scrollbar would render anyway. */\n .sandbox-container *::-webkit-scrollbar { display: none; }\n .sandbox-container * { scrollbar-width: none; -ms-overflow-style: none; }\n .justify-\\\\[safe_center\\\\]{\n justify-content: safe center;\n }\n .overflow-y-auto { overflow-y: visible !important; }\n `;\n }, []);\n\n useEffect(() => {\n if (resetToken !== prevResetTokenRef.current) {\n setHasRenderedContent(false);\n prevResetTokenRef.current = resetToken;\n }\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n\n const container = containerRef.current;\n if (!container) return;\n const doc = container.ownerDocument;\n const body = doc?.body;\n if (!body) return;\n const reusableImages = collectReusableImages(container);\n\n appendedStylesRef.current.forEach((node) => node.remove());\n appendedStylesRef.current = [];\n appendedScriptsRef.current.forEach((node) => node.remove());\n appendedScriptsRef.current = [];\n\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n const wrapper = doc.createElement(\"div\");\n wrapper.innerHTML = html;\n\n const openScriptCount = (html.match(/<script[\\s>]/gi) || []).length;\n const closeScriptCount = (html.match(/<\\/script>/gi) || []).length;\n const shouldExecuteScripts =\n openScriptCount > 0 && openScriptCount === closeScriptCount;\n\n const resourceQueue: HTMLElement[] = [];\n\n Array.from(wrapper.querySelectorAll(\"style, script\")).forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n const cloned = doc.createElement(\"style\");\n cloned.textContent = node.textContent || \"\";\n Array.from(node.attributes).forEach((attr) => {\n cloned.setAttribute(attr.name, attr.value);\n });\n resourceQueue.push(cloned);\n } else {\n const replacement = doc.createElement(\"script\");\n Array.from(node.attributes).forEach((attr) => {\n replacement.setAttribute(attr.name, attr.value);\n });\n replacement.textContent = node.textContent || \"\";\n resourceQueue.push(replacement);\n }\n node.remove();\n });\n reuseRenderedImages(wrapper, reusableImages);\n\n const hasStyles = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"style\"\n );\n const hasScripts = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"script\"\n );\n if (hasStyles) {\n hasStylesRef.current = true;\n styleStartRef.current = performance.now();\n clearTimer(styleTimerRef);\n setIsGeneratingStyles(true);\n }\n if (hasScripts) {\n hasScriptsRef.current = true;\n scriptStartRef.current = performance.now();\n clearTimer(scriptTimerRef);\n setIsGeneratingScripts(true);\n }\n\n const contentNodes = Array.from(wrapper.childNodes);\n const hasInitialContent = contentNodes.length > 0;\n setHasRenderedContent(hasInitialContent);\n container.replaceChildren(...contentNodes);\n\n let scriptContentObserver: MutationObserver | null = null;\n const markRenderedContentFromContainer = () => {\n if (container.childNodes.length === 0) {\n return false;\n }\n\n setHasRenderedContent(true);\n scriptContentObserver?.disconnect();\n scriptContentObserver = null;\n return true;\n };\n if (!hasInitialContent && hasScripts && shouldExecuteScripts) {\n scriptContentObserver = new MutationObserver(() => {\n markRenderedContentFromContainer();\n });\n scriptContentObserver.observe(container, {\n childList: true,\n subtree: true,\n });\n }\n\n resourceQueue.forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n doc.head?.appendChild(node);\n appendedStylesRef.current.push(node as HTMLStyleElement);\n return;\n }\n\n if (shouldExecuteScripts) {\n const scriptNode = node as HTMLScriptElement;\n const scriptText = scriptNode.textContent || \"\";\n const shouldValidate = !scriptNode.src;\n\n if (shouldValidate) {\n try {\n // Validate script is syntactically complete before executing\n\n new Function(scriptText);\n } catch {\n scriptNode.remove();\n return;\n }\n }\n\n try {\n body.appendChild(scriptNode);\n appendedScriptsRef.current.push(scriptNode);\n } catch {\n scriptNode.remove();\n }\n } else {\n // Defer execution until all script tags are fully received\n node.remove();\n }\n });\n markRenderedContentFromContainer();\n if (enableScaling) {\n const win = container.ownerDocument?.defaultView as ScalingWindow | null;\n win?.__mdf_triggerFitContent?.();\n }\n\n requestAnimationFrame(() => {\n if (hasStyles) {\n settleStateWithMinimumDelay(\n setIsGeneratingStyles,\n styleTimerRef,\n styleStartRef,\n () => {\n hasStylesRef.current = false;\n }\n );\n }\n if (hasScripts) {\n settleStateWithMinimumDelay(\n setIsGeneratingScripts,\n scriptTimerRef,\n scriptStartRef,\n () => {\n hasScriptsRef.current = false;\n }\n );\n }\n });\n\n return () => {\n scriptContentObserver?.disconnect();\n };\n }, [html, resetToken, enableScaling]);\n\n useEffect(\n () => () => {\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n },\n []\n );\n\n useEffect(() => {\n if (!disableLoadingOverlay) {\n return;\n }\n\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n }, [disableLoadingOverlay]);\n\n const overlayMessage = (() => {\n if (disableLoadingOverlay) {\n return null;\n }\n\n if (isGeneratingScripts || hasScriptsRef.current)\n return (\n scriptLoadingText || loadingText || localeTexts.sandboxScriptLoadingText\n );\n if (isGeneratingStyles || hasStylesRef.current)\n return (\n styleLoadingText || loadingText || localeTexts.sandboxStyleLoadingText\n );\n if (!hasRenderedContent)\n return loadingText || localeTexts.sandboxLoadingText;\n return null;\n })();\n\n const isBlackboard = mode === \"blackboard\";\n const shouldStretchRootHeight = isBlackboard && stretchRootHeight;\n const sandboxWrapperStyle: React.CSSProperties = {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n // Keep blackboard scroll behavior while centering content in non-blackboard mode\n justifyContent: shouldStretchRootHeight\n ? \"flex-start\"\n : isBlackboard\n ? \"space-around\"\n : \"flex-start\",\n };\n const sandboxContainerStyle: React.CSSProperties = {\n pointerEvents: overlayMessage ? \"none\" : undefined,\n margin: isBlackboard ? undefined : \"auto 0\",\n width: \"100%\",\n height: shouldStretchRootHeight ? \"100%\" : undefined,\n minHeight: shouldStretchRootHeight ? 0 : undefined,\n flex: shouldStretchRootHeight ? \"1 1 auto\" : undefined,\n };\n\n return (\n <div\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className=\"sandbox-wrapper\"\n style={sandboxWrapperStyle}\n aria-busy={!!overlayMessage}\n >\n <div\n ref={containerRef}\n className=\"sandbox-container\"\n style={sandboxContainerStyle}\n />\n {overlayMessage && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n }}\n >\n <LoadingOverlayCard message={overlayMessage} />\n </div>\n )}\n </div>\n );\n};\n\nexport default SandboxApp;\n"],"names":["IMAGE_REUSE_ATTRIBUTES","getImageReuseKey","image","attribute","collectReusableImages","root","imageMap","node","key","bucket","syncImageAttributes","sourceImage","targetImage","sourceAttributes","sourceAttributeNames","reuseRenderedImages","nextImage","preservedImage","SandboxApp","html","locale","loadingText","styleLoadingText","scriptLoadingText","resetToken","mode","hasRootVhHeight","stretchRootHeight","enableScaling","disableLoadingOverlay","localeTexts","getContentRenderLocaleTexts","containerRef","useRef","isGeneratingStyles","setIsGeneratingStyles","useState","isGeneratingScripts","setIsGeneratingScripts","hasRenderedContent","setHasRenderedContent","appendedStylesRef","appendedScriptsRef","styleStartRef","scriptStartRef","styleTimerRef","scriptTimerRef","hasStylesRef","hasScriptsRef","prevResetTokenRef","MIN_LOADING_MS","clearTimer","timerRef","settleStateWithMinimumDelay","setter","startRef","onDone","elapsed","delay","useEffect","doc","styleId","styleEl","container","body","reusableImages","wrapper","openScriptCount","closeScriptCount","shouldExecuteScripts","resourceQueue","cloned","attr","replacement","hasStyles","hasScripts","contentNodes","hasInitialContent","scriptContentObserver","markRenderedContentFromContainer","scriptNode","scriptText","overlayMessage","isBlackboard","shouldStretchRootHeight","sandboxWrapperStyle","sandboxContainerStyle","jsxs","jsx","LoadingOverlayCard"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SandboxApp.cjs.js","sources":["../../../src/components/ContentRender/SandboxApp.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport LoadingOverlayCard from \"../ui/loading-overlay-card\";\nimport type { MarkdownFlowLocale } from \"../../lib/locale\";\nimport { getContentRenderLocaleTexts } from \"./contentRenderI18n\";\nimport type { ScalingWindow } from \"./utils/iframe-scaling\";\nimport {\n isAnchorElement,\n mergeAnchorRelValue,\n resolveClosestAnchor,\n shouldForceAnchorHrefToNewTab,\n} from \"./utils/link-targets\";\n\nexport interface SandboxAppProps {\n html: string;\n locale?: MarkdownFlowLocale;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n resetToken?: number;\n mode?: \"content\" | \"blackboard\";\n hasRootVhHeight?: boolean;\n stretchRootHeight?: boolean;\n enableScaling?: boolean;\n disableLoadingOverlay?: boolean;\n}\n\nconst IMAGE_REUSE_ATTRIBUTES = [\n \"src\",\n \"srcset\",\n \"sizes\",\n \"alt\",\n \"class\",\n \"width\",\n \"height\",\n \"style\",\n \"loading\",\n \"decoding\",\n \"crossorigin\",\n \"referrerpolicy\",\n \"fetchpriority\",\n];\n\nconst getImageReuseKey = (image: HTMLImageElement) =>\n IMAGE_REUSE_ATTRIBUTES.map(\n (attribute) => `${attribute}:${image.getAttribute(attribute) || \"\"}`\n ).join(\"|\");\n\nconst collectReusableImages = (root: ParentNode) => {\n const imageMap = new Map<string, HTMLImageElement[]>();\n root.querySelectorAll(\"img\").forEach((node) => {\n const image = node as HTMLImageElement;\n const key = getImageReuseKey(image);\n const bucket = imageMap.get(key) || [];\n bucket.push(image);\n imageMap.set(key, bucket);\n });\n return imageMap;\n};\n\nconst syncImageAttributes = (\n sourceImage: HTMLImageElement,\n targetImage: HTMLImageElement\n) => {\n const sourceAttributes = Array.from(sourceImage.attributes);\n const sourceAttributeNames = new Set(\n sourceAttributes.map((attribute) => attribute.name)\n );\n\n Array.from(targetImage.attributes).forEach((attribute) => {\n if (!sourceAttributeNames.has(attribute.name)) {\n targetImage.removeAttribute(attribute.name);\n }\n });\n\n sourceAttributes.forEach((attribute) => {\n targetImage.setAttribute(attribute.name, attribute.value);\n });\n};\n\nconst reuseRenderedImages = (\n root: ParentNode,\n imageMap: Map<string, HTMLImageElement[]>\n) => {\n if (!imageMap.size) return;\n\n root.querySelectorAll(\"img\").forEach((node) => {\n const nextImage = node as HTMLImageElement;\n const key = getImageReuseKey(nextImage);\n const bucket = imageMap.get(key);\n const preservedImage = bucket?.shift();\n if (!preservedImage) return;\n\n syncImageAttributes(nextImage, preservedImage);\n nextImage.replaceWith(preservedImage);\n\n if (bucket && bucket.length === 0) {\n imageMap.delete(key);\n }\n });\n};\n\nconst SandboxApp: React.FC<SandboxAppProps> = ({\n html,\n locale,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n resetToken = 0,\n mode = \"content\",\n hasRootVhHeight = false,\n stretchRootHeight = false,\n enableScaling = false,\n disableLoadingOverlay = false,\n}) => {\n const localeTexts = getContentRenderLocaleTexts(locale);\n const containerRef = useRef<HTMLDivElement>(null);\n const [isGeneratingStyles, setIsGeneratingStyles] = useState(false);\n const [isGeneratingScripts, setIsGeneratingScripts] = useState(false);\n const [hasRenderedContent, setHasRenderedContent] = useState(false);\n const appendedStylesRef = useRef<HTMLStyleElement[]>([]);\n const appendedScriptsRef = useRef<HTMLScriptElement[]>([]);\n const styleStartRef = useRef(0);\n const scriptStartRef = useRef(0);\n const styleTimerRef = useRef<number | null>(null);\n const scriptTimerRef = useRef<number | null>(null);\n const hasStylesRef = useRef(false);\n const hasScriptsRef = useRef(false);\n const prevResetTokenRef = useRef(resetToken);\n const MIN_LOADING_MS = 200;\n\n const clearTimer = (timerRef: React.MutableRefObject<number | null>) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const settleStateWithMinimumDelay = (\n setter: React.Dispatch<React.SetStateAction<boolean>>,\n timerRef: React.MutableRefObject<number | null>,\n startRef: React.MutableRefObject<number>,\n onDone?: () => void\n ) => {\n const elapsed = performance.now() - startRef.current;\n const delay = Math.max(0, MIN_LOADING_MS - elapsed);\n clearTimer(timerRef);\n timerRef.current = window.setTimeout(() => {\n setter(false);\n onDone?.();\n timerRef.current = null;\n }, delay);\n };\n\n useEffect(() => {\n const doc = containerRef.current?.ownerDocument;\n if (!doc) return;\n const styleId = \"sandbox-spinner-style\";\n let styleEl = doc.getElementById(styleId) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = doc.createElement(\"style\");\n styleEl.id = styleId;\n doc.head?.appendChild(styleEl);\n }\n styleEl.textContent = `\n .sandbox-wrapper { align-items: center; }\n .sandbox-container { position: relative; width: 100%; }\n .sandbox-container svg,\n .sandbox-container img { display: block; margin-left: auto; margin-right: auto; }\n /* Hide scroll UI without removing scroll semantics. Content roots with\n inline style=\"height:100vh; overflow-y:auto\" are the signal that\n IframeSandbox.getInnerScrollableHeight relies on to grow the iframe\n when content exceeds the viewport. We must not change overflow-y,\n but sub-pixel rounding (1px below the +1 detection threshold) can\n still surface a phantom scrollbar on content that visually fits.\n Hide the scrollbar UI globally; when content truly overflows the\n iframe is resized and no scrollbar would render anyway. */\n .sandbox-container *::-webkit-scrollbar { display: none; }\n .sandbox-container * { scrollbar-width: none; -ms-overflow-style: none; }\n .justify-\\\\[safe_center\\\\]{\n justify-content: safe center;\n }\n .overflow-y-auto { overflow-y: visible !important; }\n `;\n }, []);\n\n useEffect(() => {\n if (resetToken !== prevResetTokenRef.current) {\n setHasRenderedContent(false);\n prevResetTokenRef.current = resetToken;\n }\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n\n const container = containerRef.current;\n if (!container) return;\n const doc = container.ownerDocument;\n const body = doc?.body;\n if (!body) return;\n const reusableImages = collectReusableImages(container);\n\n appendedStylesRef.current.forEach((node) => node.remove());\n appendedStylesRef.current = [];\n appendedScriptsRef.current.forEach((node) => node.remove());\n appendedScriptsRef.current = [];\n\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n const wrapper = doc.createElement(\"div\");\n wrapper.innerHTML = html;\n wrapper.querySelectorAll(\"a[href]\").forEach((node) => {\n if (!isAnchorElement(node)) {\n return;\n }\n\n if (!shouldForceAnchorHrefToNewTab(node.getAttribute(\"href\"))) {\n return;\n }\n\n node.setAttribute(\"target\", \"_blank\");\n node.setAttribute(\"rel\", mergeAnchorRelValue(node.getAttribute(\"rel\")));\n });\n\n const openScriptCount = (html.match(/<script[\\s>]/gi) || []).length;\n const closeScriptCount = (html.match(/<\\/script>/gi) || []).length;\n const shouldExecuteScripts =\n openScriptCount > 0 && openScriptCount === closeScriptCount;\n\n const resourceQueue: HTMLElement[] = [];\n\n Array.from(wrapper.querySelectorAll(\"style, script\")).forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n const cloned = doc.createElement(\"style\");\n cloned.textContent = node.textContent || \"\";\n Array.from(node.attributes).forEach((attr) => {\n cloned.setAttribute(attr.name, attr.value);\n });\n resourceQueue.push(cloned);\n } else {\n const replacement = doc.createElement(\"script\");\n Array.from(node.attributes).forEach((attr) => {\n replacement.setAttribute(attr.name, attr.value);\n });\n replacement.textContent = node.textContent || \"\";\n resourceQueue.push(replacement);\n }\n node.remove();\n });\n reuseRenderedImages(wrapper, reusableImages);\n\n const hasStyles = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"style\"\n );\n const hasScripts = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"script\"\n );\n if (hasStyles) {\n hasStylesRef.current = true;\n styleStartRef.current = performance.now();\n clearTimer(styleTimerRef);\n setIsGeneratingStyles(true);\n }\n if (hasScripts) {\n hasScriptsRef.current = true;\n scriptStartRef.current = performance.now();\n clearTimer(scriptTimerRef);\n setIsGeneratingScripts(true);\n }\n\n const contentNodes = Array.from(wrapper.childNodes);\n const hasInitialContent = contentNodes.length > 0;\n setHasRenderedContent(hasInitialContent);\n container.replaceChildren(...contentNodes);\n\n const handleLinkClick = (event: MouseEvent) => {\n const anchor = resolveClosestAnchor(event.target);\n if (!anchor) {\n return;\n }\n\n if (!shouldForceAnchorHrefToNewTab(anchor.getAttribute(\"href\"))) {\n return;\n }\n\n anchor.setAttribute(\"target\", \"_blank\");\n anchor.setAttribute(\n \"rel\",\n mergeAnchorRelValue(anchor.getAttribute(\"rel\"))\n );\n };\n container.addEventListener(\"click\", handleLinkClick, true);\n\n let scriptContentObserver: MutationObserver | null = null;\n const markRenderedContentFromContainer = () => {\n if (container.childNodes.length === 0) {\n return false;\n }\n\n setHasRenderedContent(true);\n scriptContentObserver?.disconnect();\n scriptContentObserver = null;\n return true;\n };\n if (!hasInitialContent && hasScripts && shouldExecuteScripts) {\n scriptContentObserver = new MutationObserver(() => {\n markRenderedContentFromContainer();\n });\n scriptContentObserver.observe(container, {\n childList: true,\n subtree: true,\n });\n }\n\n resourceQueue.forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n doc.head?.appendChild(node);\n appendedStylesRef.current.push(node as HTMLStyleElement);\n return;\n }\n\n if (shouldExecuteScripts) {\n const scriptNode = node as HTMLScriptElement;\n const scriptText = scriptNode.textContent || \"\";\n const shouldValidate = !scriptNode.src;\n\n if (shouldValidate) {\n try {\n // Validate script is syntactically complete before executing\n\n new Function(scriptText);\n } catch {\n scriptNode.remove();\n return;\n }\n }\n\n try {\n body.appendChild(scriptNode);\n appendedScriptsRef.current.push(scriptNode);\n } catch {\n scriptNode.remove();\n }\n } else {\n // Defer execution until all script tags are fully received\n node.remove();\n }\n });\n markRenderedContentFromContainer();\n if (enableScaling) {\n const win = container.ownerDocument?.defaultView as ScalingWindow | null;\n win?.__mdf_triggerFitContent?.();\n }\n\n requestAnimationFrame(() => {\n if (hasStyles) {\n settleStateWithMinimumDelay(\n setIsGeneratingStyles,\n styleTimerRef,\n styleStartRef,\n () => {\n hasStylesRef.current = false;\n }\n );\n }\n if (hasScripts) {\n settleStateWithMinimumDelay(\n setIsGeneratingScripts,\n scriptTimerRef,\n scriptStartRef,\n () => {\n hasScriptsRef.current = false;\n }\n );\n }\n });\n\n return () => {\n scriptContentObserver?.disconnect();\n container.removeEventListener(\"click\", handleLinkClick, true);\n };\n }, [html, resetToken, enableScaling]);\n\n useEffect(\n () => () => {\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n },\n []\n );\n\n useEffect(() => {\n if (!disableLoadingOverlay) {\n return;\n }\n\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n }, [disableLoadingOverlay]);\n\n const overlayMessage = (() => {\n if (disableLoadingOverlay) {\n return null;\n }\n\n if (isGeneratingScripts || hasScriptsRef.current)\n return (\n scriptLoadingText || loadingText || localeTexts.sandboxScriptLoadingText\n );\n if (isGeneratingStyles || hasStylesRef.current)\n return (\n styleLoadingText || loadingText || localeTexts.sandboxStyleLoadingText\n );\n if (!hasRenderedContent)\n return loadingText || localeTexts.sandboxLoadingText;\n return null;\n })();\n\n const isBlackboard = mode === \"blackboard\";\n const shouldStretchRootHeight = isBlackboard && stretchRootHeight;\n const sandboxWrapperStyle: React.CSSProperties = {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n // Keep blackboard scroll behavior while centering content in non-blackboard mode\n justifyContent: shouldStretchRootHeight\n ? \"flex-start\"\n : isBlackboard\n ? \"space-around\"\n : \"flex-start\",\n };\n const sandboxContainerStyle: React.CSSProperties = {\n pointerEvents: overlayMessage ? \"none\" : undefined,\n margin: isBlackboard ? undefined : \"auto 0\",\n width: \"100%\",\n height: shouldStretchRootHeight ? \"100%\" : undefined,\n minHeight: shouldStretchRootHeight ? 0 : undefined,\n flex: shouldStretchRootHeight ? \"1 1 auto\" : undefined,\n };\n\n return (\n <div\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className=\"sandbox-wrapper\"\n style={sandboxWrapperStyle}\n aria-busy={!!overlayMessage}\n >\n <div\n ref={containerRef}\n className=\"sandbox-container\"\n style={sandboxContainerStyle}\n />\n {overlayMessage && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n }}\n >\n <LoadingOverlayCard message={overlayMessage} />\n </div>\n )}\n </div>\n );\n};\n\nexport default SandboxApp;\n"],"names":["IMAGE_REUSE_ATTRIBUTES","getImageReuseKey","image","attribute","collectReusableImages","root","imageMap","node","key","bucket","syncImageAttributes","sourceImage","targetImage","sourceAttributes","sourceAttributeNames","reuseRenderedImages","nextImage","preservedImage","SandboxApp","html","locale","loadingText","styleLoadingText","scriptLoadingText","resetToken","mode","hasRootVhHeight","stretchRootHeight","enableScaling","disableLoadingOverlay","localeTexts","getContentRenderLocaleTexts","containerRef","useRef","isGeneratingStyles","setIsGeneratingStyles","useState","isGeneratingScripts","setIsGeneratingScripts","hasRenderedContent","setHasRenderedContent","appendedStylesRef","appendedScriptsRef","styleStartRef","scriptStartRef","styleTimerRef","scriptTimerRef","hasStylesRef","hasScriptsRef","prevResetTokenRef","MIN_LOADING_MS","clearTimer","timerRef","settleStateWithMinimumDelay","setter","startRef","onDone","elapsed","delay","useEffect","doc","styleId","styleEl","container","body","reusableImages","wrapper","isAnchorElement","shouldForceAnchorHrefToNewTab","mergeAnchorRelValue","openScriptCount","closeScriptCount","shouldExecuteScripts","resourceQueue","cloned","attr","replacement","hasStyles","hasScripts","contentNodes","hasInitialContent","handleLinkClick","event","anchor","resolveClosestAnchor","scriptContentObserver","markRenderedContentFromContainer","scriptNode","scriptText","overlayMessage","isBlackboard","shouldStretchRootHeight","sandboxWrapperStyle","sandboxContainerStyle","jsxs","jsx","LoadingOverlayCard"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { j as R } from "../../_virtual/jsx-runtime.es.js";
|
|
2
2
|
import { useRef as f, useState as q, useEffect as N } from "react";
|
|
3
|
-
import
|
|
4
|
-
import { getContentRenderLocaleTexts as
|
|
5
|
-
|
|
3
|
+
import ie from "../ui/loading-overlay-card.es.js";
|
|
4
|
+
import { getContentRenderLocaleTexts as le } from "./contentRenderI18n.es.js";
|
|
5
|
+
import { isAnchorElement as ue, shouldForceAnchorHrefToNewTab as P, mergeAnchorRelValue as X, resolveClosestAnchor as fe } from "./utils/link-targets.es.js";
|
|
6
|
+
const de = [
|
|
6
7
|
"src",
|
|
7
8
|
"srcset",
|
|
8
9
|
"sizes",
|
|
@@ -16,55 +17,55 @@ const ce = [
|
|
|
16
17
|
"crossorigin",
|
|
17
18
|
"referrerpolicy",
|
|
18
19
|
"fetchpriority"
|
|
19
|
-
],
|
|
20
|
+
], Y = (o) => de.map(
|
|
20
21
|
(n) => `${n}:${o.getAttribute(n) || ""}`
|
|
21
|
-
).join("|"),
|
|
22
|
+
).join("|"), he = (o) => {
|
|
22
23
|
const n = /* @__PURE__ */ new Map();
|
|
23
24
|
return o.querySelectorAll("img").forEach((i) => {
|
|
24
|
-
const l = i,
|
|
25
|
-
|
|
25
|
+
const l = i, s = Y(l), a = n.get(s) || [];
|
|
26
|
+
a.push(l), n.set(s, a);
|
|
26
27
|
}), n;
|
|
27
|
-
},
|
|
28
|
+
}, me = (o, n) => {
|
|
28
29
|
const i = Array.from(o.attributes), l = new Set(
|
|
29
|
-
i.map((
|
|
30
|
+
i.map((s) => s.name)
|
|
30
31
|
);
|
|
31
|
-
Array.from(n.attributes).forEach((
|
|
32
|
-
l.has(
|
|
33
|
-
}), i.forEach((
|
|
34
|
-
n.setAttribute(
|
|
32
|
+
Array.from(n.attributes).forEach((s) => {
|
|
33
|
+
l.has(s.name) || n.removeAttribute(s.name);
|
|
34
|
+
}), i.forEach((s) => {
|
|
35
|
+
n.setAttribute(s.name, s.value);
|
|
35
36
|
});
|
|
36
|
-
},
|
|
37
|
+
}, pe = (o, n) => {
|
|
37
38
|
n.size && o.querySelectorAll("img").forEach((i) => {
|
|
38
|
-
const l = i,
|
|
39
|
-
p && (
|
|
39
|
+
const l = i, s = Y(l), a = n.get(s), p = a?.shift();
|
|
40
|
+
p && (me(l, p), l.replaceWith(p), a && a.length === 0 && n.delete(s));
|
|
40
41
|
});
|
|
41
|
-
},
|
|
42
|
+
}, Ae = ({
|
|
42
43
|
html: o,
|
|
43
44
|
locale: n,
|
|
44
45
|
loadingText: i,
|
|
45
46
|
styleLoadingText: l,
|
|
46
|
-
scriptLoadingText:
|
|
47
|
-
resetToken:
|
|
47
|
+
scriptLoadingText: s,
|
|
48
|
+
resetToken: a = 0,
|
|
48
49
|
mode: p = "content",
|
|
49
|
-
hasRootVhHeight:
|
|
50
|
-
stretchRootHeight:
|
|
51
|
-
enableScaling:
|
|
52
|
-
disableLoadingOverlay:
|
|
50
|
+
hasRootVhHeight: Z = !1,
|
|
51
|
+
stretchRootHeight: ee = !1,
|
|
52
|
+
enableScaling: F = !1,
|
|
53
|
+
disableLoadingOverlay: k = !1
|
|
53
54
|
}) => {
|
|
54
|
-
const
|
|
55
|
+
const L = le(n), T = f(null), [te, A] = q(!1), [re, S] = q(!1), [ne, j] = q(!1), _ = f([]), H = f([]), z = f(0), U = f(0), y = f(null), g = f(null), b = f(!1), x = f(!1), V = f(a), se = 200, d = (t) => {
|
|
55
56
|
t.current && (clearTimeout(t.current), t.current = null);
|
|
56
|
-
},
|
|
57
|
-
const
|
|
58
|
-
d(
|
|
59
|
-
t(!1), D?.(),
|
|
57
|
+
}, W = (t, c, u, D) => {
|
|
58
|
+
const h = performance.now() - u.current, I = Math.max(0, se - h);
|
|
59
|
+
d(c), c.current = window.setTimeout(() => {
|
|
60
|
+
t(!1), D?.(), c.current = null;
|
|
60
61
|
}, I);
|
|
61
62
|
};
|
|
62
63
|
N(() => {
|
|
63
|
-
const t =
|
|
64
|
+
const t = T.current?.ownerDocument;
|
|
64
65
|
if (!t) return;
|
|
65
|
-
const
|
|
66
|
-
let u = t.getElementById(
|
|
67
|
-
u || (u = t.createElement("style"), u.id =
|
|
66
|
+
const c = "sandbox-spinner-style";
|
|
67
|
+
let u = t.getElementById(c);
|
|
68
|
+
u || (u = t.createElement("style"), u.id = c, t.head?.appendChild(u)), u.textContent = `
|
|
68
69
|
.sandbox-wrapper { align-items: center; }
|
|
69
70
|
.sandbox-container { position: relative; width: 100%; }
|
|
70
71
|
.sandbox-container svg,
|
|
@@ -85,76 +86,86 @@ const ce = [
|
|
|
85
86
|
.overflow-y-auto { overflow-y: visible !important; }
|
|
86
87
|
`;
|
|
87
88
|
}, []), N(() => {
|
|
88
|
-
|
|
89
|
-
const t =
|
|
89
|
+
a !== V.current && (j(!1), V.current = a), d(y), d(g), b.current = !1, x.current = !1;
|
|
90
|
+
const t = T.current;
|
|
90
91
|
if (!t) return;
|
|
91
|
-
const
|
|
92
|
+
const c = t.ownerDocument, u = c?.body;
|
|
92
93
|
if (!u) return;
|
|
93
|
-
const D =
|
|
94
|
-
_.current.forEach((e) => e.remove()), _.current = [],
|
|
95
|
-
const
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
94
|
+
const D = he(t);
|
|
95
|
+
_.current.forEach((e) => e.remove()), _.current = [], H.current.forEach((e) => e.remove()), H.current = [], A(!1), S(!1);
|
|
96
|
+
const h = c.createElement("div");
|
|
97
|
+
h.innerHTML = o, h.querySelectorAll("a[href]").forEach((e) => {
|
|
98
|
+
ue(e) && P(e.getAttribute("href")) && (e.setAttribute("target", "_blank"), e.setAttribute("rel", X(e.getAttribute("rel"))));
|
|
99
|
+
});
|
|
100
|
+
const I = (o.match(/<script[\s>]/gi) || []).length, ae = (o.match(/<\/script>/gi) || []).length, B = I > 0 && I === ae, v = [];
|
|
101
|
+
Array.from(h.querySelectorAll("style, script")).forEach((e) => {
|
|
99
102
|
if (e.tagName.toLowerCase() === "style") {
|
|
100
|
-
const
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
}),
|
|
103
|
+
const r = c.createElement("style");
|
|
104
|
+
r.textContent = e.textContent || "", Array.from(e.attributes).forEach((m) => {
|
|
105
|
+
r.setAttribute(m.name, m.value);
|
|
106
|
+
}), v.push(r);
|
|
104
107
|
} else {
|
|
105
|
-
const
|
|
106
|
-
Array.from(e.attributes).forEach((
|
|
107
|
-
|
|
108
|
-
}),
|
|
108
|
+
const r = c.createElement("script");
|
|
109
|
+
Array.from(e.attributes).forEach((m) => {
|
|
110
|
+
r.setAttribute(m.name, m.value);
|
|
111
|
+
}), r.textContent = e.textContent || "", v.push(r);
|
|
109
112
|
}
|
|
110
113
|
e.remove();
|
|
111
|
-
}),
|
|
112
|
-
const O =
|
|
114
|
+
}), pe(h, D);
|
|
115
|
+
const O = v.some(
|
|
113
116
|
(e) => e.tagName.toLowerCase() === "style"
|
|
114
|
-
), G =
|
|
117
|
+
), G = v.some(
|
|
115
118
|
(e) => e.tagName.toLowerCase() === "script"
|
|
116
119
|
);
|
|
117
|
-
O && (b.current = !0,
|
|
118
|
-
const $ = Array.from(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
120
|
+
O && (b.current = !0, z.current = performance.now(), d(y), A(!0)), G && (x.current = !0, U.current = performance.now(), d(g), S(!0));
|
|
121
|
+
const $ = Array.from(h.childNodes), K = $.length > 0;
|
|
122
|
+
j(K), t.replaceChildren(...$);
|
|
123
|
+
const Q = (e) => {
|
|
124
|
+
const r = fe(e.target);
|
|
125
|
+
r && P(r.getAttribute("href")) && (r.setAttribute("target", "_blank"), r.setAttribute(
|
|
126
|
+
"rel",
|
|
127
|
+
X(r.getAttribute("rel"))
|
|
128
|
+
));
|
|
129
|
+
};
|
|
130
|
+
t.addEventListener("click", Q, !0);
|
|
131
|
+
let w = null;
|
|
132
|
+
const J = () => t.childNodes.length === 0 ? !1 : (j(!0), w?.disconnect(), w = null, !0);
|
|
133
|
+
return !K && G && B && (w = new MutationObserver(() => {
|
|
134
|
+
J();
|
|
135
|
+
}), w.observe(t, {
|
|
125
136
|
childList: !0,
|
|
126
137
|
subtree: !0
|
|
127
|
-
})),
|
|
138
|
+
})), v.forEach((e) => {
|
|
128
139
|
if (e.tagName.toLowerCase() === "style") {
|
|
129
|
-
|
|
140
|
+
c.head?.appendChild(e), _.current.push(e);
|
|
130
141
|
return;
|
|
131
142
|
}
|
|
132
|
-
if (
|
|
133
|
-
const
|
|
134
|
-
if (!
|
|
143
|
+
if (B) {
|
|
144
|
+
const r = e, m = r.textContent || "";
|
|
145
|
+
if (!r.src)
|
|
135
146
|
try {
|
|
136
|
-
new Function(
|
|
147
|
+
new Function(m);
|
|
137
148
|
} catch {
|
|
138
|
-
|
|
149
|
+
r.remove();
|
|
139
150
|
return;
|
|
140
151
|
}
|
|
141
152
|
try {
|
|
142
|
-
u.appendChild(
|
|
153
|
+
u.appendChild(r), H.current.push(r);
|
|
143
154
|
} catch {
|
|
144
|
-
|
|
155
|
+
r.remove();
|
|
145
156
|
}
|
|
146
157
|
} else
|
|
147
158
|
e.remove();
|
|
148
|
-
}),
|
|
149
|
-
O &&
|
|
150
|
-
|
|
159
|
+
}), J(), F && t.ownerDocument?.defaultView?.__mdf_triggerFitContent?.(), requestAnimationFrame(() => {
|
|
160
|
+
O && W(
|
|
161
|
+
A,
|
|
151
162
|
y,
|
|
152
|
-
|
|
163
|
+
z,
|
|
153
164
|
() => {
|
|
154
165
|
b.current = !1;
|
|
155
166
|
}
|
|
156
|
-
), G &&
|
|
157
|
-
|
|
167
|
+
), G && W(
|
|
168
|
+
S,
|
|
158
169
|
g,
|
|
159
170
|
U,
|
|
160
171
|
() => {
|
|
@@ -162,49 +173,49 @@ const ce = [
|
|
|
162
173
|
}
|
|
163
174
|
);
|
|
164
175
|
}), () => {
|
|
165
|
-
|
|
176
|
+
w?.disconnect(), t.removeEventListener("click", Q, !0);
|
|
166
177
|
};
|
|
167
|
-
}, [o,
|
|
178
|
+
}, [o, a, F]), N(
|
|
168
179
|
() => () => {
|
|
169
180
|
d(y), d(g);
|
|
170
181
|
},
|
|
171
182
|
[]
|
|
172
183
|
), N(() => {
|
|
173
|
-
|
|
174
|
-
}, [
|
|
175
|
-
const
|
|
184
|
+
k && (d(y), d(g), b.current = !1, x.current = !1, A(!1), S(!1));
|
|
185
|
+
}, [k]);
|
|
186
|
+
const C = k ? null : re || x.current ? s || i || L.sandboxScriptLoadingText : te || b.current ? l || i || L.sandboxStyleLoadingText : ne ? null : i || L.sandboxLoadingText, M = p === "blackboard", E = M && ee, oe = {
|
|
176
187
|
position: "relative",
|
|
177
188
|
width: "100%",
|
|
178
189
|
height: "100%",
|
|
179
190
|
display: "flex",
|
|
180
191
|
flexDirection: "column",
|
|
181
192
|
// Keep blackboard scroll behavior while centering content in non-blackboard mode
|
|
182
|
-
justifyContent:
|
|
183
|
-
},
|
|
184
|
-
pointerEvents:
|
|
185
|
-
margin:
|
|
193
|
+
justifyContent: E ? "flex-start" : M ? "space-around" : "flex-start"
|
|
194
|
+
}, ce = {
|
|
195
|
+
pointerEvents: C ? "none" : void 0,
|
|
196
|
+
margin: M ? void 0 : "auto 0",
|
|
186
197
|
width: "100%",
|
|
187
|
-
height:
|
|
188
|
-
minHeight:
|
|
189
|
-
flex:
|
|
198
|
+
height: E ? "100%" : void 0,
|
|
199
|
+
minHeight: E ? 0 : void 0,
|
|
200
|
+
flex: E ? "1 1 auto" : void 0
|
|
190
201
|
};
|
|
191
202
|
return /* @__PURE__ */ R.jsxs(
|
|
192
203
|
"div",
|
|
193
204
|
{
|
|
194
|
-
"data-root-vh":
|
|
205
|
+
"data-root-vh": Z ? "true" : "false",
|
|
195
206
|
className: "sandbox-wrapper",
|
|
196
|
-
style:
|
|
197
|
-
"aria-busy": !!
|
|
207
|
+
style: oe,
|
|
208
|
+
"aria-busy": !!C,
|
|
198
209
|
children: [
|
|
199
210
|
/* @__PURE__ */ R.jsx(
|
|
200
211
|
"div",
|
|
201
212
|
{
|
|
202
|
-
ref:
|
|
213
|
+
ref: T,
|
|
203
214
|
className: "sandbox-container",
|
|
204
|
-
style:
|
|
215
|
+
style: ce
|
|
205
216
|
}
|
|
206
217
|
),
|
|
207
|
-
|
|
218
|
+
C && /* @__PURE__ */ R.jsx(
|
|
208
219
|
"div",
|
|
209
220
|
{
|
|
210
221
|
style: {
|
|
@@ -216,7 +227,7 @@ const ce = [
|
|
|
216
227
|
justifyContent: "center",
|
|
217
228
|
pointerEvents: "none"
|
|
218
229
|
},
|
|
219
|
-
children: /* @__PURE__ */ R.jsx(
|
|
230
|
+
children: /* @__PURE__ */ R.jsx(ie, { message: C })
|
|
220
231
|
}
|
|
221
232
|
)
|
|
222
233
|
]
|
|
@@ -224,6 +235,6 @@ const ce = [
|
|
|
224
235
|
);
|
|
225
236
|
};
|
|
226
237
|
export {
|
|
227
|
-
|
|
238
|
+
Ae as default
|
|
228
239
|
};
|
|
229
240
|
//# sourceMappingURL=SandboxApp.es.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SandboxApp.es.js","sources":["../../../src/components/ContentRender/SandboxApp.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport LoadingOverlayCard from \"../ui/loading-overlay-card\";\nimport type { MarkdownFlowLocale } from \"../../lib/locale\";\nimport { getContentRenderLocaleTexts } from \"./contentRenderI18n\";\nimport type { ScalingWindow } from \"./utils/iframe-scaling\";\n\nexport interface SandboxAppProps {\n html: string;\n locale?: MarkdownFlowLocale;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n resetToken?: number;\n mode?: \"content\" | \"blackboard\";\n hasRootVhHeight?: boolean;\n stretchRootHeight?: boolean;\n enableScaling?: boolean;\n disableLoadingOverlay?: boolean;\n}\n\nconst IMAGE_REUSE_ATTRIBUTES = [\n \"src\",\n \"srcset\",\n \"sizes\",\n \"alt\",\n \"class\",\n \"width\",\n \"height\",\n \"style\",\n \"loading\",\n \"decoding\",\n \"crossorigin\",\n \"referrerpolicy\",\n \"fetchpriority\",\n];\n\nconst getImageReuseKey = (image: HTMLImageElement) =>\n IMAGE_REUSE_ATTRIBUTES.map(\n (attribute) => `${attribute}:${image.getAttribute(attribute) || \"\"}`\n ).join(\"|\");\n\nconst collectReusableImages = (root: ParentNode) => {\n const imageMap = new Map<string, HTMLImageElement[]>();\n root.querySelectorAll(\"img\").forEach((node) => {\n const image = node as HTMLImageElement;\n const key = getImageReuseKey(image);\n const bucket = imageMap.get(key) || [];\n bucket.push(image);\n imageMap.set(key, bucket);\n });\n return imageMap;\n};\n\nconst syncImageAttributes = (\n sourceImage: HTMLImageElement,\n targetImage: HTMLImageElement\n) => {\n const sourceAttributes = Array.from(sourceImage.attributes);\n const sourceAttributeNames = new Set(\n sourceAttributes.map((attribute) => attribute.name)\n );\n\n Array.from(targetImage.attributes).forEach((attribute) => {\n if (!sourceAttributeNames.has(attribute.name)) {\n targetImage.removeAttribute(attribute.name);\n }\n });\n\n sourceAttributes.forEach((attribute) => {\n targetImage.setAttribute(attribute.name, attribute.value);\n });\n};\n\nconst reuseRenderedImages = (\n root: ParentNode,\n imageMap: Map<string, HTMLImageElement[]>\n) => {\n if (!imageMap.size) return;\n\n root.querySelectorAll(\"img\").forEach((node) => {\n const nextImage = node as HTMLImageElement;\n const key = getImageReuseKey(nextImage);\n const bucket = imageMap.get(key);\n const preservedImage = bucket?.shift();\n if (!preservedImage) return;\n\n syncImageAttributes(nextImage, preservedImage);\n nextImage.replaceWith(preservedImage);\n\n if (bucket && bucket.length === 0) {\n imageMap.delete(key);\n }\n });\n};\n\nconst SandboxApp: React.FC<SandboxAppProps> = ({\n html,\n locale,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n resetToken = 0,\n mode = \"content\",\n hasRootVhHeight = false,\n stretchRootHeight = false,\n enableScaling = false,\n disableLoadingOverlay = false,\n}) => {\n const localeTexts = getContentRenderLocaleTexts(locale);\n const containerRef = useRef<HTMLDivElement>(null);\n const [isGeneratingStyles, setIsGeneratingStyles] = useState(false);\n const [isGeneratingScripts, setIsGeneratingScripts] = useState(false);\n const [hasRenderedContent, setHasRenderedContent] = useState(false);\n const appendedStylesRef = useRef<HTMLStyleElement[]>([]);\n const appendedScriptsRef = useRef<HTMLScriptElement[]>([]);\n const styleStartRef = useRef(0);\n const scriptStartRef = useRef(0);\n const styleTimerRef = useRef<number | null>(null);\n const scriptTimerRef = useRef<number | null>(null);\n const hasStylesRef = useRef(false);\n const hasScriptsRef = useRef(false);\n const prevResetTokenRef = useRef(resetToken);\n const MIN_LOADING_MS = 200;\n\n const clearTimer = (timerRef: React.MutableRefObject<number | null>) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const settleStateWithMinimumDelay = (\n setter: React.Dispatch<React.SetStateAction<boolean>>,\n timerRef: React.MutableRefObject<number | null>,\n startRef: React.MutableRefObject<number>,\n onDone?: () => void\n ) => {\n const elapsed = performance.now() - startRef.current;\n const delay = Math.max(0, MIN_LOADING_MS - elapsed);\n clearTimer(timerRef);\n timerRef.current = window.setTimeout(() => {\n setter(false);\n onDone?.();\n timerRef.current = null;\n }, delay);\n };\n\n useEffect(() => {\n const doc = containerRef.current?.ownerDocument;\n if (!doc) return;\n const styleId = \"sandbox-spinner-style\";\n let styleEl = doc.getElementById(styleId) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = doc.createElement(\"style\");\n styleEl.id = styleId;\n doc.head?.appendChild(styleEl);\n }\n styleEl.textContent = `\n .sandbox-wrapper { align-items: center; }\n .sandbox-container { position: relative; width: 100%; }\n .sandbox-container svg,\n .sandbox-container img { display: block; margin-left: auto; margin-right: auto; }\n /* Hide scroll UI without removing scroll semantics. Content roots with\n inline style=\"height:100vh; overflow-y:auto\" are the signal that\n IframeSandbox.getInnerScrollableHeight relies on to grow the iframe\n when content exceeds the viewport. We must not change overflow-y,\n but sub-pixel rounding (1px below the +1 detection threshold) can\n still surface a phantom scrollbar on content that visually fits.\n Hide the scrollbar UI globally; when content truly overflows the\n iframe is resized and no scrollbar would render anyway. */\n .sandbox-container *::-webkit-scrollbar { display: none; }\n .sandbox-container * { scrollbar-width: none; -ms-overflow-style: none; }\n .justify-\\\\[safe_center\\\\]{\n justify-content: safe center;\n }\n .overflow-y-auto { overflow-y: visible !important; }\n `;\n }, []);\n\n useEffect(() => {\n if (resetToken !== prevResetTokenRef.current) {\n setHasRenderedContent(false);\n prevResetTokenRef.current = resetToken;\n }\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n\n const container = containerRef.current;\n if (!container) return;\n const doc = container.ownerDocument;\n const body = doc?.body;\n if (!body) return;\n const reusableImages = collectReusableImages(container);\n\n appendedStylesRef.current.forEach((node) => node.remove());\n appendedStylesRef.current = [];\n appendedScriptsRef.current.forEach((node) => node.remove());\n appendedScriptsRef.current = [];\n\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n const wrapper = doc.createElement(\"div\");\n wrapper.innerHTML = html;\n\n const openScriptCount = (html.match(/<script[\\s>]/gi) || []).length;\n const closeScriptCount = (html.match(/<\\/script>/gi) || []).length;\n const shouldExecuteScripts =\n openScriptCount > 0 && openScriptCount === closeScriptCount;\n\n const resourceQueue: HTMLElement[] = [];\n\n Array.from(wrapper.querySelectorAll(\"style, script\")).forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n const cloned = doc.createElement(\"style\");\n cloned.textContent = node.textContent || \"\";\n Array.from(node.attributes).forEach((attr) => {\n cloned.setAttribute(attr.name, attr.value);\n });\n resourceQueue.push(cloned);\n } else {\n const replacement = doc.createElement(\"script\");\n Array.from(node.attributes).forEach((attr) => {\n replacement.setAttribute(attr.name, attr.value);\n });\n replacement.textContent = node.textContent || \"\";\n resourceQueue.push(replacement);\n }\n node.remove();\n });\n reuseRenderedImages(wrapper, reusableImages);\n\n const hasStyles = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"style\"\n );\n const hasScripts = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"script\"\n );\n if (hasStyles) {\n hasStylesRef.current = true;\n styleStartRef.current = performance.now();\n clearTimer(styleTimerRef);\n setIsGeneratingStyles(true);\n }\n if (hasScripts) {\n hasScriptsRef.current = true;\n scriptStartRef.current = performance.now();\n clearTimer(scriptTimerRef);\n setIsGeneratingScripts(true);\n }\n\n const contentNodes = Array.from(wrapper.childNodes);\n const hasInitialContent = contentNodes.length > 0;\n setHasRenderedContent(hasInitialContent);\n container.replaceChildren(...contentNodes);\n\n let scriptContentObserver: MutationObserver | null = null;\n const markRenderedContentFromContainer = () => {\n if (container.childNodes.length === 0) {\n return false;\n }\n\n setHasRenderedContent(true);\n scriptContentObserver?.disconnect();\n scriptContentObserver = null;\n return true;\n };\n if (!hasInitialContent && hasScripts && shouldExecuteScripts) {\n scriptContentObserver = new MutationObserver(() => {\n markRenderedContentFromContainer();\n });\n scriptContentObserver.observe(container, {\n childList: true,\n subtree: true,\n });\n }\n\n resourceQueue.forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n doc.head?.appendChild(node);\n appendedStylesRef.current.push(node as HTMLStyleElement);\n return;\n }\n\n if (shouldExecuteScripts) {\n const scriptNode = node as HTMLScriptElement;\n const scriptText = scriptNode.textContent || \"\";\n const shouldValidate = !scriptNode.src;\n\n if (shouldValidate) {\n try {\n // Validate script is syntactically complete before executing\n\n new Function(scriptText);\n } catch {\n scriptNode.remove();\n return;\n }\n }\n\n try {\n body.appendChild(scriptNode);\n appendedScriptsRef.current.push(scriptNode);\n } catch {\n scriptNode.remove();\n }\n } else {\n // Defer execution until all script tags are fully received\n node.remove();\n }\n });\n markRenderedContentFromContainer();\n if (enableScaling) {\n const win = container.ownerDocument?.defaultView as ScalingWindow | null;\n win?.__mdf_triggerFitContent?.();\n }\n\n requestAnimationFrame(() => {\n if (hasStyles) {\n settleStateWithMinimumDelay(\n setIsGeneratingStyles,\n styleTimerRef,\n styleStartRef,\n () => {\n hasStylesRef.current = false;\n }\n );\n }\n if (hasScripts) {\n settleStateWithMinimumDelay(\n setIsGeneratingScripts,\n scriptTimerRef,\n scriptStartRef,\n () => {\n hasScriptsRef.current = false;\n }\n );\n }\n });\n\n return () => {\n scriptContentObserver?.disconnect();\n };\n }, [html, resetToken, enableScaling]);\n\n useEffect(\n () => () => {\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n },\n []\n );\n\n useEffect(() => {\n if (!disableLoadingOverlay) {\n return;\n }\n\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n }, [disableLoadingOverlay]);\n\n const overlayMessage = (() => {\n if (disableLoadingOverlay) {\n return null;\n }\n\n if (isGeneratingScripts || hasScriptsRef.current)\n return (\n scriptLoadingText || loadingText || localeTexts.sandboxScriptLoadingText\n );\n if (isGeneratingStyles || hasStylesRef.current)\n return (\n styleLoadingText || loadingText || localeTexts.sandboxStyleLoadingText\n );\n if (!hasRenderedContent)\n return loadingText || localeTexts.sandboxLoadingText;\n return null;\n })();\n\n const isBlackboard = mode === \"blackboard\";\n const shouldStretchRootHeight = isBlackboard && stretchRootHeight;\n const sandboxWrapperStyle: React.CSSProperties = {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n // Keep blackboard scroll behavior while centering content in non-blackboard mode\n justifyContent: shouldStretchRootHeight\n ? \"flex-start\"\n : isBlackboard\n ? \"space-around\"\n : \"flex-start\",\n };\n const sandboxContainerStyle: React.CSSProperties = {\n pointerEvents: overlayMessage ? \"none\" : undefined,\n margin: isBlackboard ? undefined : \"auto 0\",\n width: \"100%\",\n height: shouldStretchRootHeight ? \"100%\" : undefined,\n minHeight: shouldStretchRootHeight ? 0 : undefined,\n flex: shouldStretchRootHeight ? \"1 1 auto\" : undefined,\n };\n\n return (\n <div\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className=\"sandbox-wrapper\"\n style={sandboxWrapperStyle}\n aria-busy={!!overlayMessage}\n >\n <div\n ref={containerRef}\n className=\"sandbox-container\"\n style={sandboxContainerStyle}\n />\n {overlayMessage && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n }}\n >\n <LoadingOverlayCard message={overlayMessage} />\n </div>\n )}\n </div>\n );\n};\n\nexport default SandboxApp;\n"],"names":["IMAGE_REUSE_ATTRIBUTES","getImageReuseKey","image","attribute","collectReusableImages","root","imageMap","node","key","bucket","syncImageAttributes","sourceImage","targetImage","sourceAttributes","sourceAttributeNames","reuseRenderedImages","nextImage","preservedImage","SandboxApp","html","locale","loadingText","styleLoadingText","scriptLoadingText","resetToken","mode","hasRootVhHeight","stretchRootHeight","enableScaling","disableLoadingOverlay","localeTexts","getContentRenderLocaleTexts","containerRef","useRef","isGeneratingStyles","setIsGeneratingStyles","useState","isGeneratingScripts","setIsGeneratingScripts","hasRenderedContent","setHasRenderedContent","appendedStylesRef","appendedScriptsRef","styleStartRef","scriptStartRef","styleTimerRef","scriptTimerRef","hasStylesRef","hasScriptsRef","prevResetTokenRef","MIN_LOADING_MS","clearTimer","timerRef","settleStateWithMinimumDelay","setter","startRef","onDone","elapsed","delay","useEffect","doc","styleId","styleEl","container","body","reusableImages","wrapper","openScriptCount","closeScriptCount","shouldExecuteScripts","resourceQueue","cloned","attr","replacement","hasStyles","hasScripts","contentNodes","hasInitialContent","scriptContentObserver","markRenderedContentFromContainer","scriptNode","scriptText","overlayMessage","isBlackboard","shouldStretchRootHeight","sandboxWrapperStyle","sandboxContainerStyle","jsxs","jsx","LoadingOverlayCard"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"SandboxApp.es.js","sources":["../../../src/components/ContentRender/SandboxApp.tsx"],"sourcesContent":["import React, { useEffect, useRef, useState } from \"react\";\nimport LoadingOverlayCard from \"../ui/loading-overlay-card\";\nimport type { MarkdownFlowLocale } from \"../../lib/locale\";\nimport { getContentRenderLocaleTexts } from \"./contentRenderI18n\";\nimport type { ScalingWindow } from \"./utils/iframe-scaling\";\nimport {\n isAnchorElement,\n mergeAnchorRelValue,\n resolveClosestAnchor,\n shouldForceAnchorHrefToNewTab,\n} from \"./utils/link-targets\";\n\nexport interface SandboxAppProps {\n html: string;\n locale?: MarkdownFlowLocale;\n loadingText?: string;\n styleLoadingText?: string;\n scriptLoadingText?: string;\n resetToken?: number;\n mode?: \"content\" | \"blackboard\";\n hasRootVhHeight?: boolean;\n stretchRootHeight?: boolean;\n enableScaling?: boolean;\n disableLoadingOverlay?: boolean;\n}\n\nconst IMAGE_REUSE_ATTRIBUTES = [\n \"src\",\n \"srcset\",\n \"sizes\",\n \"alt\",\n \"class\",\n \"width\",\n \"height\",\n \"style\",\n \"loading\",\n \"decoding\",\n \"crossorigin\",\n \"referrerpolicy\",\n \"fetchpriority\",\n];\n\nconst getImageReuseKey = (image: HTMLImageElement) =>\n IMAGE_REUSE_ATTRIBUTES.map(\n (attribute) => `${attribute}:${image.getAttribute(attribute) || \"\"}`\n ).join(\"|\");\n\nconst collectReusableImages = (root: ParentNode) => {\n const imageMap = new Map<string, HTMLImageElement[]>();\n root.querySelectorAll(\"img\").forEach((node) => {\n const image = node as HTMLImageElement;\n const key = getImageReuseKey(image);\n const bucket = imageMap.get(key) || [];\n bucket.push(image);\n imageMap.set(key, bucket);\n });\n return imageMap;\n};\n\nconst syncImageAttributes = (\n sourceImage: HTMLImageElement,\n targetImage: HTMLImageElement\n) => {\n const sourceAttributes = Array.from(sourceImage.attributes);\n const sourceAttributeNames = new Set(\n sourceAttributes.map((attribute) => attribute.name)\n );\n\n Array.from(targetImage.attributes).forEach((attribute) => {\n if (!sourceAttributeNames.has(attribute.name)) {\n targetImage.removeAttribute(attribute.name);\n }\n });\n\n sourceAttributes.forEach((attribute) => {\n targetImage.setAttribute(attribute.name, attribute.value);\n });\n};\n\nconst reuseRenderedImages = (\n root: ParentNode,\n imageMap: Map<string, HTMLImageElement[]>\n) => {\n if (!imageMap.size) return;\n\n root.querySelectorAll(\"img\").forEach((node) => {\n const nextImage = node as HTMLImageElement;\n const key = getImageReuseKey(nextImage);\n const bucket = imageMap.get(key);\n const preservedImage = bucket?.shift();\n if (!preservedImage) return;\n\n syncImageAttributes(nextImage, preservedImage);\n nextImage.replaceWith(preservedImage);\n\n if (bucket && bucket.length === 0) {\n imageMap.delete(key);\n }\n });\n};\n\nconst SandboxApp: React.FC<SandboxAppProps> = ({\n html,\n locale,\n loadingText,\n styleLoadingText,\n scriptLoadingText,\n resetToken = 0,\n mode = \"content\",\n hasRootVhHeight = false,\n stretchRootHeight = false,\n enableScaling = false,\n disableLoadingOverlay = false,\n}) => {\n const localeTexts = getContentRenderLocaleTexts(locale);\n const containerRef = useRef<HTMLDivElement>(null);\n const [isGeneratingStyles, setIsGeneratingStyles] = useState(false);\n const [isGeneratingScripts, setIsGeneratingScripts] = useState(false);\n const [hasRenderedContent, setHasRenderedContent] = useState(false);\n const appendedStylesRef = useRef<HTMLStyleElement[]>([]);\n const appendedScriptsRef = useRef<HTMLScriptElement[]>([]);\n const styleStartRef = useRef(0);\n const scriptStartRef = useRef(0);\n const styleTimerRef = useRef<number | null>(null);\n const scriptTimerRef = useRef<number | null>(null);\n const hasStylesRef = useRef(false);\n const hasScriptsRef = useRef(false);\n const prevResetTokenRef = useRef(resetToken);\n const MIN_LOADING_MS = 200;\n\n const clearTimer = (timerRef: React.MutableRefObject<number | null>) => {\n if (timerRef.current) {\n clearTimeout(timerRef.current);\n timerRef.current = null;\n }\n };\n\n const settleStateWithMinimumDelay = (\n setter: React.Dispatch<React.SetStateAction<boolean>>,\n timerRef: React.MutableRefObject<number | null>,\n startRef: React.MutableRefObject<number>,\n onDone?: () => void\n ) => {\n const elapsed = performance.now() - startRef.current;\n const delay = Math.max(0, MIN_LOADING_MS - elapsed);\n clearTimer(timerRef);\n timerRef.current = window.setTimeout(() => {\n setter(false);\n onDone?.();\n timerRef.current = null;\n }, delay);\n };\n\n useEffect(() => {\n const doc = containerRef.current?.ownerDocument;\n if (!doc) return;\n const styleId = \"sandbox-spinner-style\";\n let styleEl = doc.getElementById(styleId) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = doc.createElement(\"style\");\n styleEl.id = styleId;\n doc.head?.appendChild(styleEl);\n }\n styleEl.textContent = `\n .sandbox-wrapper { align-items: center; }\n .sandbox-container { position: relative; width: 100%; }\n .sandbox-container svg,\n .sandbox-container img { display: block; margin-left: auto; margin-right: auto; }\n /* Hide scroll UI without removing scroll semantics. Content roots with\n inline style=\"height:100vh; overflow-y:auto\" are the signal that\n IframeSandbox.getInnerScrollableHeight relies on to grow the iframe\n when content exceeds the viewport. We must not change overflow-y,\n but sub-pixel rounding (1px below the +1 detection threshold) can\n still surface a phantom scrollbar on content that visually fits.\n Hide the scrollbar UI globally; when content truly overflows the\n iframe is resized and no scrollbar would render anyway. */\n .sandbox-container *::-webkit-scrollbar { display: none; }\n .sandbox-container * { scrollbar-width: none; -ms-overflow-style: none; }\n .justify-\\\\[safe_center\\\\]{\n justify-content: safe center;\n }\n .overflow-y-auto { overflow-y: visible !important; }\n `;\n }, []);\n\n useEffect(() => {\n if (resetToken !== prevResetTokenRef.current) {\n setHasRenderedContent(false);\n prevResetTokenRef.current = resetToken;\n }\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n\n const container = containerRef.current;\n if (!container) return;\n const doc = container.ownerDocument;\n const body = doc?.body;\n if (!body) return;\n const reusableImages = collectReusableImages(container);\n\n appendedStylesRef.current.forEach((node) => node.remove());\n appendedStylesRef.current = [];\n appendedScriptsRef.current.forEach((node) => node.remove());\n appendedScriptsRef.current = [];\n\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n const wrapper = doc.createElement(\"div\");\n wrapper.innerHTML = html;\n wrapper.querySelectorAll(\"a[href]\").forEach((node) => {\n if (!isAnchorElement(node)) {\n return;\n }\n\n if (!shouldForceAnchorHrefToNewTab(node.getAttribute(\"href\"))) {\n return;\n }\n\n node.setAttribute(\"target\", \"_blank\");\n node.setAttribute(\"rel\", mergeAnchorRelValue(node.getAttribute(\"rel\")));\n });\n\n const openScriptCount = (html.match(/<script[\\s>]/gi) || []).length;\n const closeScriptCount = (html.match(/<\\/script>/gi) || []).length;\n const shouldExecuteScripts =\n openScriptCount > 0 && openScriptCount === closeScriptCount;\n\n const resourceQueue: HTMLElement[] = [];\n\n Array.from(wrapper.querySelectorAll(\"style, script\")).forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n const cloned = doc.createElement(\"style\");\n cloned.textContent = node.textContent || \"\";\n Array.from(node.attributes).forEach((attr) => {\n cloned.setAttribute(attr.name, attr.value);\n });\n resourceQueue.push(cloned);\n } else {\n const replacement = doc.createElement(\"script\");\n Array.from(node.attributes).forEach((attr) => {\n replacement.setAttribute(attr.name, attr.value);\n });\n replacement.textContent = node.textContent || \"\";\n resourceQueue.push(replacement);\n }\n node.remove();\n });\n reuseRenderedImages(wrapper, reusableImages);\n\n const hasStyles = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"style\"\n );\n const hasScripts = resourceQueue.some(\n (node) => node.tagName.toLowerCase() === \"script\"\n );\n if (hasStyles) {\n hasStylesRef.current = true;\n styleStartRef.current = performance.now();\n clearTimer(styleTimerRef);\n setIsGeneratingStyles(true);\n }\n if (hasScripts) {\n hasScriptsRef.current = true;\n scriptStartRef.current = performance.now();\n clearTimer(scriptTimerRef);\n setIsGeneratingScripts(true);\n }\n\n const contentNodes = Array.from(wrapper.childNodes);\n const hasInitialContent = contentNodes.length > 0;\n setHasRenderedContent(hasInitialContent);\n container.replaceChildren(...contentNodes);\n\n const handleLinkClick = (event: MouseEvent) => {\n const anchor = resolveClosestAnchor(event.target);\n if (!anchor) {\n return;\n }\n\n if (!shouldForceAnchorHrefToNewTab(anchor.getAttribute(\"href\"))) {\n return;\n }\n\n anchor.setAttribute(\"target\", \"_blank\");\n anchor.setAttribute(\n \"rel\",\n mergeAnchorRelValue(anchor.getAttribute(\"rel\"))\n );\n };\n container.addEventListener(\"click\", handleLinkClick, true);\n\n let scriptContentObserver: MutationObserver | null = null;\n const markRenderedContentFromContainer = () => {\n if (container.childNodes.length === 0) {\n return false;\n }\n\n setHasRenderedContent(true);\n scriptContentObserver?.disconnect();\n scriptContentObserver = null;\n return true;\n };\n if (!hasInitialContent && hasScripts && shouldExecuteScripts) {\n scriptContentObserver = new MutationObserver(() => {\n markRenderedContentFromContainer();\n });\n scriptContentObserver.observe(container, {\n childList: true,\n subtree: true,\n });\n }\n\n resourceQueue.forEach((node) => {\n if (node.tagName.toLowerCase() === \"style\") {\n doc.head?.appendChild(node);\n appendedStylesRef.current.push(node as HTMLStyleElement);\n return;\n }\n\n if (shouldExecuteScripts) {\n const scriptNode = node as HTMLScriptElement;\n const scriptText = scriptNode.textContent || \"\";\n const shouldValidate = !scriptNode.src;\n\n if (shouldValidate) {\n try {\n // Validate script is syntactically complete before executing\n\n new Function(scriptText);\n } catch {\n scriptNode.remove();\n return;\n }\n }\n\n try {\n body.appendChild(scriptNode);\n appendedScriptsRef.current.push(scriptNode);\n } catch {\n scriptNode.remove();\n }\n } else {\n // Defer execution until all script tags are fully received\n node.remove();\n }\n });\n markRenderedContentFromContainer();\n if (enableScaling) {\n const win = container.ownerDocument?.defaultView as ScalingWindow | null;\n win?.__mdf_triggerFitContent?.();\n }\n\n requestAnimationFrame(() => {\n if (hasStyles) {\n settleStateWithMinimumDelay(\n setIsGeneratingStyles,\n styleTimerRef,\n styleStartRef,\n () => {\n hasStylesRef.current = false;\n }\n );\n }\n if (hasScripts) {\n settleStateWithMinimumDelay(\n setIsGeneratingScripts,\n scriptTimerRef,\n scriptStartRef,\n () => {\n hasScriptsRef.current = false;\n }\n );\n }\n });\n\n return () => {\n scriptContentObserver?.disconnect();\n container.removeEventListener(\"click\", handleLinkClick, true);\n };\n }, [html, resetToken, enableScaling]);\n\n useEffect(\n () => () => {\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n },\n []\n );\n\n useEffect(() => {\n if (!disableLoadingOverlay) {\n return;\n }\n\n clearTimer(styleTimerRef);\n clearTimer(scriptTimerRef);\n hasStylesRef.current = false;\n hasScriptsRef.current = false;\n setIsGeneratingStyles(false);\n setIsGeneratingScripts(false);\n }, [disableLoadingOverlay]);\n\n const overlayMessage = (() => {\n if (disableLoadingOverlay) {\n return null;\n }\n\n if (isGeneratingScripts || hasScriptsRef.current)\n return (\n scriptLoadingText || loadingText || localeTexts.sandboxScriptLoadingText\n );\n if (isGeneratingStyles || hasStylesRef.current)\n return (\n styleLoadingText || loadingText || localeTexts.sandboxStyleLoadingText\n );\n if (!hasRenderedContent)\n return loadingText || localeTexts.sandboxLoadingText;\n return null;\n })();\n\n const isBlackboard = mode === \"blackboard\";\n const shouldStretchRootHeight = isBlackboard && stretchRootHeight;\n const sandboxWrapperStyle: React.CSSProperties = {\n position: \"relative\",\n width: \"100%\",\n height: \"100%\",\n display: \"flex\",\n flexDirection: \"column\",\n // Keep blackboard scroll behavior while centering content in non-blackboard mode\n justifyContent: shouldStretchRootHeight\n ? \"flex-start\"\n : isBlackboard\n ? \"space-around\"\n : \"flex-start\",\n };\n const sandboxContainerStyle: React.CSSProperties = {\n pointerEvents: overlayMessage ? \"none\" : undefined,\n margin: isBlackboard ? undefined : \"auto 0\",\n width: \"100%\",\n height: shouldStretchRootHeight ? \"100%\" : undefined,\n minHeight: shouldStretchRootHeight ? 0 : undefined,\n flex: shouldStretchRootHeight ? \"1 1 auto\" : undefined,\n };\n\n return (\n <div\n data-root-vh={hasRootVhHeight ? \"true\" : \"false\"}\n className=\"sandbox-wrapper\"\n style={sandboxWrapperStyle}\n aria-busy={!!overlayMessage}\n >\n <div\n ref={containerRef}\n className=\"sandbox-container\"\n style={sandboxContainerStyle}\n />\n {overlayMessage && (\n <div\n style={{\n position: \"absolute\",\n inset: 0,\n zIndex: 20,\n display: \"flex\",\n alignItems: \"center\",\n justifyContent: \"center\",\n pointerEvents: \"none\",\n }}\n >\n <LoadingOverlayCard message={overlayMessage} />\n </div>\n )}\n </div>\n );\n};\n\nexport default SandboxApp;\n"],"names":["IMAGE_REUSE_ATTRIBUTES","getImageReuseKey","image","attribute","collectReusableImages","root","imageMap","node","key","bucket","syncImageAttributes","sourceImage","targetImage","sourceAttributes","sourceAttributeNames","reuseRenderedImages","nextImage","preservedImage","SandboxApp","html","locale","loadingText","styleLoadingText","scriptLoadingText","resetToken","mode","hasRootVhHeight","stretchRootHeight","enableScaling","disableLoadingOverlay","localeTexts","getContentRenderLocaleTexts","containerRef","useRef","isGeneratingStyles","setIsGeneratingStyles","useState","isGeneratingScripts","setIsGeneratingScripts","hasRenderedContent","setHasRenderedContent","appendedStylesRef","appendedScriptsRef","styleStartRef","scriptStartRef","styleTimerRef","scriptTimerRef","hasStylesRef","hasScriptsRef","prevResetTokenRef","MIN_LOADING_MS","clearTimer","timerRef","settleStateWithMinimumDelay","setter","startRef","onDone","elapsed","delay","useEffect","doc","styleId","styleEl","container","body","reusableImages","wrapper","isAnchorElement","shouldForceAnchorHrefToNewTab","mergeAnchorRelValue","openScriptCount","closeScriptCount","shouldExecuteScripts","resourceQueue","cloned","attr","replacement","hasStyles","hasScripts","contentNodes","hasInitialContent","handleLinkClick","event","anchor","resolveClosestAnchor","scriptContentObserver","markRenderedContentFromContainer","scriptNode","scriptText","overlayMessage","isBlackboard","shouldStretchRootHeight","sandboxWrapperStyle","sandboxContainerStyle","jsxs","jsx","LoadingOverlayCard"],"mappings":";;;;;AA0BA,MAAMA,KAAyB;AAAA,EAC7B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,GAEMC,IAAmB,CAACC,MACxBF,GAAuB;AAAA,EACrB,CAACG,MAAc,GAAGA,CAAS,IAAID,EAAM,aAAaC,CAAS,KAAK,EAAE;AACpE,EAAE,KAAK,GAAG,GAENC,KAAwB,CAACC,MAAqB;AAClD,QAAMC,wBAAe,IAAA;AACrB,SAAAD,EAAK,iBAAiB,KAAK,EAAE,QAAQ,CAACE,MAAS;AAC7C,UAAML,IAAQK,GACRC,IAAMP,EAAiBC,CAAK,GAC5BO,IAASH,EAAS,IAAIE,CAAG,KAAK,CAAA;AACpC,IAAAC,EAAO,KAAKP,CAAK,GACjBI,EAAS,IAAIE,GAAKC,CAAM;AAAA,EAC1B,CAAC,GACMH;AACT,GAEMI,KAAsB,CAC1BC,GACAC,MACG;AACH,QAAMC,IAAmB,MAAM,KAAKF,EAAY,UAAU,GACpDG,IAAuB,IAAI;AAAA,IAC/BD,EAAiB,IAAI,CAACV,MAAcA,EAAU,IAAI;AAAA,EAAA;AAGpD,QAAM,KAAKS,EAAY,UAAU,EAAE,QAAQ,CAACT,MAAc;AACxD,IAAKW,EAAqB,IAAIX,EAAU,IAAI,KAC1CS,EAAY,gBAAgBT,EAAU,IAAI;AAAA,EAE9C,CAAC,GAEDU,EAAiB,QAAQ,CAACV,MAAc;AACtC,IAAAS,EAAY,aAAaT,EAAU,MAAMA,EAAU,KAAK;AAAA,EAC1D,CAAC;AACH,GAEMY,KAAsB,CAC1BV,GACAC,MACG;AACH,EAAKA,EAAS,QAEdD,EAAK,iBAAiB,KAAK,EAAE,QAAQ,CAACE,MAAS;AAC7C,UAAMS,IAAYT,GACZC,IAAMP,EAAiBe,CAAS,GAChCP,IAASH,EAAS,IAAIE,CAAG,GACzBS,IAAiBR,GAAQ,MAAA;AAC/B,IAAKQ,MAELP,GAAoBM,GAAWC,CAAc,GAC7CD,EAAU,YAAYC,CAAc,GAEhCR,KAAUA,EAAO,WAAW,KAC9BH,EAAS,OAAOE,CAAG;AAAA,EAEvB,CAAC;AACH,GAEMU,KAAwC,CAAC;AAAA,EAC7C,MAAAC;AAAA,EACA,QAAAC;AAAA,EACA,aAAAC;AAAA,EACA,kBAAAC;AAAA,EACA,mBAAAC;AAAA,EACA,YAAAC,IAAa;AAAA,EACb,MAAAC,IAAO;AAAA,EACP,iBAAAC,IAAkB;AAAA,EAClB,mBAAAC,KAAoB;AAAA,EACpB,eAAAC,IAAgB;AAAA,EAChB,uBAAAC,IAAwB;AAC1B,MAAM;AACJ,QAAMC,IAAcC,GAA4BX,CAAM,GAChDY,IAAeC,EAAuB,IAAI,GAC1C,CAACC,IAAoBC,CAAqB,IAAIC,EAAS,EAAK,GAC5D,CAACC,IAAqBC,CAAsB,IAAIF,EAAS,EAAK,GAC9D,CAACG,IAAoBC,CAAqB,IAAIJ,EAAS,EAAK,GAC5DK,IAAoBR,EAA2B,EAAE,GACjDS,IAAqBT,EAA4B,EAAE,GACnDU,IAAgBV,EAAO,CAAC,GACxBW,IAAiBX,EAAO,CAAC,GACzBY,IAAgBZ,EAAsB,IAAI,GAC1Ca,IAAiBb,EAAsB,IAAI,GAC3Cc,IAAed,EAAO,EAAK,GAC3Be,IAAgBf,EAAO,EAAK,GAC5BgB,IAAoBhB,EAAOT,CAAU,GACrC0B,KAAiB,KAEjBC,IAAa,CAACC,MAAoD;AACtE,IAAIA,EAAS,YACX,aAAaA,EAAS,OAAO,GAC7BA,EAAS,UAAU;AAAA,EAEvB,GAEMC,IAA8B,CAClCC,GACAF,GACAG,GACAC,MACG;AACH,UAAMC,IAAU,YAAY,IAAA,IAAQF,EAAS,SACvCG,IAAQ,KAAK,IAAI,GAAGR,KAAiBO,CAAO;AAClD,IAAAN,EAAWC,CAAQ,GACnBA,EAAS,UAAU,OAAO,WAAW,MAAM;AACzC,MAAAE,EAAO,EAAK,GACZE,IAAA,GACAJ,EAAS,UAAU;AAAA,IACrB,GAAGM,CAAK;AAAA,EACV;AAEA,EAAAC,EAAU,MAAM;AACd,UAAMC,IAAM5B,EAAa,SAAS;AAClC,QAAI,CAAC4B,EAAK;AACV,UAAMC,IAAU;AAChB,QAAIC,IAAUF,EAAI,eAAeC,CAAO;AACxC,IAAKC,MACHA,IAAUF,EAAI,cAAc,OAAO,GACnCE,EAAQ,KAAKD,GACbD,EAAI,MAAM,YAAYE,CAAO,IAE/BA,EAAQ,cAAc;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAoBxB,GAAG,CAAA,CAAE,GAELH,EAAU,MAAM;AACd,IAAInC,MAAeyB,EAAkB,YACnCT,EAAsB,EAAK,GAC3BS,EAAkB,UAAUzB,IAE9B2B,EAAWN,CAAa,GACxBM,EAAWL,CAAc,GACzBC,EAAa,UAAU,IACvBC,EAAc,UAAU;AAExB,UAAMe,IAAY/B,EAAa;AAC/B,QAAI,CAAC+B,EAAW;AAChB,UAAMH,IAAMG,EAAU,eAChBC,IAAOJ,GAAK;AAClB,QAAI,CAACI,EAAM;AACX,UAAMC,IAAiB7D,GAAsB2D,CAAS;AAEtD,IAAAtB,EAAkB,QAAQ,QAAQ,CAAClC,MAASA,EAAK,QAAQ,GACzDkC,EAAkB,UAAU,CAAA,GAC5BC,EAAmB,QAAQ,QAAQ,CAACnC,MAASA,EAAK,QAAQ,GAC1DmC,EAAmB,UAAU,CAAA,GAE7BP,EAAsB,EAAK,GAC3BG,EAAuB,EAAK;AAC5B,UAAM4B,IAAUN,EAAI,cAAc,KAAK;AACvC,IAAAM,EAAQ,YAAY/C,GACpB+C,EAAQ,iBAAiB,SAAS,EAAE,QAAQ,CAAC3D,MAAS;AACpD,MAAK4D,GAAgB5D,CAAI,KAIpB6D,EAA8B7D,EAAK,aAAa,MAAM,CAAC,MAI5DA,EAAK,aAAa,UAAU,QAAQ,GACpCA,EAAK,aAAa,OAAO8D,EAAoB9D,EAAK,aAAa,KAAK,CAAC,CAAC;AAAA,IACxE,CAAC;AAED,UAAM+D,KAAmBnD,EAAK,MAAM,gBAAgB,KAAK,CAAA,GAAI,QACvDoD,MAAoBpD,EAAK,MAAM,cAAc,KAAK,CAAA,GAAI,QACtDqD,IACJF,IAAkB,KAAKA,MAAoBC,IAEvCE,IAA+B,CAAA;AAErC,UAAM,KAAKP,EAAQ,iBAAiB,eAAe,CAAC,EAAE,QAAQ,CAAC3D,MAAS;AACtE,UAAIA,EAAK,QAAQ,YAAA,MAAkB,SAAS;AAC1C,cAAMmE,IAASd,EAAI,cAAc,OAAO;AACxC,QAAAc,EAAO,cAAcnE,EAAK,eAAe,IACzC,MAAM,KAAKA,EAAK,UAAU,EAAE,QAAQ,CAACoE,MAAS;AAC5C,UAAAD,EAAO,aAAaC,EAAK,MAAMA,EAAK,KAAK;AAAA,QAC3C,CAAC,GACDF,EAAc,KAAKC,CAAM;AAAA,MAC3B,OAAO;AACL,cAAME,IAAchB,EAAI,cAAc,QAAQ;AAC9C,cAAM,KAAKrD,EAAK,UAAU,EAAE,QAAQ,CAACoE,MAAS;AAC5C,UAAAC,EAAY,aAAaD,EAAK,MAAMA,EAAK,KAAK;AAAA,QAChD,CAAC,GACDC,EAAY,cAAcrE,EAAK,eAAe,IAC9CkE,EAAc,KAAKG,CAAW;AAAA,MAChC;AACA,MAAArE,EAAK,OAAA;AAAA,IACP,CAAC,GACDQ,GAAoBmD,GAASD,CAAc;AAE3C,UAAMY,IAAYJ,EAAc;AAAA,MAC9B,CAAClE,MAASA,EAAK,QAAQ,kBAAkB;AAAA,IAAA,GAErCuE,IAAaL,EAAc;AAAA,MAC/B,CAAClE,MAASA,EAAK,QAAQ,kBAAkB;AAAA,IAAA;AAE3C,IAAIsE,MACF9B,EAAa,UAAU,IACvBJ,EAAc,UAAU,YAAY,IAAA,GACpCQ,EAAWN,CAAa,GACxBV,EAAsB,EAAI,IAExB2C,MACF9B,EAAc,UAAU,IACxBJ,EAAe,UAAU,YAAY,IAAA,GACrCO,EAAWL,CAAc,GACzBR,EAAuB,EAAI;AAG7B,UAAMyC,IAAe,MAAM,KAAKb,EAAQ,UAAU,GAC5Cc,IAAoBD,EAAa,SAAS;AAChD,IAAAvC,EAAsBwC,CAAiB,GACvCjB,EAAU,gBAAgB,GAAGgB,CAAY;AAEzC,UAAME,IAAkB,CAACC,MAAsB;AAC7C,YAAMC,IAASC,GAAqBF,EAAM,MAAM;AAChD,MAAKC,KAIAf,EAA8Be,EAAO,aAAa,MAAM,CAAC,MAI9DA,EAAO,aAAa,UAAU,QAAQ,GACtCA,EAAO;AAAA,QACL;AAAA,QACAd,EAAoBc,EAAO,aAAa,KAAK,CAAC;AAAA,MAAA;AAAA,IAElD;AACA,IAAApB,EAAU,iBAAiB,SAASkB,GAAiB,EAAI;AAEzD,QAAII,IAAiD;AACrD,UAAMC,IAAmC,MACnCvB,EAAU,WAAW,WAAW,IAC3B,MAGTvB,EAAsB,EAAI,GAC1B6C,GAAuB,WAAA,GACvBA,IAAwB,MACjB;AAET,WAAI,CAACL,KAAqBF,KAAcN,MACtCa,IAAwB,IAAI,iBAAiB,MAAM;AACjD,MAAAC,EAAA;AAAA,IACF,CAAC,GACDD,EAAsB,QAAQtB,GAAW;AAAA,MACvC,WAAW;AAAA,MACX,SAAS;AAAA,IAAA,CACV,IAGHU,EAAc,QAAQ,CAAClE,MAAS;AAC9B,UAAIA,EAAK,QAAQ,YAAA,MAAkB,SAAS;AAC1C,QAAAqD,EAAI,MAAM,YAAYrD,CAAI,GAC1BkC,EAAkB,QAAQ,KAAKlC,CAAwB;AACvD;AAAA,MACF;AAEA,UAAIiE,GAAsB;AACxB,cAAMe,IAAahF,GACbiF,IAAaD,EAAW,eAAe;AAG7C,YAFuB,CAACA,EAAW;AAGjC,cAAI;AAGF,gBAAI,SAASC,CAAU;AAAA,UACzB,QAAQ;AACN,YAAAD,EAAW,OAAA;AACX;AAAA,UACF;AAGF,YAAI;AACF,UAAAvB,EAAK,YAAYuB,CAAU,GAC3B7C,EAAmB,QAAQ,KAAK6C,CAAU;AAAA,QAC5C,QAAQ;AACN,UAAAA,EAAW,OAAA;AAAA,QACb;AAAA,MACF;AAEE,QAAAhF,EAAK,OAAA;AAAA,IAET,CAAC,GACD+E,EAAA,GACI1D,KACUmC,EAAU,eAAe,aAChC,0BAAA,GAGP,sBAAsB,MAAM;AAC1B,MAAIc,KACFxB;AAAA,QACElB;AAAA,QACAU;AAAA,QACAF;AAAA,QACA,MAAM;AACJ,UAAAI,EAAa,UAAU;AAAA,QACzB;AAAA,MAAA,GAGA+B,KACFzB;AAAA,QACEf;AAAA,QACAQ;AAAA,QACAF;AAAA,QACA,MAAM;AACJ,UAAAI,EAAc,UAAU;AAAA,QAC1B;AAAA,MAAA;AAAA,IAGN,CAAC,GAEM,MAAM;AACX,MAAAqC,GAAuB,WAAA,GACvBtB,EAAU,oBAAoB,SAASkB,GAAiB,EAAI;AAAA,IAC9D;AAAA,EACF,GAAG,CAAC9D,GAAMK,GAAYI,CAAa,CAAC,GAEpC+B;AAAA,IACE,MAAM,MAAM;AACV,MAAAR,EAAWN,CAAa,GACxBM,EAAWL,CAAc;AAAA,IAC3B;AAAA,IACA,CAAA;AAAA,EAAC,GAGHa,EAAU,MAAM;AACd,IAAK9B,MAILsB,EAAWN,CAAa,GACxBM,EAAWL,CAAc,GACzBC,EAAa,UAAU,IACvBC,EAAc,UAAU,IACxBb,EAAsB,EAAK,GAC3BG,EAAuB,EAAK;AAAA,EAC9B,GAAG,CAACT,CAAqB,CAAC;AAE1B,QAAM4D,IACA5D,IACK,OAGLQ,MAAuBW,EAAc,UAErCzB,KAAqBF,KAAeS,EAAY,2BAEhDI,MAAsBa,EAAa,UAEnCzB,KAAoBD,KAAeS,EAAY,0BAE9CS,KAEE,OADElB,KAAeS,EAAY,oBAIhC4D,IAAejE,MAAS,cACxBkE,IAA0BD,KAAgB/D,IAC1CiE,KAA2C;AAAA,IAC/C,UAAU;AAAA,IACV,OAAO;AAAA,IACP,QAAQ;AAAA,IACR,SAAS;AAAA,IACT,eAAe;AAAA;AAAA,IAEf,gBAAgBD,IACZ,eACAD,IACE,iBACA;AAAA,EAAA,GAEFG,KAA6C;AAAA,IACjD,eAAeJ,IAAiB,SAAS;AAAA,IACzC,QAAQC,IAAe,SAAY;AAAA,IACnC,OAAO;AAAA,IACP,QAAQC,IAA0B,SAAS;AAAA,IAC3C,WAAWA,IAA0B,IAAI;AAAA,IACzC,MAAMA,IAA0B,aAAa;AAAA,EAAA;AAG/C,SACEG,gBAAAA,EAAAA;AAAAA,IAAC;AAAA,IAAA;AAAA,MACC,gBAAcpE,IAAkB,SAAS;AAAA,MACzC,WAAU;AAAA,MACV,OAAOkE;AAAA,MACP,aAAW,CAAC,CAACH;AAAA,MAEb,UAAA;AAAA,QAAAM,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,KAAK/D;AAAA,YACL,WAAU;AAAA,YACV,OAAO6D;AAAA,UAAA;AAAA,QAAA;AAAA,QAERJ,KACCM,gBAAAA,EAAAA;AAAAA,UAAC;AAAA,UAAA;AAAA,YACC,OAAO;AAAA,cACL,UAAU;AAAA,cACV,OAAO;AAAA,cACP,QAAQ;AAAA,cACR,SAAS;AAAA,cACT,YAAY;AAAA,cACZ,gBAAgB;AAAA,cAChB,eAAe;AAAA,YAAA;AAAA,YAGjB,UAAAA,gBAAAA,EAAAA,IAACC,IAAA,EAAmB,SAASP,EAAA,CAAgB;AAAA,UAAA;AAAA,QAAA;AAAA,MAC/C;AAAA,IAAA;AAAA,EAAA;AAIR;"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
"use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const s=t=>{const e=t?.trim().toLowerCase()??"";return!(!e||e.startsWith("#")||e.startsWith("javascript:")||e.startsWith("data:")||e.startsWith("mailto:")||e.startsWith("tel:"))},o=t=>t?.tagName?.toLowerCase()==="a",a=t=>{const e=t;if(!e)return null;if(typeof e.closest=="function"){const n=e.closest("a[href]");return o(n)?n:null}const r=e.parentElement?.closest("a[href]");return o(r)?r:null},l=t=>{const e=new Set((t??"").split(/\s+/).map(r=>r.trim()).filter(Boolean));return e.add("noopener"),e.add("noreferrer"),Array.from(e).join(" ")};exports.isAnchorElement=o;exports.mergeAnchorRelValue=l;exports.resolveClosestAnchor=a;exports.shouldForceAnchorHrefToNewTab=s;
|
|
2
|
+
//# sourceMappingURL=link-targets.cjs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"link-targets.cjs.js","sources":["../../../../src/components/ContentRender/utils/link-targets.ts"],"sourcesContent":["export const shouldForceAnchorHrefToNewTab = (href: string | null) => {\n const normalizedHref = href?.trim().toLowerCase() ?? \"\";\n\n if (!normalizedHref) {\n return false;\n }\n\n if (\n normalizedHref.startsWith(\"#\") ||\n normalizedHref.startsWith(\"javascript:\") ||\n normalizedHref.startsWith(\"data:\") ||\n normalizedHref.startsWith(\"mailto:\") ||\n normalizedHref.startsWith(\"tel:\")\n ) {\n return false;\n }\n\n return true;\n};\n\nexport const isAnchorElement = (\n node: { tagName?: string | null } | null | undefined\n): node is HTMLAnchorElement => node?.tagName?.toLowerCase() === \"a\";\n\nexport const resolveClosestAnchor = (target: EventTarget | null) => {\n const candidate = target as {\n closest?: (selector: string) => Element | null;\n parentElement?: Element | null;\n } | null;\n\n if (!candidate) {\n return null;\n }\n\n if (typeof candidate.closest === \"function\") {\n const anchor = candidate.closest(\"a[href]\");\n return isAnchorElement(anchor) ? anchor : null;\n }\n\n const anchor = candidate.parentElement?.closest(\"a[href]\");\n return isAnchorElement(anchor) ? anchor : null;\n};\n\nexport const mergeAnchorRelValue = (rel: string | null) => {\n const tokens = new Set(\n (rel ?? \"\")\n .split(/\\s+/)\n .map((token) => token.trim())\n .filter(Boolean)\n );\n\n tokens.add(\"noopener\");\n tokens.add(\"noreferrer\");\n\n return Array.from(tokens).join(\" \");\n};\n"],"names":["shouldForceAnchorHrefToNewTab","href","normalizedHref","isAnchorElement","node","resolveClosestAnchor","target","candidate","anchor","mergeAnchorRelValue","rel","tokens","token"],"mappings":"gFAAO,MAAMA,EAAiCC,GAAwB,CACpE,MAAMC,EAAiBD,GAAM,KAAA,EAAO,eAAiB,GAMrD,MAJI,GAACC,GAKHA,EAAe,WAAW,GAAG,GAC7BA,EAAe,WAAW,aAAa,GACvCA,EAAe,WAAW,OAAO,GACjCA,EAAe,WAAW,SAAS,GACnCA,EAAe,WAAW,MAAM,EAMpC,EAEaC,EACXC,GAC8BA,GAAM,SAAS,gBAAkB,IAEpDC,EAAwBC,GAA+B,CAClE,MAAMC,EAAYD,EAKlB,GAAI,CAACC,EACH,OAAO,KAGT,GAAI,OAAOA,EAAU,SAAY,WAAY,CAC3C,MAAMC,EAASD,EAAU,QAAQ,SAAS,EAC1C,OAAOJ,EAAgBK,CAAM,EAAIA,EAAS,IAC5C,CAEA,MAAMA,EAASD,EAAU,eAAe,QAAQ,SAAS,EACzD,OAAOJ,EAAgBK,CAAM,EAAIA,EAAS,IAC5C,EAEaC,EAAuBC,GAAuB,CACzD,MAAMC,EAAS,IAAI,KAChBD,GAAO,IACL,MAAM,KAAK,EACX,IAAKE,GAAUA,EAAM,MAAM,EAC3B,OAAO,OAAO,CAAA,EAGnB,OAAAD,EAAO,IAAI,UAAU,EACrBA,EAAO,IAAI,YAAY,EAEhB,MAAM,KAAKA,CAAM,EAAE,KAAK,GAAG,CACpC"}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export declare const shouldForceAnchorHrefToNewTab: (href: string | null) => boolean;
|
|
2
|
+
export declare const isAnchorElement: (node: {
|
|
3
|
+
tagName?: string | null;
|
|
4
|
+
} | null | undefined) => node is HTMLAnchorElement;
|
|
5
|
+
export declare const resolveClosestAnchor: (target: EventTarget | null) => HTMLAnchorElement | null;
|
|
6
|
+
export declare const mergeAnchorRelValue: (rel: string | null) => string;
|