Skip to content

Commit d8bcc1c

Browse files
committed
Added bookmarks
1 parent c2b7e7e commit d8bcc1c

2 files changed

Lines changed: 88 additions & 22 deletions

File tree

src/components/Browser.tsx

Lines changed: 87 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ export default function Browser() {
66
const [tabs, setTabs] = useState<Tab[]>([{ id: 1, title: "Tab 1", url: "about:blank", active: true, reloadKey: 0 }]);
77
const [url, setUrl] = useState("about:blank");
88
const [favicons, setFavicons] = useState<{ [key: number]: string }>({});
9+
const [bookmarks, setBookmarks] = useState<Array<{ Title: string; url: string; favicon?: string }>>([]);
910
const activeTab = useMemo(() => tabs.find((tab) => tab.active), [tabs]);
1011
const iframeRefs = useRef<{ [key: number]: HTMLIFrameElement | null }>({});
1112

@@ -22,6 +23,13 @@ export default function Browser() {
2223

2324
setTabs((prev) => prev.map((tab) => ({ ...tab, url: firstTabUrl })));
2425
setUrl(firstTabUrl);
26+
27+
try {
28+
const savedBookmarks = JSON.parse(localStorage.getItem("bookmarks") || "[]");
29+
setBookmarks(savedBookmarks);
30+
} catch (error) {
31+
console.warn("Failed to load bookmarks:", error);
32+
}
2533
}, []);
2634

2735
useEffect(() => {
@@ -52,24 +60,19 @@ export default function Browser() {
5260

5361
const iframeDoc = iframe.contentWindow?.document;
5462
if (iframeDoc) {
55-
const faviconLink =
56-
iframeDoc.querySelector<HTMLLinkElement>('link[rel="icon"]') ||
57-
iframeDoc.querySelector<HTMLLinkElement>('link[rel="shortcut icon"]') ||
58-
iframeDoc.querySelector<HTMLLinkElement>('link[rel="apple-touch-icon"]');
59-
63+
const faviconLink = iframeDoc.querySelector<HTMLLinkElement>('link[rel="icon"]') || iframeDoc.querySelector<HTMLLinkElement>('link[rel="shortcut icon"]') || iframeDoc.querySelector<HTMLLinkElement>('link[rel="apple-touch-icon"]');
64+
6065
if (faviconLink?.href) {
6166
setFavicons((prev) => ({ ...prev, [activeTab.id]: faviconLink.href }));
6267
} else if (actualUrl) {
6368
try {
6469
const urlObj = new URL(actualUrl);
6570
const defaultFavicon = `${urlObj.origin}/favicon.ico`;
6671
setFavicons((prev) => ({ ...prev, [activeTab.id]: defaultFavicon }));
67-
} catch (e) {
68-
}
72+
} catch (e) {}
6973
}
7074
}
71-
} catch (e) {
72-
}
75+
} catch (e) {}
7376
};
7477

7578
iframe.addEventListener("load", updateUrlBar);
@@ -91,7 +94,7 @@ export default function Browser() {
9194
active: tab.id === id,
9295
})),
9396
);
94-
97+
9598
const iframe = iframeRefs.current[id];
9699
const actualUrl = getActualUrl(iframe);
97100
setUrl(actualUrl || target.url);
@@ -156,7 +159,17 @@ export default function Browser() {
156159
};
157160

158161
const goHome = () => {
159-
window.location.href = '/';
162+
window.location.href = "/";
163+
};
164+
165+
const removeBookmark = (index: number) => {
166+
try {
167+
const updatedBookmarks = bookmarks.filter((_, i) => i !== index);
168+
setBookmarks(updatedBookmarks);
169+
localStorage.setItem("bookmarks", JSON.stringify(updatedBookmarks));
170+
} catch (e) {
171+
console.error("Failed to remove bookmark:", e);
172+
}
160173
};
161174

162175
const goBack = () => {
@@ -196,10 +209,22 @@ export default function Browser() {
196209

197210
if (title && typeof localStorage !== "undefined") {
198211
try {
199-
const bookmarks = JSON.parse(localStorage.getItem("bookmarks") || "[]");
200-
bookmarks.push({ Title: title, url: actualUrl });
201-
localStorage.setItem("bookmarks", JSON.stringify(bookmarks));
202-
console.log("Bookmark added:", { Title: title, url: actualUrl });
212+
let faviconUrl = favicons[activeTab.id] || "";
213+
214+
if (!faviconUrl) {
215+
try {
216+
const urlObj = new URL(actualUrl);
217+
faviconUrl = `${urlObj.origin}/favicon.ico`;
218+
} catch (e) {
219+
faviconUrl = "";
220+
}
221+
}
222+
223+
const newBookmark = { Title: title, url: actualUrl, favicon: faviconUrl };
224+
const updatedBookmarks = [...bookmarks, newBookmark];
225+
setBookmarks(updatedBookmarks);
226+
localStorage.setItem("bookmarks", JSON.stringify(updatedBookmarks));
227+
console.log("Bookmark added:", newBookmark);
203228
alert("Bookmark added successfully!");
204229
} catch (e) {
205230
console.error("Failed to add bookmark:", e);
@@ -227,10 +252,15 @@ export default function Browser() {
227252
>
228253
<div className="flex min-w-0 flex-1 items-center gap-2">
229254
{favicons[tab.id] ? (
230-
<img src={favicons[tab.id]} alt="" className="h-4 w-4 shrink-0 rounded" onError={(e) => {
231-
e.currentTarget.style.display = 'none';
232-
e.currentTarget.nextElementSibling?.classList.remove('hidden');
233-
}} />
255+
<img
256+
src={favicons[tab.id]}
257+
alt=""
258+
className="h-4 w-4 shrink-0 rounded"
259+
onError={(e) => {
260+
e.currentTarget.style.display = "none";
261+
e.currentTarget.nextElementSibling?.classList.remove("hidden");
262+
}}
263+
/>
234264
) : null}
235265
<div className={classNames("h-4 w-4 shrink-0 rounded bg-accent/30", favicons[tab.id] ? "hidden" : "")} />
236266
<span className="truncate text-sm">{tab.title}</span>
@@ -253,7 +283,7 @@ export default function Browser() {
253283
</button>
254284
</div>
255285

256-
<div className="flex items-center justify-between gap-3 border-b border-border/50 bg-background-secondary px-3 py-2 backdrop-blur-xl">
286+
<div className="flex items-center justify-between gap-3 bg-background-secondary px-3 py-2 backdrop-blur-xl">
257287
<div className="flex items-center gap-1">
258288
<button type="button" className={iconButtonClass} onClick={goHome} aria-label="Home">
259289
<Home className="h-4 w-4" />
@@ -302,6 +332,42 @@ export default function Browser() {
302332
</div>
303333
</div>
304334

335+
{bookmarks.length > 0 && (
336+
<div className="flex items-center gap-0.5 bg-background-secondary px-3 py-1.5 overflow-x-auto border-b border-border/50">
337+
{bookmarks.map((bookmark, index) => (
338+
<button
339+
key={index}
340+
type="button"
341+
className="inline-flex items-center gap-2 rounded-lg px-3 py-1.5 text-sm text-text-secondary hover:bg-interactive hover:scale-105 transition-all shrink-0"
342+
style={{ maxWidth: "195px" }}
343+
onClick={() => handleNavigate(bookmark.url)}
344+
onContextMenu={(e) => {
345+
e.preventDefault();
346+
if (confirm(`Remove bookmark "${bookmark.Title}"?`)) {
347+
removeBookmark(index);
348+
}
349+
}}
350+
>
351+
{bookmark.favicon ? (
352+
<img
353+
src={bookmark.favicon}
354+
alt=""
355+
className="h-[18px] w-[18px] shrink-0"
356+
onError={(e) => {
357+
e.currentTarget.style.display = "none";
358+
e.currentTarget.nextElementSibling?.classList.remove("hidden");
359+
}}
360+
/>
361+
) : null}
362+
<Star className={`h-[18px] w-[18px] fill-current shrink-0 ${bookmark.favicon ? "hidden" : ""}`} />
363+
<span className="overflow-hidden whitespace-nowrap block min-w-0" style={{ textOverflow: "ellipsis" }}>
364+
{bookmark.Title}
365+
</span>
366+
</button>
367+
))}
368+
</div>
369+
)}
370+
305371
<div className="relative flex-1 bg-background">
306372
{tabs.map((tab) => (
307373
<iframe
@@ -318,4 +384,4 @@ export default function Browser() {
318384
</div>
319385
</div>
320386
);
321-
}
387+
}

src/lib/tabs.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -121,4 +121,4 @@ export const addBookmark = (iframeRefs: { [key: number]: HTMLIFrameElement | nul
121121
console.error("Failed to add bookmark:", err);
122122
alert("Failed to add bookmark. Storage may be blocked.");
123123
}
124-
};
124+
};

0 commit comments

Comments
 (0)