Skip to content

Commit 1dc647f

Browse files
committed
refac
1 parent 6890618 commit 1dc647f

1 file changed

Lines changed: 23 additions & 107 deletions

File tree

src/lib/components/chat/FileNav/PortPreview.svelte

Lines changed: 23 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
66
const i18n = getContext('i18n');
77
8+
89
export let baseUrl: string;
910
export let port: number;
1011
export let path: string = '';
@@ -23,7 +24,6 @@
2324
$: canGoForward = historyIndex < history.length - 1;
2425
2526
const pushHistory = (newPath: string) => {
26-
// Trim forward history when navigating from a non-tip position
2727
if (historyIndex < history.length - 1) {
2828
history = history.slice(0, historyIndex + 1);
2929
}
@@ -35,35 +35,34 @@
3535
if (!canGoBack) return;
3636
historyIndex -= 1;
3737
path = history[historyIndex];
38+
syncUrlBar();
3839
iframeKey += 1;
3940
};
4041
4142
const goForward = () => {
4243
if (!canGoForward) return;
4344
historyIndex += 1;
4445
path = history[historyIndex];
46+
syncUrlBar();
4547
iframeKey += 1;
4648
};
4749
4850
// ── URLs ─────────────────────────────────────────────────────────────
4951
$: proxyUrl = getPortProxyUrl(baseUrl, port, path);
5052
51-
// The proxy path prefix, e.g. "/proxy/8080/"
5253
$: proxyPathPrefix = (() => {
5354
try {
54-
return new URL(getPortProxyUrl(baseUrl, port, '')).pathname;
55+
return new URL(getPortProxyUrl(baseUrl, port, ''), window.location.origin).pathname;
5556
} catch {
5657
return `/proxy/${port}/`;
5758
}
5859
})();
5960
60-
// Display-friendly localhost URL for the address bar
61-
$: displayUrl = `localhost:${port}${path ? '/' + path : ''}`;
62-
$: urlInput = displayUrl;
61+
const makeDisplayUrl = (p: string) => `localhost:${port}${p ? '/' + p : ''}`;
62+
const syncUrlBar = () => { urlInput = makeDisplayUrl(path); };
63+
urlInput = makeDisplayUrl(path);
6364
64-
const refresh = () => {
65-
iframeKey += 1;
66-
};
65+
const refresh = () => { iframeKey += 1; };
6766
6867
const openExternal = () => {
6968
window.open(proxyUrl, '_blank', 'noopener,noreferrer');
@@ -84,113 +83,36 @@
8483
path = newPath;
8584
pushHistory(path);
8685
}
86+
syncUrlBar();
8787
iframeKey += 1;
8888
};
8989
9090
/**
91-
* After each iframe load:
92-
* 1. Sync the address bar with the current iframe path
93-
* 2. Inject a click interceptor to rewrite links BEFORE navigation
94-
* 3. Redirect if the page already escaped the proxy (fallback)
91+
* Read the iframe's current location and sync the URL bar.
92+
* If the iframe escaped the proxy prefix, redirect it back.
9593
*/
9694
const onIframeLoad = () => {
9795
isLoading = false;
9896
if (!iframeEl) return;
9997
try {
100-
const doc = iframeEl.contentDocument;
101-
const win = iframeEl.contentWindow;
102-
if (!doc || !win) return;
103-
104-
const loc = win.location;
105-
const iframePath = loc?.pathname ?? '';
106-
const iframeSearch = loc?.search ?? '';
107-
const iframeHash = loc?.hash ?? '';
98+
const loc = iframeEl.contentWindow?.location;
99+
if (!loc) return;
100+
const iframePath = loc.pathname ?? '';
101+
const iframeSearch = loc.search ?? '';
102+
const iframeHash = loc.hash ?? '';
108103
109104
if (iframePath.startsWith(proxyPathPrefix)) {
110-
// Still inside the proxy — sync address bar
111105
const relativePath = iframePath.slice(proxyPathPrefix.length) + iframeSearch + iframeHash;
112106
if (relativePath !== path) {
113107
path = relativePath;
114108
pushHistory(path);
115-
urlInput = displayUrl;
109+
syncUrlBar();
116110
}
117-
118-
// Inject click interceptor to rewrite links before navigation
119-
injectLinkInterceptor(doc, win);
120-
} else if (iframePath && iframePath !== 'about:blank') {
121-
// Escaped the proxy — redirect back through it
122-
const escapedPath = iframePath.replace(/^\//, '') + iframeSearch + iframeHash;
123-
const correctedUrl = getPortProxyUrl(baseUrl, port, escapedPath);
124-
loc?.replace(correctedUrl);
125111
}
126112
} catch {
127113
// Cross-origin — can't access
128114
}
129115
};
130-
131-
/**
132-
* Inject a click interceptor into the iframe document that rewrites
133-
* anchor hrefs to stay within the proxy path.
134-
*/
135-
const injectLinkInterceptor = (doc: Document, win: Window) => {
136-
// Skip if we already injected
137-
if ((win as any).__portPreviewIntercepted) return;
138-
(win as any).__portPreviewIntercepted = true;
139-
140-
const prefix = proxyPathPrefix;
141-
142-
// Intercept all click events on anchors
143-
doc.addEventListener('click', (e: MouseEvent) => {
144-
const anchor = (e.target as HTMLElement)?.closest?.('a');
145-
if (!anchor || !anchor.href) return;
146-
147-
try {
148-
const url = new URL(anchor.href, win.location.href);
149-
150-
// Only intercept same-origin links that escaped the proxy
151-
if (url.origin === win.location.origin && !url.pathname.startsWith(prefix)) {
152-
e.preventDefault();
153-
e.stopPropagation();
154-
// Rewrite to go through proxy
155-
const rewrittenPath = prefix + url.pathname.replace(/^\//, '') + url.search + url.hash;
156-
win.location.href = url.origin + rewrittenPath;
157-
}
158-
} catch {
159-
// Invalid URL — let browser handle it
160-
}
161-
}, true); // Use capture phase to intercept before SPA routers
162-
163-
// Also intercept form submissions
164-
doc.addEventListener('submit', (e: Event) => {
165-
const form = e.target as HTMLFormElement;
166-
if (!form?.action) return;
167-
168-
try {
169-
const url = new URL(form.action, win.location.href);
170-
if (url.origin === win.location.origin && !url.pathname.startsWith(prefix)) {
171-
form.action = url.origin + prefix + url.pathname.replace(/^\//, '') + url.search;
172-
}
173-
} catch {}
174-
}, true);
175-
176-
// Intercept window.open and programmatic navigation
177-
const origOpen = win.open.bind(win);
178-
win.open = function(url?: string | URL, ...args: any[]) {
179-
if (url && typeof url === 'string') {
180-
try {
181-
const parsed = new URL(url, win.location.href);
182-
if (parsed.origin === win.location.origin && !parsed.pathname.startsWith(prefix)) {
183-
url = parsed.origin + prefix + parsed.pathname.replace(/^\//, '') + parsed.search + parsed.hash;
184-
}
185-
} catch {}
186-
}
187-
return origOpen(url, ...args);
188-
};
189-
};
190-
191-
const onIframeLoadStart = () => {
192-
isLoading = true;
193-
};
194116
</script>
195117

196118
<div class="flex flex-col h-full min-h-0">
@@ -230,22 +152,16 @@
230152
</button>
231153
</Tooltip>
232154

233-
<!-- Refresh / Stop -->
155+
<!-- Refresh -->
234156
<Tooltip content={$i18n.t('Refresh')}>
235157
<button
236158
class="p-1 rounded text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 transition"
237159
on:click={refresh}
238160
aria-label={$i18n.t('Refresh')}
239161
>
240-
{#if isLoading}
241-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3.5 animate-spin">
242-
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.451a.75.75 0 0 0 0-1.5H4.5a.75.75 0 0 0-.75.75v3.75a.75.75 0 0 0 1.5 0v-2.127l.13.13a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm-10.624-2.85a5.5 5.5 0 0 1 9.201-2.465l.312.31H11.75a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 .75-.75V3.42a.75.75 0 0 0-1.5 0v2.126l-.13-.129A7 7 0 0 0 3.239 8.555a.75.75 0 0 0 1.449.39Z" clip-rule="evenodd" />
243-
</svg>
244-
{:else}
245-
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3.5">
246-
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.451a.75.75 0 0 0 0-1.5H4.5a.75.75 0 0 0-.75.75v3.75a.75.75 0 0 0 1.5 0v-2.127l.13.13a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm-10.624-2.85a5.5 5.5 0 0 1 9.201-2.465l.312.31H11.75a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 .75-.75V3.42a.75.75 0 0 0-1.5 0v2.126l-.13-.129A7 7 0 0 0 3.239 8.555a.75.75 0 0 0 1.449.39Z" clip-rule="evenodd" />
247-
</svg>
248-
{/if}
162+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="currentColor" class="size-3.5" class:animate-spin={isLoading}>
163+
<path fill-rule="evenodd" d="M15.312 11.424a5.5 5.5 0 0 1-9.201 2.466l-.312-.311h2.451a.75.75 0 0 0 0-1.5H4.5a.75.75 0 0 0-.75.75v3.75a.75.75 0 0 0 1.5 0v-2.127l.13.13a7 7 0 0 0 11.712-3.138.75.75 0 0 0-1.449-.39Zm-10.624-2.85a5.5 5.5 0 0 1 9.201-2.465l.312.31H11.75a.75.75 0 0 0 0 1.5h3.75a.75.75 0 0 0 .75-.75V3.42a.75.75 0 0 0-1.5 0v2.126l-.13-.129A7 7 0 0 0 3.239 8.555a.75.75 0 0 0 1.449.39Z" clip-rule="evenodd" />
164+
</svg>
249165
</button>
250166
</Tooltip>
251167

@@ -275,7 +191,7 @@
275191
</button>
276192
</Tooltip>
277193

278-
<!-- Close preview -->
194+
<!-- Close -->
279195
<Tooltip content={$i18n.t('Close')}>
280196
<button
281197
class="p-1 rounded text-gray-500 dark:text-gray-400 hover:bg-gray-200 dark:hover:bg-gray-800 hover:text-gray-700 dark:hover:text-gray-300 transition"
@@ -296,7 +212,7 @@
296212
</div>
297213
{/if}
298214

299-
<!-- Iframe content -->
215+
<!-- Iframe -->
300216
{#key iframeKey}
301217
<iframe
302218
bind:this={iframeEl}

0 commit comments

Comments
 (0)