fix: resolve ES module bare specifiers in widget iframe#51
fix: resolve ES module bare specifiers in widget iframe#51GeneralJerel merged 3 commits intomainfrom
Conversation
The async useState+useEffect refactor caused matchedTemplate to change after first render, inserting a badge div before the iframe and shifting its child index. React reconciles by position, so this remounted the iframe and destroyed rendered 3D/canvas content. Restores the original synchronous ref with eslint-disable comments. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add import map to iframe shell mapping common libraries (three, gsap, d3, chart.js) to esm.sh CDN URLs so bare specifiers like `import "three"` resolve correctly. Auto-detect ES module syntax in dynamically injected scripts and promote them to type="module" when import/export statements are present. Update skill instructions to prefer the ES module pattern for Three.js.
References issue #3 — allow-same-origin is required for import maps in srcdoc iframes but weakens the sandbox. Document the tradeoff.
GeneralJerel left a comment
There was a problem hiding this comment.
Review Notes
sandbox allow-same-origin (addressed)
allow-scripts + allow-same-origin effectively negates the iframe sandbox — the iframe can access window.parent, DOM, cookies, etc. For this demo (no auth, no client-side secrets) the practical risk is low. Added a code comment referencing issue #3 and posted investigation context there (blob URLs, sandbox subdomain as alternatives).
Auto-detect regex may false-positive
The module auto-detection regex at widget-renderer.tsx:433:
/\b(import\s|export\s|import\()/.test(scriptInfo.text)matches inside comments and strings (e.g. // We don't import anything), which would promote a non-module script to type="module" — changing execution semantics (strict mode, deferred execution, this is undefined). Consider anchoring to start-of-line or only promoting when a mapped bare specifier is present.
useMemo with ref (minor)
sourceRef.current is read inside useMemo in save-template-overlay.tsx but isn't a dependency. Works for the initial-render capture case, but if pending arrives after mount the memo won't recompute. If late arrivals aren't possible, a comment noting that assumption would help.
Looks good otherwise
- Import map placement before CSP meta is correct
esm.shalready in CSPscript-srcandconnect-src- SKILL.md update guiding LLM toward
type="module"reduces reliance on auto-detect
Summary
three,gsap,d3, andchart.jstoesm.shCDN URLs so bare specifiers likeimport "three"resolve correctly in the sandboxed srcdoc iframe<script>tags containingimport/exportsyntax are automatically promoted totype="module"so the import map applies even when the LLM omits the type attributeimport * as THREE from 'three') over the legacy r128 UMD script tagTest plan
pnpm devand ask the agent to create a Three.js 3D visualizationFailed to resolve module specifier "three"console errorsimport * as THREE from 'three') and UMD (<script src="cdnjs...">) patterns work🤖 Generated with Claude Code