@@ -2,33 +2,74 @@ import { ChevronLeft, ChevronRight, Home, Lock, Menu, MoreVertical, Plus, Rotate
22import { useMemo , useState , useRef , useEffect } from "react" ;
33import {
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
2017export 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 />
0 commit comments