Skip to content

Commit bd8a83b

Browse files
authored
Merge pull request #46 from CopilotKit/fix/svg-rendering-idiomorph
fix: prevent broken script execution in widget iframe
2 parents 4a776bd + 67d5753 commit bd8a83b

File tree

1 file changed

+25
-15
lines changed

1 file changed

+25
-15
lines changed

apps/app/src/components/generative-ui/widget-renderer.tsx

Lines changed: 25 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -361,9 +361,13 @@ window.addEventListener('message', function(e) {
361361
if (!content) return;
362362
363363
// Strip script tags from HTML before inserting — scripts are handled separately below
364+
var rawHtml = e.data.html;
364365
var tmp = document.createElement('div');
365-
tmp.innerHTML = e.data.html;
366+
tmp.innerHTML = rawHtml;
366367
var incomingScripts = [];
368+
var scriptOpens = (rawHtml.match(/<script[\\s>]/gi) || []).length;
369+
var scriptCloses = (rawHtml.match(/<\\/script>/gi) || []).length;
370+
var allScriptsClosed = scriptOpens <= scriptCloses;
367371
tmp.querySelectorAll('script').forEach(function(s) {
368372
incomingScripts.push({ src: s.src, text: s.textContent });
369373
s.remove();
@@ -388,7 +392,7 @@ window.addEventListener('message', function(e) {
388392
// Use idiomorph to diff/patch DOM (preserves existing nodes, no flicker)
389393
if (window.Idiomorph) {
390394
try {
391-
Idiomorph.morph(content, tmp.innerHTML, {
395+
Idiomorph.morph(content, tmp, {
392396
morphStyle: 'innerHTML',
393397
callbacks: {
394398
beforeNodeAdded: function(node) {
@@ -410,19 +414,25 @@ window.addEventListener('message', function(e) {
410414
content.innerHTML = tmp.innerHTML;
411415
}
412416
413-
// Execute only new scripts (not previously executed)
414-
incomingScripts.forEach(function(scriptInfo) {
415-
var key = scriptInfo.src || scriptInfo.text;
416-
if (content.getAttribute('data-exec-' + btoa(key).slice(0, 16))) return;
417-
content.setAttribute('data-exec-' + btoa(key).slice(0, 16), '1');
418-
var newScript = document.createElement('script');
419-
if (scriptInfo.src) {
420-
newScript.src = scriptInfo.src;
421-
} else {
422-
newScript.textContent = scriptInfo.text;
423-
}
424-
content.appendChild(newScript);
425-
});
417+
// Execute only new scripts — skip entirely while a <script> tag is still streaming
418+
if (allScriptsClosed) {
419+
incomingScripts.forEach(function(scriptInfo) {
420+
var key = scriptInfo.src || scriptInfo.text;
421+
if (!key || !key.trim()) return;
422+
var hash = btoa(unescape(encodeURIComponent(key))).slice(0, 16).replace(/[^a-zA-Z0-9]/g, '');
423+
if (!hash || content.getAttribute('data-exec-' + hash)) return;
424+
content.setAttribute('data-exec-' + hash, '1');
425+
try {
426+
var newScript = document.createElement('script');
427+
if (scriptInfo.src) {
428+
newScript.src = scriptInfo.src;
429+
} else {
430+
newScript.textContent = scriptInfo.text;
431+
}
432+
content.appendChild(newScript);
433+
} catch(e) { console.warn('[widget] script exec failed:', e); }
434+
});
435+
}
426436
reportHeight();
427437
}
428438
});

0 commit comments

Comments
 (0)