diff --git a/packages/ui/src/components/layout/ContextPanel.tsx b/packages/ui/src/components/layout/ContextPanel.tsx index 1a395639..de5e3026 100644 --- a/packages/ui/src/components/layout/ContextPanel.tsx +++ b/packages/ui/src/components/layout/ContextPanel.tsx @@ -1306,6 +1306,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire const startUrl = normalized !== 'about:blank' ? normalized : ''; const [urlInput, setUrlInput] = React.useState(startUrl); const [currentUrl, setCurrentUrl] = React.useState(startUrl); + const [loadedUrl, setLoadedUrl] = React.useState(startUrl); const [history, setHistory] = React.useState(() => startUrl ? [startUrl] : []); const [historyIndex, setHistoryIndex] = React.useState(() => startUrl ? 0 : -1); const [reloadNonce, bumpReload] = React.useReducer((value: number) => value + 1, 0); @@ -1324,12 +1325,17 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire setContextPanelTabTargetPath(directory, tabID, url); }, [directory, tabID, setContextPanelTabTargetPath]); - const applyUrl = React.useCallback((url: string, options?: { replaceHistory?: boolean }) => { + const applyUrl = React.useCallback((url: string, options?: { replaceHistory?: boolean; inFrame?: boolean }) => { const normalizedUrl = normalizeBrowserUrl(url); const nextUrl = normalizedUrl !== 'about:blank' ? normalizedUrl : ''; setCurrentUrl(nextUrl); setUrlInput(nextUrl); - setIsLoading(Boolean(nextUrl)); + if (!options?.inFrame) { + setLoadedUrl(nextUrl); + setIsLoading(Boolean(nextUrl)); + } else { + setIsLoading(false); + } persistUrl(nextUrl); setHistory((current) => { @@ -1360,6 +1366,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire if (!nextUrl) return; setHistoryIndex(nextIndex); setCurrentUrl(nextUrl); + setLoadedUrl(nextUrl); setUrlInput(nextUrl); setIsLoading(true); persistUrl(nextUrl); @@ -1376,12 +1383,12 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire }, [currentUrl]); React.useEffect(() => { - if (!currentUrl) { + if (!loadedUrl) { setProxyState({ status: 'idle' }); return; } - const proxyTargetKey = getBrowserProxyTargetKey(currentUrl); + const proxyTargetKey = getBrowserProxyTargetKey(loadedUrl); const cached = getCachedProxyTarget(proxyTargetKey); if (cached?.previewToken) { setProxyState({ status: 'ready', proxyBasePath: cached.proxyBasePath, previewToken: cached.previewToken, expiresAt: cached.expiresAt }); @@ -1401,7 +1408,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire method: 'POST', headers: { 'Content-Type': 'application/json' }, credentials: 'include', - body: JSON.stringify({ url: currentUrl, allowExternal: true }), + body: JSON.stringify({ url: loadedUrl, allowExternal: true }), }); if (!response.ok) { @@ -1441,9 +1448,9 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire return () => { cancelled = true; }; - }, [currentUrl, t]); + }, [loadedUrl, t]); - const proxyUrlAuthKey = currentUrl && proxyState.status === 'ready' + const proxyUrlAuthKey = loadedUrl && proxyState.status === 'ready' ? `${proxyState.proxyBasePath}|${proxyState.previewToken || ''}|${reloadNonce}` : ''; @@ -1468,9 +1475,9 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire const proxySrc = React.useMemo(() => { if (urlAuthReadyKey !== proxyUrlAuthKey) return ''; - if (!currentUrl || proxyState.status !== 'ready') return ''; + if (!loadedUrl || proxyState.status !== 'ready') return ''; try { - const parsed = new URL(currentUrl); + const parsed = new URL(loadedUrl); const path = parsed.pathname || '/'; const searchParams = new URLSearchParams(parsed.search); searchParams.delete('oc_url_token'); @@ -1482,12 +1489,12 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire } catch { return ''; } - }, [currentUrl, proxyState, proxyUrlAuthKey, reloadNonce, urlAuthReadyKey]); + }, [loadedUrl, proxyState, proxyUrlAuthKey, reloadNonce, urlAuthReadyKey]); - const iframeSrc = proxySrc || (proxyState.status === 'error' ? currentUrl : ''); + const iframeSrc = proxySrc || (proxyState.status === 'error' ? loadedUrl : ''); const getCurrentUrlFromFrameUrl = React.useCallback((frameUrl: string): string => { - if (!frameUrl || !currentUrl || proxyState.status !== 'ready') return ''; + if (!frameUrl || !loadedUrl || proxyState.status !== 'ready') return ''; try { const parsedFrameUrl = new URL(frameUrl, window.location.origin); const proxyBasePath = proxyState.proxyBasePath.endsWith('/') @@ -1498,18 +1505,18 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire } const rest = parsedFrameUrl.pathname.slice(proxyBasePath.length) || '/'; - const upstreamOrigin = new URL(currentUrl).origin; + const upstreamOrigin = new URL(loadedUrl).origin; return stripPreviewQueryParams(new URL(`${rest}${parsedFrameUrl.search}${parsedFrameUrl.hash}`, upstreamOrigin).toString()); } catch { return ''; } - }, [currentUrl, proxyState]); + }, [loadedUrl, proxyState]); const getUpstreamUrlFromLocalFrameUrl = React.useCallback((frameUrl: string): string => { - if (!frameUrl || !currentUrl || proxyState.status !== 'ready') return ''; + if (!frameUrl || !loadedUrl || proxyState.status !== 'ready') return ''; try { const parsedFrameUrl = new URL(frameUrl, window.location.origin); - const upstreamOrigin = new URL(currentUrl).origin; + const upstreamOrigin = new URL(loadedUrl).origin; if (parsedFrameUrl.origin !== window.location.origin || upstreamOrigin === window.location.origin) { return ''; } @@ -1525,7 +1532,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire } catch { return ''; } - }, [currentUrl, proxyState]); + }, [loadedUrl, proxyState]); const postInspectMode = React.useCallback((enabled: boolean) => { const frameWindow = iframeRef.current?.contentWindow; @@ -1614,7 +1621,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire const frameUrl = typeof data.url === 'string' ? data.url : ''; const nextUrl = getCurrentUrlFromFrameUrl(frameUrl); if (nextUrl && nextUrl !== currentUrl) { - applyUrl(nextUrl); + applyUrl(nextUrl, { inFrame: true }); } return; } @@ -1685,8 +1692,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire const frameUrl = iframeRef.current?.contentWindow?.location.href || ''; const upstreamUrl = getUpstreamUrlFromLocalFrameUrl(frameUrl); if (upstreamUrl) { - setIsLoading(true); - applyUrl(upstreamUrl); + applyUrl(upstreamUrl, { inFrame: true }); return; } } catch { @@ -1739,7 +1745,7 @@ const IframeBrowserPane: React.FC = ({ initialUrl, dire {iframeSrc ? (