mirror of
https://github.com/nexu-io/open-design.git
synced 2026-06-01 03:14:35 +07:00
fix(web): render srcdoc artifacts directly after leaving URL-load (#3042)
Lazy srcdoc transport was still active after URL-load preview switched off, leaving the visible iframe on an empty activation shell until Edit forced a full srcdoc reload. Mount real artifact HTML whenever srcdoc is the active transport and remount when leaving URL-load. Fixes #2791
This commit is contained in:
parent
3d6e06ad21
commit
4808cdab3c
2 changed files with 44 additions and 10 deletions
|
|
@ -4352,13 +4352,9 @@ function HtmlViewer({
|
|||
[previewSource, effectiveDeck, projectId, file.name, previewStateKey, manualEditMode],
|
||||
);
|
||||
const lazySrcDocTransport = useMemo(() => buildLazySrcdocTransport(), []);
|
||||
const [hasLazySrcDocTransport, setHasLazySrcDocTransport] = useState(useUrlLoadPreview);
|
||||
const [srcDocTransportResetKey, setSrcDocTransportResetKey] = useState(0);
|
||||
const [srcDocShellReady, setSrcDocShellReady] = useState(false);
|
||||
const wasUrlLoadPreviewRef = useRef(useUrlLoadPreview);
|
||||
useEffect(() => {
|
||||
if (useUrlLoadPreview) setHasLazySrcDocTransport(true);
|
||||
}, [useUrlLoadPreview]);
|
||||
// Reset the shell-ready latch whenever the srcDoc iframe re-mounts. The
|
||||
// next shell will post `od:srcdoc-transport-ready` (or fire onLoad) and
|
||||
// flip this back to true. See #2253.
|
||||
|
|
@ -4380,7 +4376,11 @@ function HtmlViewer({
|
|||
window.addEventListener('message', onMessage);
|
||||
return () => window.removeEventListener('message', onMessage);
|
||||
}, []);
|
||||
const useLazySrcDocTransport = !manualEditMode && (useUrlLoadPreview || hasLazySrcDocTransport);
|
||||
// Lazy transport preloads an empty shell only while URL-load is the active
|
||||
// transport. Once srcdoc becomes active (sandbox shim, Draw, Tweaks, etc.),
|
||||
// mount the real artifact HTML directly so we do not depend on a postMessage
|
||||
// activation that can race (#2253) and strand the iframe blank (#2361, #2791).
|
||||
const useLazySrcDocTransport = !manualEditMode && useUrlLoadPreview;
|
||||
const srcDocTransportContent = useLazySrcDocTransport ? lazySrcDocTransport : srcDoc;
|
||||
const urlTransportSrc = useUrlLoadPreview ? activePreviewSrcUrl : 'about:blank';
|
||||
const activateSrcDocTransport = useCallback((target: HTMLIFrameElement | null = srcDocPreviewIframeRef.current) => {
|
||||
|
|
@ -4438,6 +4438,10 @@ function HtmlViewer({
|
|||
wasUrlLoadPreviewRef.current = true;
|
||||
return;
|
||||
}
|
||||
if (wasUrlLoadPreviewRef.current) {
|
||||
setSrcDocTransportResetKey((key) => key + 1);
|
||||
activatedSrcDocTransportHtmlRef.current = null;
|
||||
}
|
||||
wasUrlLoadPreviewRef.current = false;
|
||||
activateSrcDocTransport();
|
||||
}, [activateSrcDocTransport, useUrlLoadPreview]);
|
||||
|
|
|
|||
|
|
@ -454,7 +454,6 @@ describe('FileViewer SVG artifacts', () => {
|
|||
const srcDocFrameAfter = container.querySelector('iframe[data-od-render-mode="srcdoc"]') as HTMLIFrameElement | null;
|
||||
|
||||
expect(urlFrameAfter).toBe(urlFrame);
|
||||
expect(srcDocFrameAfter).toBe(srcDocFrame);
|
||||
expect(urlFrameAfter?.getAttribute('data-od-active')).toBe('false');
|
||||
expect(urlFrameAfter?.getAttribute('src')).toBe('about:blank');
|
||||
expect(srcDocFrameAfter?.getAttribute('data-od-active')).toBe('true');
|
||||
|
|
@ -462,6 +461,38 @@ describe('FileViewer SVG artifacts', () => {
|
|||
expect(srcDocFrameAfter?.srcdoc).toContain('data-od-edit-bridge');
|
||||
});
|
||||
|
||||
it('renders sandbox-shim artifacts on the srcdoc transport without entering edit mode (#2791)', () => {
|
||||
const file = baseFile({
|
||||
name: 'search.html',
|
||||
path: 'search.html',
|
||||
mime: 'text/html',
|
||||
kind: 'html',
|
||||
artifactManifest: {
|
||||
version: 1,
|
||||
kind: 'html',
|
||||
title: 'Search',
|
||||
entry: 'search.html',
|
||||
renderer: 'html',
|
||||
exports: ['html'],
|
||||
},
|
||||
});
|
||||
|
||||
const { container } = render(
|
||||
<FileViewer
|
||||
projectId="project-1"
|
||||
projectKind="prototype"
|
||||
file={file}
|
||||
liveHtml='<html><body><script src="app.js"></script><main data-od-id="results">Results</main></body></html>'
|
||||
/>,
|
||||
);
|
||||
|
||||
const srcDocFrame = container.querySelector('iframe[data-od-render-mode="srcdoc"]') as HTMLIFrameElement | null;
|
||||
expect(srcDocFrame?.getAttribute('data-od-active')).toBe('true');
|
||||
expect(srcDocFrame?.srcdoc).toContain('data-od-id="results"');
|
||||
expect(srcDocFrame?.srcdoc).not.toContain('data-od-lazy-srcdoc-transport');
|
||||
expect(srcDocFrame?.srcdoc).toContain('data-od-sandbox-shim');
|
||||
});
|
||||
|
||||
it('reactivates the srcDoc transport after switching source back to preview', async () => {
|
||||
const file = baseFile({
|
||||
name: 'page.html',
|
||||
|
|
@ -1498,18 +1529,17 @@ describe('FileViewer tweaks toolbar', () => {
|
|||
);
|
||||
|
||||
expect((screen.getByTestId('artifact-preview-frame') as HTMLIFrameElement).getAttribute('data-od-render-mode')).toBe('url-load');
|
||||
const inactiveSrcDocFrame = screen.getByTestId('artifact-preview-frame-srcdoc') as HTMLIFrameElement;
|
||||
const postMessageSpy = vi.spyOn(inactiveSrcDocFrame.contentWindow!, 'postMessage');
|
||||
clickAgentTool('draw-overlay-toggle');
|
||||
|
||||
const frame = await waitFor(() => {
|
||||
const activeFrame = screen.getByTestId('artifact-preview-frame') as HTMLIFrameElement;
|
||||
expect(activeFrame.getAttribute('data-od-render-mode')).toBe('srcdoc');
|
||||
expect(activeFrame.srcdoc).toContain('data-od-lazy-srcdoc-transport');
|
||||
expect(activeFrame.srcdoc).toContain('data-od-selection-bridge');
|
||||
expect(activeFrame.srcdoc).not.toContain('data-od-lazy-srcdoc-transport');
|
||||
return activeFrame;
|
||||
});
|
||||
await waitFor(() => {
|
||||
expect(srcDocActivationMessages(postMessageSpy.mock.calls).at(-1)?.html).toContain('data-od-selection-bridge');
|
||||
expect(frame.srcdoc).toContain('data-od-id="hero"');
|
||||
});
|
||||
expect(screen.queryByRole('button', { name: 'Click' })).toBeNull();
|
||||
expect(screen.getByRole('button', { name: 'Undo' })).toBeTruthy();
|
||||
|
|
|
|||
Loading…
Reference in a new issue