fix(builder): Preview includes site headCode and is \$-safe (D8)
Two bugs in TopBar's Preview handler: 1. exportToHtml() was called without a headCode option, so Preview silently ignored SiteDesign.headCode (custom <head> code the user set via the Code modal). Pull `design.headCode` from useSiteDesign() and pass it through, matching exportToHtml's ExportOptions.headCode signature. 2. html.replace(bodyMatch[1], composedBody) used the string-replacer overload of String.prototype.replace, which treats `$&`, `$1`, `$$`, etc. in the replacement string as special patterns -- any user content containing a literal `$` sequence would corrupt the preview HTML. Switch to a function replacer (`.replace(bodyMatch[1], () => composedBody)`) so the composed body is inserted literally. Verified by reading + `npm run build`; no existing TopBar test harness to extend (dynamic import + window.open/Blob in the click handler). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -3,6 +3,7 @@ import { useEditor } from '@craftjs/core';
|
||||
import { useEditorConfig } from '../../state/EditorConfigContext';
|
||||
import { useWhpApi } from '../../hooks/useWhpApi';
|
||||
import { usePages } from '../../state/PageContext';
|
||||
import { useSiteDesign } from '../../state/SiteDesignContext';
|
||||
import { DeviceMode } from '../../types';
|
||||
import { TemplateModal } from './TemplateModal';
|
||||
import { HeadCodeModal } from './HeadCodeModal';
|
||||
@@ -22,6 +23,7 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange }) => {
|
||||
}));
|
||||
const { save, publish, load } = useWhpApi();
|
||||
const { headerPage, footerPage } = usePages();
|
||||
const { design } = useSiteDesign();
|
||||
|
||||
const [saveStatus, setSaveStatus] = useState<'idle' | 'saving' | 'saved' | 'error'>('idle');
|
||||
const [publishStatus, setPublishStatus] = useState<'idle' | 'publishing' | 'published' | 'error'>('idle');
|
||||
@@ -189,13 +191,17 @@ export const TopBar: React.FC<TopBarProps> = ({ device, onDeviceChange }) => {
|
||||
const result = exportToHtml(serialized, {
|
||||
title: whpConfig?.siteName || 'Preview',
|
||||
includeFonts: true,
|
||||
headCode: design.headCode,
|
||||
});
|
||||
|
||||
// Replace the body in the full document with our composed version
|
||||
// Replace the body in the full document with our composed version.
|
||||
// Use a function replacer -- a plain string replacer treats
|
||||
// `$&`/`$1`/`$$` sequences in user content as replacement
|
||||
// patterns, silently corrupting the output.
|
||||
let html = result.html;
|
||||
const bodyMatch = html.match(/<body[^>]*>([\s\S]*)<\/body>/i);
|
||||
if (bodyMatch) {
|
||||
html = html.replace(bodyMatch[1], composedBody);
|
||||
html = html.replace(bodyMatch[1], () => composedBody);
|
||||
}
|
||||
|
||||
// Make proxy URLs absolute so they work from the blob: context
|
||||
|
||||
Reference in New Issue
Block a user