fix: HTML code blocks no longer break chat flow
- HTML code blocks now show as syntax-highlighted code with a Preview button - Added sandboxed iframe HTML preview panel - Added 'Open in Browser' button for full-screen preview - Raw HTML from AI responses no longer injected into chat DOM
This commit is contained in:
@@ -330,9 +330,20 @@
|
||||
return code;
|
||||
},
|
||||
breaks: true,
|
||||
gfm: true
|
||||
gfm: true,
|
||||
sanitize: false
|
||||
});
|
||||
return marked.parse(text);
|
||||
var html = marked.parse(text);
|
||||
html = html.replace(/<pre><code class="language-html">([\s\S]*?)<\/code><\/pre>/gi, function(match, codeContent) {
|
||||
var decoded = codeContent.replace(/</g, '<').replace(/>/g, '>').replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, "'");
|
||||
var uid = 'hprev_' + Math.random().toString(36).substr(2, 9);
|
||||
return '<div class="html-preview-block" data-uid="' + uid + '">' +
|
||||
'<div class="html-preview-bar"><span>HTML</span><button class="html-preview-btn" data-uid="' + uid + '">Preview</button></div>' +
|
||||
'<pre><code class="language-html">' + codeContent + '</code></pre>' +
|
||||
'<textarea class="html-preview-src" style="display:none">' + escapeHtml(decoded) + '</textarea>' +
|
||||
'</div>';
|
||||
});
|
||||
return html;
|
||||
}
|
||||
return text.replace(/</g, '<').replace(/>/g, '>').replace(/\n/g, '<br>');
|
||||
}
|
||||
@@ -405,6 +416,43 @@
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
container.querySelectorAll('.html-preview-btn').forEach(function(btn) {
|
||||
btn.addEventListener('click', function() {
|
||||
var uid = this.getAttribute('data-uid');
|
||||
var wrapper = container.querySelector('.html-preview-block[data-uid="' + uid + '"]');
|
||||
if (!wrapper) return;
|
||||
var srcEl = wrapper.querySelector('.html-preview-src');
|
||||
if (!srcEl) return;
|
||||
var src = srcEl.textContent;
|
||||
openHtmlPreview(src);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function openHtmlPreview(htmlSource) {
|
||||
var preview = $('#html-preview');
|
||||
var frame = $('#html-preview-frame');
|
||||
var title = $('#html-preview-title');
|
||||
if (!preview || !frame) return;
|
||||
if (title) title.textContent = 'HTML Preview';
|
||||
preview.style.display = 'flex';
|
||||
var doc = frame.contentDocument || frame.contentWindow.document;
|
||||
doc.open();
|
||||
doc.write(htmlSource);
|
||||
doc.close();
|
||||
}
|
||||
|
||||
function closeHtmlPreview() {
|
||||
var preview = $('#html-preview');
|
||||
if (preview) preview.style.display = 'none';
|
||||
}
|
||||
|
||||
function openHtmlInNewTab(htmlSource) {
|
||||
var blob = new Blob([htmlSource], { type: 'text/html' });
|
||||
var url = URL.createObjectURL(blob);
|
||||
window.open(url, '_blank');
|
||||
setTimeout(function() { URL.revokeObjectURL(url); }, 60000);
|
||||
}
|
||||
|
||||
function highlightFilePaths(html) {
|
||||
@@ -3519,6 +3567,14 @@
|
||||
if (approvalState && approvalState.onApprove) approvalState.onApprove();
|
||||
});
|
||||
$('#approval-deny').addEventListener('click', closeApproval);
|
||||
$('#html-preview-close').addEventListener('click', closeHtmlPreview);
|
||||
$('#html-preview-newtab').addEventListener('click', function() {
|
||||
var frame = $('#html-preview-frame');
|
||||
if (!frame) return;
|
||||
var doc = frame.contentDocument || frame.contentWindow.document;
|
||||
var src = doc.documentElement.outerHTML;
|
||||
openHtmlInNewTab('<!DOCTYPE html>' + src);
|
||||
});
|
||||
|
||||
$('#theme-toggle-header').addEventListener('click', toggleTheme);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user