Skip to content

Commit 99eae64

Browse files
refactor: simplify task Browser tab — drop localhost rewrite, add open-in-new-tab
The Browser tab's iframe used to rewrite localhost URLs to the host's full Tailscale FQDN to handle the "remote UI / dev server only on the host" case, but the user navigates with explicit Tailscale IPs and hostnames, so the rewrite isn't pulling its weight. Delete `preview-url.ts` and its tests, and stop pulling `useTailscaleHostname` into the Browser tab — the iframe `src` is just the user-typed URL now. Also drop the iframe `sandbox` attribute (with `allow-scripts`+`allow-same-origin` it provided no real security benefit anyway, but nudged some browsers toward stricter cookie partitioning) and add an "Open in new tab" toolbar button. Modern browsers block cross-site iframe cookies by default, which broke logins for dev apps like `http://citadel:5173`; the new button is a one-click escape hatch into a real top-level tab where auth works. The `tailscaleHostname` setting, `useTailscaleHostname` hook, and `detectTailscaleHostname()` stay — they're still used by the public-domain/tunnel feature. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 50d20a0 commit 99eae64

3 files changed

Lines changed: 17 additions & 107 deletions

File tree

frontend/components/viewer/browser-preview.tsx

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -5,29 +5,21 @@ import { HugeiconsIcon } from '@hugeicons/react'
55
import {
66
ArrowLeft01Icon,
77
ArrowRight01Icon,
8+
LinkSquare02Icon,
89
RefreshIcon,
910
} from '@hugeicons/core-free-icons'
1011
import { useBrowserUrl } from '@/hooks/use-browser-url'
11-
import { useTailscaleHostname } from '@/hooks/use-config'
12-
import { rewriteLocalhostForPreview } from '@/lib/preview-url'
1312

1413
interface BrowserPreviewProps {
1514
taskId: string
1615
}
1716

1817
export function BrowserPreview({ taskId }: BrowserPreviewProps) {
1918
const { url, setUrl } = useBrowserUrl(taskId)
20-
const { data: tailscaleHostname } = useTailscaleHostname()
2119
const [inputValue, setInputValue] = useState(url)
2220
const [key, setKey] = useState(0)
2321
const iframeRef = useRef<HTMLIFrameElement>(null)
2422

25-
const iframeSrc = rewriteLocalhostForPreview(
26-
url,
27-
tailscaleHostname,
28-
typeof window !== 'undefined' ? window.location.host : '',
29-
)
30-
3123
// Sync input value when URL changes (e.g., on initial load)
3224
useEffect(() => {
3325
setInputValue(url)
@@ -49,6 +41,11 @@ export function BrowserPreview({ taskId }: BrowserPreviewProps) {
4941
setKey((k) => k + 1)
5042
}, [])
5143

44+
const handleOpenInNewTab = useCallback(() => {
45+
if (!url) return
46+
window.open(url, '_blank', 'noopener,noreferrer')
47+
}, [url])
48+
5249
const handleNavigate = useCallback(
5350
(e: React.FormEvent) => {
5451
e.preventDefault()
@@ -84,17 +81,26 @@ export function BrowserPreview({ taskId }: BrowserPreviewProps) {
8481
placeholder="Enter URL..."
8582
/>
8683
</form>
84+
85+
<Button
86+
variant="ghost"
87+
size="icon-xs"
88+
onClick={handleOpenInNewTab}
89+
disabled={!url}
90+
title="Open in new tab"
91+
>
92+
<HugeiconsIcon icon={LinkSquare02Icon} size={14} strokeWidth={2} />
93+
</Button>
8794
</div>
8895

8996
{/* Browser content */}
9097
<div className="flex-1 overflow-hidden bg-card">
9198
<iframe
9299
ref={iframeRef}
93100
key={key}
94-
src={iframeSrc}
101+
src={url}
95102
className="h-full w-full border-0"
96103
title="Browser Preview"
97-
sandbox="allow-scripts allow-same-origin allow-forms allow-popups"
98104
onLoad={handleIframeLoad}
99105
/>
100106
</div>

frontend/lib/preview-url.test.ts

Lines changed: 0 additions & 55 deletions
This file was deleted.

frontend/lib/preview-url.ts

Lines changed: 0 additions & 41 deletions
This file was deleted.

0 commit comments

Comments
 (0)