mirror of
https://github.com/bytedance/deer-flow.git
synced 2026-08-06 04:48:44 +00:00
fix(frontend): stop matching <header> as <head> in HTML preview base injection (#4625)
appendHtmlPreviewBaseHref detected the head tag with /<head[^>]*>/i, which also matches <header ...>. For a fragment with no <head> that opens with <header> - a common shape in agent-generated report pages - the <base> element was injected after the <header> opening tag instead of being prepended, so relative assets appearing before that point (e.g. a leading <img>) resolved without the base and failed to load in the sandboxed iframe. Use the word-boundary-safe /<head(?:\s[^>]*)?>/i that the sibling appendHtmlPreviewScrollRestoration already uses, keeping the two injectors consistent.
This commit is contained in:
parent
5bf3e3960d
commit
2f6cadb8b0
@ -175,8 +175,14 @@ export function appendHtmlPreviewBaseHref(
|
|||||||
|
|
||||||
const baseHref = htmlBaseHref(url, currentHref);
|
const baseHref = htmlBaseHref(url, currentHref);
|
||||||
const baseElement = `<base href="${escapeHtmlAttribute(baseHref)}">`;
|
const baseElement = `<base href="${escapeHtmlAttribute(baseHref)}">`;
|
||||||
if (/<head[^>]*>/i.exec(content)) {
|
// "(?:\s[^>]*)?" keeps the tag-name boundary so `<header>` (a common
|
||||||
return content.replace(/<head([^>]*)>/i, `<head$1>${baseElement}`);
|
// leading tag in agent-generated fragments) is not mistaken for `<head>`;
|
||||||
|
// mirrors appendHtmlPreviewScrollRestoration below.
|
||||||
|
if (/<head(?:\s[^>]*)?>/i.test(content)) {
|
||||||
|
return content.replace(
|
||||||
|
/<head(?:\s[^>]*)?>/i,
|
||||||
|
(headTag) => `${headTag}${baseElement}`,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
return `${baseElement}${content}`;
|
return `${baseElement}${content}`;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -282,6 +282,39 @@ test("preserves existing head elements when injecting scroll restoration", () =>
|
|||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("does not mistake <header> for <head> when injecting the base href", () => {
|
||||||
|
// Agent-generated fragments often have no <head> but open with <header>;
|
||||||
|
// the base must then be prepended so assets before the tag resolve too.
|
||||||
|
const html =
|
||||||
|
'<img src="logo.png"><header class="top">Report</header><main>content</main>';
|
||||||
|
|
||||||
|
const result = appendHtmlPreviewBaseHref(
|
||||||
|
html,
|
||||||
|
"/demo/threads/thread-1/user-data/outputs/report.html",
|
||||||
|
"http://localhost/workspace/chats/thread-1",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toBe(
|
||||||
|
'<base href="http://localhost/demo/threads/thread-1/user-data/outputs/">' +
|
||||||
|
html,
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
test("injects the base href inside an attributed <head> tag", () => {
|
||||||
|
const html =
|
||||||
|
'<!doctype html><html><head lang="en"><meta charset="utf-8"></head><body>x</body></html>';
|
||||||
|
|
||||||
|
const result = appendHtmlPreviewBaseHref(
|
||||||
|
html,
|
||||||
|
"/demo/threads/thread-1/user-data/outputs/report.html",
|
||||||
|
"http://localhost/workspace/chats/thread-1",
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(result).toContain(
|
||||||
|
'<head lang="en"><base href="http://localhost/demo/threads/thread-1/user-data/outputs/">',
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
test("does not duplicate HTML scroll restoration script", () => {
|
test("does not duplicate HTML scroll restoration script", () => {
|
||||||
const html = appendHtmlPreviewScrollRestoration(
|
const html = appendHtmlPreviewScrollRestoration(
|
||||||
"<html><body>x</body></html>",
|
"<html><body>x</body></html>",
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user