diff --git a/js/core/app-shell.js b/js/core/app-shell.js
index 17add05..c7fcc08 100644
--- a/js/core/app-shell.js
+++ b/js/core/app-shell.js
@@ -2083,7 +2083,7 @@
renderHtmlPreview(htmlContent, manifest, options = {}) {
if (!this.el.viewer) return;
const { partial = false } = options;
- const preparedHtml = this.prepareHtmlDocument(htmlContent);
+ const preparedHtml = this.prepareHtmlDocument(htmlContent, { partial });
this.el.viewer.innerHTML = '';
const wrapper = document.createElement('div');
@@ -2115,16 +2115,66 @@
this.el.viewer.appendChild(wrapper);
}
- prepareHtmlDocument(htmlContent) {
+ prepareHtmlDocument(htmlContent, options = {}) {
+ const { partial = false } = options;
const raw = typeof htmlContent === 'string' ? htmlContent.trim() : '';
if (!raw) {
return '
空白页面';
}
const hasDocumentTag = /]/i.test(raw);
if (hasDocumentTag) {
- return raw;
+ return this.ensureHtmlClosingTags(raw, { partial });
}
- return `Generated Page${raw}`;
+ const bodyContent = raw;
+ const skeleton = `Generated Page${bodyContent}`;
+ return this.ensureHtmlClosingTags(skeleton, { partial: false });
+ }
+
+ ensureHtmlClosingTags(html, options = {}) {
+ const { partial = false } = options;
+ let output = html;
+
+ const ensureClosingTag = (tag, insertionStrategy) => {
+ const openRegex = new RegExp(`<${tag}[\\s>]`, 'i');
+ const closeRegex = new RegExp(`${tag}>`, 'i');
+ if (!openRegex.test(output) || closeRegex.test(output)) {
+ return;
+ }
+ const closingMarkup = `${tag}>`;
+ const insertAt = insertionStrategy();
+ if (insertAt < 0 || insertAt > output.length) {
+ output += `\n${closingMarkup}`;
+ } else {
+ output =
+ output.slice(0, insertAt) +
+ `\n${closingMarkup}\n` +
+ output.slice(insertAt);
+ }
+ };
+
+ ensureClosingTag('head', () => {
+ const bodyIndex = output.search(/]/i);
+ if (bodyIndex !== -1) return bodyIndex;
+ const htmlCloseIndex = output.search(/<\/html>/i);
+ if (htmlCloseIndex !== -1) return htmlCloseIndex;
+ return output.length;
+ });
+
+ ensureClosingTag('body', () => {
+ const htmlCloseIndex = output.search(/<\/html>/i);
+ return htmlCloseIndex !== -1 ? htmlCloseIndex : output.length;
+ });
+
+ ensureClosingTag('html', () => output.length);
+
+ if (partial) {
+ // 对流式场景追加最外层闭合,避免 iframe 在标签未闭合时渲染失败
+ if (!/