@@ -21,6 +21,9 @@ export default function BrowserPage() {
2121 const [ privacy , setPrivacy ] = useState < any > ( null )
2222 const [ uaDrop , setUaDrop ] = useState ( false )
2323 const [ blocked , setBlocked ] = useState ( 0 )
24+ const [ favBar , setFavBar ] = useState < any [ ] > ( [ ] )
25+ const [ showFavBar , setShowFavBar ] = useState ( ( ) => localStorage . getItem ( 'showFavBar' ) !== 'false' )
26+ const [ favDropOpen , setFavDropOpen ] = useState ( false )
2427 const [ currentUA , setCurrentUA ] = useState ( '' )
2528 const [ payment , setPayment ] = useState < any > ( null )
2629 const [ pageNostr , setPageNostr ] = useState ( false )
@@ -36,10 +39,14 @@ export default function BrowserPage() {
3639 // Load privacy settings
3740 useEffect ( ( ) => {
3841 window . zap ?. getPrivacy ( ) . then ( setPrivacy )
42+ const loadFavBar = ( ) => window . zap ?. getFavorites ( ) . then ( ( f : any [ ] ) => setFavBar ( f || [ ] ) )
43+ loadFavBar ( )
44+ window . addEventListener ( 'favorites-updated' , loadFavBar )
3945 window . zap ?. getUAPool ( ) . then ( ( pool : string [ ] ) => {
4046 if ( pool && pool . length > 0 ) setCurrentUA ( pool [ Math . floor ( Math . random ( ) * pool . length ) ] )
4147 } )
4248 window . zap ?. getBlockedCount ( ) . then ( setBlocked )
49+ return ( ) => window . removeEventListener ( 'favorites-updated' , loadFavBar )
4350 } , [ ] )
4451
4552 // Listen to events from main process
@@ -57,6 +64,22 @@ export default function BrowserPage() {
5764 handleNewTab ( url )
5865 } )
5966 window . zap ?. on ( 'payment-detected' , ( data : any ) => setPayment ( data ) )
67+
68+ // Navigate from history/bookmarks
69+ const onNavigateTo = ( e : any ) => {
70+ handleNavigate ( ( e as CustomEvent ) . detail )
71+ setPanel ( null )
72+ }
73+ window . addEventListener ( 'navigate-to' , onNavigateTo )
74+
75+ // Toggle bookmarks bar
76+ const onToggleFavBar = ( e : any ) => setShowFavBar ( ( e as CustomEvent ) . detail )
77+ window . addEventListener ( 'toggle-favbar' , onToggleFavBar )
78+
79+ return ( ) => {
80+ window . removeEventListener ( 'navigate-to' , onNavigateTo )
81+ window . removeEventListener ( 'toggle-favbar' , onToggleFavBar )
82+ }
6083 } , [ activeId ] )
6184
6285 // Sync address bar with active tab
@@ -92,6 +115,7 @@ export default function BrowserPage() {
92115 const url = ( ! tab ?. url || tab . url === 'zap://newtab' ) ? '' : tab . url
93116 setActive ( id )
94117 setAddrVal ( url )
118+ setShowSuggest ( false )
95119 window . zap ?. tabSwitch ( { tabId : id , url } )
96120 } , [ setActive , tabs ] )
97121
@@ -311,6 +335,75 @@ export default function BrowserPage() {
311335 </ div >
312336 </ div >
313337
338+ { /* ── Bookmarks bar ────────────────────────────────────────── */ }
339+ { showFavBar && favBar . length > 0 && ( ( ) => {
340+ const MAX = 10
341+ const visible = favBar . slice ( 0 , MAX )
342+ const hidden = favBar . slice ( MAX )
343+ return (
344+ < div style = { {
345+ display :'flex' , alignItems :'center' , gap :2 ,
346+ padding :'2px 8px' , borderBottom :'1px solid var(--b0)' ,
347+ background :'var(--bg-1)' , flexShrink :0 ,
348+ WebkitAppRegion :'no-drag' as any , height :28 , position :'relative' ,
349+ } } >
350+ { visible . map ( ( f : any ) => (
351+ < button key = { f . id } onClick = { ( ) => handleNavigate ( f . url ) }
352+ title = { f . url }
353+ style = { {
354+ background :'none' , border :'none' , cursor :'pointer' ,
355+ color :'var(--t0)' , fontSize :11 , padding :'2px 7px' ,
356+ borderRadius :'var(--r-sm)' , whiteSpace :'nowrap' ,
357+ display :'flex' , alignItems :'center' , gap :3 , flexShrink :0 ,
358+ } }
359+ onMouseEnter = { e => ( e . currentTarget . style . background = 'var(--bg-3)' ) }
360+ onMouseLeave = { e => ( e . currentTarget . style . background = 'none' ) }
361+ >
362+ 🌐 { f . title ?. slice ( 0 , 18 ) || ( ( ) => { try { return new URL ( f . url ) . hostname } catch ( _ ) { return f . url } } ) ( ) }
363+ </ button >
364+ ) ) }
365+ { hidden . length > 0 && (
366+ < div style = { { position :'relative' , marginLeft :'auto' , flexShrink :0 } } >
367+ < button
368+ onClick = { ( ) => setFavDropOpen ( d => ! d ) }
369+ style = { {
370+ background :'none' , border :'none' , cursor :'pointer' ,
371+ color :'var(--t2)' , fontSize :12 , padding :'2px 7px' ,
372+ borderRadius :'var(--r-sm)' ,
373+ } }
374+ onMouseEnter = { e => ( e . currentTarget . style . background = 'var(--bg-3)' ) }
375+ onMouseLeave = { e => ( e . currentTarget . style . background = 'none' ) }
376+ > » { hidden . length } </ button >
377+ { favDropOpen && (
378+ < div style = { {
379+ position :'absolute' , top :24 , right :0 , zIndex :9999 ,
380+ background :'var(--bg-1)' , border :'1px solid var(--b1)' ,
381+ borderRadius :'var(--r-md)' , boxShadow :'0 8px 32px rgba(0,0,0,.4)' ,
382+ minWidth :220 , maxHeight :300 , overflowY :'auto' ,
383+ } } >
384+ { hidden . map ( ( f : any ) => (
385+ < button key = { f . id }
386+ onClick = { ( ) => { handleNavigate ( f . url ) ; setFavDropOpen ( false ) } }
387+ style = { {
388+ display :'block' , width :'100%' , textAlign :'left' ,
389+ background :'none' , border :'none' , cursor :'pointer' ,
390+ color :'var(--t0)' , fontSize :12 , padding :'8px 12px' ,
391+ whiteSpace :'nowrap' , overflow :'hidden' , textOverflow :'ellipsis' ,
392+ } }
393+ onMouseEnter = { e => ( e . currentTarget . style . background = 'var(--bg-3)' ) }
394+ onMouseLeave = { e => ( e . currentTarget . style . background = 'none' ) }
395+ >
396+ 🌐 { f . title || f . url }
397+ </ button >
398+ ) ) }
399+ </ div >
400+ ) }
401+ </ div >
402+ ) }
403+ </ div >
404+ )
405+ } ) ( ) }
406+
314407 { /* ── Browser body ─────────────────────────────────────────────── */ }
315408 < div className = "app-body" >
316409 { /* New tab page (shown when tab is zap://newtab) */ }
@@ -322,7 +415,9 @@ export default function BrowserPage() {
322415 { /* When not new tab, BrowserView renders here (injected by Electron) */ }
323416 { ! isNew && < div style = { { flex :1 } } /> }
324417
325- { /* Suggestions dropdown */ }
418+
419+
420+ { /* Suggestions dropdown */ }
326421 { showSuggest && (
327422 < div style = { {
328423 position :'fixed' , top :114 , left :0 , right :panel ?320 :0 ,
0 commit comments