Skip to content

Commit 1009609

Browse files
committed
Fixed proxying not working on the intial tab
1 parent 3ba6d63 commit 1009609

File tree

3 files changed

+198
-99
lines changed

3 files changed

+198
-99
lines changed

src/components/Browser.tsx

Lines changed: 114 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,33 +2,74 @@ import { ChevronLeft, ChevronRight, Home, Lock, Menu, MoreVertical, Plus, Rotate
22
import { useMemo, useState, useRef, useEffect } from "react";
33
import {
44
type Tab,
5-
baseTabs,
65
formatUrl,
76
classNames,
87
iconButtonClass,
98
tabButtonClass,
109
closeButtonClass,
1110
addressInputClass,
1211
actionBarClass,
13-
goBack,
14-
goForward,
15-
reloadTab,
16-
toggleFullscreen,
17-
addBookmark,
12+
getDefaultUrl,
13+
encodeProxyUrl,
14+
getActualUrl,
1815
} from "@/lib/browser";
1916

2017
export default function Browser() {
21-
const [tabs, setTabs] = useState<Tab[]>(baseTabs);
22-
const [url, setUrl] = useState(baseTabs[0].url);
18+
const [tabs, setTabs] = useState<Tab[]>([
19+
{ id: 1, title: "New Tab", url: "about:blank", active: true, reloadKey: 0 },
20+
]);
21+
const [url, setUrl] = useState("about:blank");
2322
const activeTab = useMemo(() => tabs.find((tab) => tab.active), [tabs]);
2423
const iframeRefs = useRef<{ [key: number]: HTMLIFrameElement | null }>({});
2524

25+
useEffect(() => {
26+
const defaultUrl = getDefaultUrl();
27+
setTabs(prev => prev.map(tab => ({ ...tab, url: defaultUrl })));
28+
setUrl(defaultUrl);
29+
}, []);
30+
2631
useEffect(() => {
2732
if (activeTab) {
2833
setUrl(activeTab.url);
2934
}
3035
}, [activeTab]);
3136

37+
useEffect(() => {
38+
if (!activeTab) return;
39+
40+
const iframe = iframeRefs.current[activeTab.id];
41+
if (!iframe) return;
42+
43+
const updateUrlBar = () => {
44+
const actualUrl = getActualUrl(iframe);
45+
if (actualUrl && actualUrl !== url) {
46+
setUrl(actualUrl);
47+
48+
try {
49+
const hostname = new URL(actualUrl).hostname;
50+
setTabs((prev) =>
51+
prev.map((tab) =>
52+
tab.id === activeTab.id
53+
? { ...tab, title: hostname || "New Tab" }
54+
: tab
55+
)
56+
);
57+
} catch (e) {
58+
// Invalid URL
59+
}
60+
}
61+
};
62+
63+
iframe.addEventListener("load", updateUrlBar);
64+
65+
const interval = setInterval(updateUrlBar, 1000);
66+
67+
return () => {
68+
iframe.removeEventListener("load", updateUrlBar);
69+
clearInterval(interval);
70+
};
71+
}, [activeTab, url]);
72+
3273
const setActiveTab = (id: number) => {
3374
const target = tabs.find((tab) => tab.id === id);
3475
if (!target) return;
@@ -45,9 +86,9 @@ export default function Browser() {
4586
setTabs((prev) => {
4687
const nextId = prev.length ? Math.max(...prev.map((tab) => tab.id)) + 1 : 1;
4788
const newTabs = prev.map((tab) => ({ ...tab, active: false }));
48-
return [...newTabs, { id: nextId, title: "New Tab", url: "about:blank", active: true, reloadKey: 0 }];
89+
return [...newTabs, { id: nextId, title: "New Tab", url: getDefaultUrl(), active: true, reloadKey: 0 }];
4990
});
50-
setUrl("about:blank");
91+
setUrl(getDefaultUrl());
5192
};
5293

5394
const closeTab = (id: number) => {
@@ -56,7 +97,8 @@ export default function Browser() {
5697
const filtered = prev.filter((tab) => tab.id !== id);
5798

5899
if (filtered.length === 0) {
59-
return [{ id: Date.now(), title: "New Tab", url: "about:blank", active: true, reloadKey: 0 }];
100+
const defaultUrl = getDefaultUrl();
101+
return [{ id: Date.now(), title: "New Tab", url: defaultUrl, active: true, reloadKey: 0 }];
60102
} else if (!filtered.some((tab) => tab.active)) {
61103
filtered[0] = { ...filtered[0], active: true };
62104
nextUrl = filtered[0].url;
@@ -74,25 +116,76 @@ export default function Browser() {
74116

75117
const handleNavigate = (value: string) => {
76118
if (!activeTab) return;
77-
const nextUrl = formatUrl(value);
119+
const formattedUrl = formatUrl(value);
120+
78121
setTabs((prev) =>
79122
prev.map((tab) =>
80123
tab.id === activeTab.id
81124
? {
82125
...tab,
83-
url: nextUrl,
84-
title: new URL(nextUrl).hostname || "New Tab",
126+
url: formattedUrl,
127+
title: new URL(formattedUrl).hostname || "New Tab",
128+
reloadKey: tab.reloadKey + 1,
85129
}
86130
: tab,
87131
),
88132
);
89-
setUrl(nextUrl);
133+
setUrl(formattedUrl);
90134
};
91135

92136
const goHome = () => {
93137
handleNavigate("https://duckduckgo.com");
94138
};
95139

140+
const goBack = () => {
141+
if (!activeTab) return;
142+
const iframe = iframeRefs.current[activeTab.id];
143+
iframe?.contentWindow?.history.back();
144+
};
145+
146+
const goForward = () => {
147+
if (!activeTab) return;
148+
const iframe = iframeRefs.current[activeTab.id];
149+
iframe?.contentWindow?.history.forward();
150+
};
151+
152+
const reloadTab = () => {
153+
if (!activeTab) return;
154+
const iframe = iframeRefs.current[activeTab.id];
155+
iframe?.contentWindow?.location.reload();
156+
};
157+
158+
const toggleFullscreen = () => {
159+
if (!activeTab) return;
160+
const iframe = iframeRefs.current[activeTab.id];
161+
if (iframe) {
162+
iframe.requestFullscreen().catch((err) => {
163+
console.error("Failed to enter fullscreen mode:", err);
164+
});
165+
}
166+
};
167+
168+
const addBookmark = () => {
169+
if (!activeTab) return;
170+
const iframe = iframeRefs.current[activeTab.id];
171+
const actualUrl = getActualUrl(iframe) || activeTab.url;
172+
173+
const title = prompt("Enter a Title for this bookmark:", activeTab.title || "New Bookmark");
174+
175+
if (title && typeof localStorage !== 'undefined') {
176+
try {
177+
const bookmarks = JSON.parse(localStorage.getItem("bookmarks") || "[]");
178+
bookmarks.push({ Title: title, url: actualUrl });
179+
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
180+
console.log("Bookmark added:", { Title: title, url: actualUrl });
181+
alert("Bookmark added successfully!");
182+
} catch (e) {
183+
console.error("Failed to add bookmark:", e);
184+
alert("Failed to add bookmark. Storage may be blocked.");
185+
}
186+
}
187+
};
188+
96189
return (
97190
<div className="flex h-screen flex-col bg-background">
98191
<div className="flex items-center gap-1 bg-background px-3 pt-1.5 pb-0">
@@ -139,13 +232,13 @@ export default function Browser() {
139232

140233
<div className="flex items-center justify-between gap-3 border-b border-border/50 bg-background-secondary px-3 py-2 backdrop-blur-xl">
141234
<div className="flex items-center gap-1">
142-
<button type="button" className={iconButtonClass} onClick={() => activeTab && goBack(iframeRefs.current, activeTab.id)} aria-label="Back">
235+
<button type="button" className={iconButtonClass} onClick={goBack} aria-label="Back">
143236
<ChevronLeft className="h-4 w-4" />
144237
</button>
145-
<button type="button" className={iconButtonClass} onClick={() => activeTab && goForward(iframeRefs.current, activeTab.id)} aria-label="Forward">
238+
<button type="button" className={iconButtonClass} onClick={goForward} aria-label="Forward">
146239
<ChevronRight className="h-4 w-4" />
147240
</button>
148-
<button type="button" className={iconButtonClass} onClick={() => activeTab && reloadTab(iframeRefs.current, activeTab.id)} aria-label="Reload">
241+
<button type="button" className={iconButtonClass} onClick={reloadTab} aria-label="Reload">
149242
<RotateCw className="h-4 w-4" />
150243
</button>
151244
<button type="button" className={iconButtonClass} onClick={goHome} aria-label="Home">
@@ -171,10 +264,10 @@ export default function Browser() {
171264
</div>
172265

173266
<div className="flex items-center gap-1">
174-
<button type="button" className={iconButtonClass} onClick={() => activeTab && toggleFullscreen(iframeRefs.current, activeTab.id)} aria-label="Fullscreen">
267+
<button type="button" className={iconButtonClass} onClick={toggleFullscreen} aria-label="Fullscreen">
175268
<Maximize2 className="h-4 w-4" />
176269
</button>
177-
<button type="button" className={iconButtonClass} onClick={() => activeTab && addBookmark(iframeRefs.current, activeTab.id, activeTab.title, activeTab.url)} aria-label="Bookmark">
270+
<button type="button" className={iconButtonClass} onClick={addBookmark} aria-label="Bookmark">
178271
<Star className="h-4 w-4" />
179272
</button>
180273
<button type="button" className={iconButtonClass} aria-label="Menu">
@@ -194,7 +287,7 @@ export default function Browser() {
194287
iframeRefs.current[tab.id] = el;
195288
}}
196289
title={tab.title}
197-
src={tab.url}
290+
src={encodeProxyUrl(tab.url)}
198291
className={classNames("absolute inset-0 h-full w-full border-0", tab.active ? "block" : "hidden")}
199292
sandbox="allow-scripts allow-same-origin allow-forms allow-popups allow-popups-to-escape-sandbox"
200293
/>

src/lib/browser.ts

Lines changed: 81 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,25 @@ export interface Tab {
66
reloadKey: number;
77
}
88

9+
export interface UVConfig {
10+
prefix: string;
11+
encodeUrl: (url: string) => string;
12+
decodeUrl: (url: string) => string;
13+
}
14+
15+
declare global {
16+
interface Window {
17+
__uv$config: UVConfig;
18+
}
19+
}
20+
921
export const baseTabs: Tab[] = [
1022
{ id: 1, title: "New Tab", url: "about:blank", active: true, reloadKey: 0 },
1123
];
1224

1325
export const formatUrl = (value: string) => {
1426
if (!value.trim()) return "about:blank";
15-
const hasProtocol = /^https?:\/\//i.test(value);
16-
return hasProtocol ? value : `https://${value}`;
27+
return /^https?:\/\//i.test(value) ? value : `https://${value}`;
1728
};
1829

1930
export const classNames = (...classes: Array<string | false | undefined>) =>
@@ -25,34 +36,74 @@ export const closeButtonClass = "inline-flex h-5 w-5 items-center justify-center
2536
export const addressInputClass = "h-auto flex-1 border-0 bg-transparent p-0 text-sm text-text focus:outline-none";
2637
export const actionBarClass = "flex items-center gap-2 rounded-lg border border-border bg-background-secondary/50 px-3 py-1.5 transition-all focus-within:bg-background focus-within:ring-2 focus-within:ring-accent/20";
2738

28-
export const goBack = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
29-
const iframe = iframeRefs[tabId];
30-
if (iframe?.contentWindow) {
31-
iframe.contentWindow.history.back();
39+
export const getDefaultUrl = () => {
40+
if (typeof window === "undefined") {
41+
return "https://duckduckgo.com";
42+
}
43+
44+
try {
45+
return sessionStorage.getItem("goUrl") ||
46+
localStorage.getItem("engine") ||
47+
"https://duckduckgo.com";
48+
} catch (error) {
49+
console.warn("Storage access failed:", error);
50+
return "https://duckduckgo.com";
3251
}
3352
};
3453

35-
export const goForward = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
36-
const iframe = iframeRefs[tabId];
37-
if (iframe?.contentWindow) {
38-
iframe.contentWindow.history.forward();
54+
export const encodeProxyUrl = (url: string): string => {
55+
if (!url || url === "about:blank") return "about:blank";
56+
if (typeof window === "undefined") return url;
57+
58+
const config = window.__uv$config;
59+
return config ? config.prefix + config.encodeUrl(url) : url;
60+
};
61+
62+
export const decodeProxyUrl = (proxyUrl: string): string => {
63+
if (!proxyUrl || proxyUrl === "about:blank") return proxyUrl;
64+
if (typeof window === "undefined") return proxyUrl;
65+
66+
const config = window.__uv$config;
67+
68+
if (config) {
69+
try {
70+
const path = proxyUrl.split("/").pop() || "";
71+
return config.decodeUrl(path);
72+
} catch {
73+
return proxyUrl;
74+
}
3975
}
76+
77+
return proxyUrl;
4078
};
4179

42-
export const reloadTab = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
43-
const iframe = iframeRefs[tabId];
44-
if (iframe?.contentWindow) {
45-
iframe.contentWindow.location.reload();
80+
export const getActualUrl = (iframe: HTMLIFrameElement | null): string => {
81+
if (!iframe?.contentWindow) return "";
82+
83+
try {
84+
return (iframe.contentWindow as any).__uv$location?.href ||
85+
iframe.contentWindow.location.href;
86+
} catch {
87+
return "";
4688
}
4789
};
4890

91+
export const goBack = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
92+
iframeRefs[tabId]?.contentWindow?.history.back();
93+
};
94+
95+
export const goForward = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
96+
iframeRefs[tabId]?.contentWindow?.history.forward();
97+
};
98+
99+
export const reloadTab = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
100+
iframeRefs[tabId]?.contentWindow?.location.reload();
101+
};
102+
49103
export const toggleFullscreen = (iframeRefs: { [key: number]: HTMLIFrameElement | null }, tabId: number) => {
50-
const iframe = iframeRefs[tabId];
51-
if (iframe) {
52-
iframe.requestFullscreen().catch((err) => {
53-
console.error("Failed to enter fullscreen mode:", err);
54-
});
55-
}
104+
iframeRefs[tabId]?.requestFullscreen().catch(err =>
105+
console.error("Fullscreen failed:", err)
106+
);
56107
};
57108

58109
export const addBookmark = (
@@ -61,15 +112,20 @@ export const addBookmark = (
61112
tabTitle: string,
62113
tabUrl: string
63114
) => {
64-
const iframe = iframeRefs[tabId];
65-
const currentUrl = iframe?.contentWindow?.location.href || tabUrl;
115+
if (typeof window === "undefined") return;
66116

117+
const actualUrl = getActualUrl(iframeRefs[tabId]) || tabUrl;
67118
const title = prompt("Enter a Title for this bookmark:", tabTitle || "New Bookmark");
68119

69-
if (title) {
120+
if (!title) return;
121+
122+
try {
70123
const bookmarks = JSON.parse(localStorage.getItem("bookmarks") || "[]");
71-
bookmarks.push({ Title: title, url: currentUrl });
124+
bookmarks.push({ Title: title, url: actualUrl });
72125
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
73-
console.log("Bookmark added:", { Title: title, url: currentUrl });
126+
alert("Bookmark added successfully!");
127+
} catch (err) {
128+
console.error("Failed to add bookmark:", err);
129+
alert("Failed to add bookmark. Storage may be blocked.");
74130
}
75131
};

0 commit comments

Comments
 (0)