77 < title > VisionBrowser</ title >
88 < script src ="https://cdn.tailwindcss.com "> </ script >
99 < link href ="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap " rel ="stylesheet ">
10- < style >
10+ < style id =" vision-theme-styles " >
1111 : root {
1212 --bg-main : # 000000 ;
1313 --bg-toolbar : # 0a0a0b ;
1414 --bg-tab-active : # 000000 ;
1515 --bg-tab-inactive : # 050505 ;
1616 --text-main : # ffffff ;
1717 --text-dim : # a1a1aa ;
18- --accent-purple : # 8b5cf6 ;
18+ --accent-color : # 8b5cf6 ;
1919 --capsule-bg : rgba (255 , 255 , 255 , 0.03 );
20+ --tab-radius : 8px ;
2021 }
2122
2223 body {
4142 font-weight : 600 ;
4243 cursor : pointer;
4344 transition : all 0.2s ease;
44- border-top-left-radius : 8 px ;
45- border-top-right-radius : 8 px ;
45+ border-top-left-radius : var ( --tab-radius ) ;
46+ border-top-right-radius : var ( --tab-radius ) ;
4647 z-index : 1 ;
4748 border : 1px solid rgba (255 , 255 , 255 , 0.05 );
4849 border-bottom : none;
5556 background : var (--bg-tab-active );
5657 color : var (--text-main );
5758 z-index : 10 ;
58- border-top : 2px solid var (--accent-purple );
59+ border-top : 2px solid var (--accent-color );
5960 border-left : 1px solid rgba (255 , 255 , 255 , 0.1 );
6061 border-right : 1px solid rgba (255 , 255 , 255 , 0.1 );
6162 box-shadow : 0 -4px 20px rgba (0 , 0 , 0 , 0.5 );
7879 }
7980
8081 # address-bar-wrapper : focus-within {
81- border-color : var (--accent-purple );
82+ border-color : var (--accent-color );
8283 background : rgba (0 , 0 , 0 , 0.8 );
8384 box-shadow : 0 0 0 2px rgba (139 , 92 , 246 , 0.2 );
8485 }
110111 .viewport { background : # 000 ; }
111112 iframe { width : 100% ; height : 100% ; border : none; background : # 000 ; }
112113 .no-scrollbar ::-webkit-scrollbar { display : none; }
114+
115+ /* Dynamic Theme Colors */
116+ .text-accent { color : var (--accent-color ); }
117+ .bg-accent { background-color : var (--accent-color ); }
118+ .border-accent { border-color : var (--accent-color ); }
113119 </ style >
114120</ head >
115121< body class ="h-screen flex flex-col ">
130136
131137 < div class ="flex-1 flex items-center h-full ">
132138 < div id ="address-bar-wrapper " class ="w-full flex items-center h-10 px-4 group ">
133- < div id ="address-icon " class ="flex items-center text-gray-500 group-focus-within:text-purple-400 mr-3 ">
139+ < div id ="address-icon " class ="flex items-center text-gray-500 mr-3 ">
134140 < svg xmlns ="http://www.w3.org/2000/svg " width ="14 " height ="14 " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="2.5 " stroke-linecap ="round " stroke-linejoin ="round "> < circle cx ="11 " cy ="11 " r ="8 "> </ circle > < line x1 ="21 " y1 ="21 " x2 ="16.65 " y2 ="16.65 "> </ line > </ svg >
135141 </ div >
136142 < input id ="address-bar " type ="text " autocomplete ="off " placeholder ="Search or enter address "
164170 <!-- Side Panel -->
165171 < div id ="side-panel ">
166172 < div class ="flex items-center justify-between p-5 border-b border-white/10 shrink-0 bg-[#0a0a0b] ">
167- < h2 id ="side-panel-title " class ="font-black text-[10px] tracking-widest uppercase text-purple-400 "> Vision Audio</ h2 >
173+ < h2 id ="side-panel-title " class ="font-black text-[10px] tracking-widest uppercase text-accent "> Vision Audio</ h2 >
168174 < button id ="close-side-panel " class ="p-2 hover:bg-white/10 rounded-full transition-all text-gray-500 hover:text-white ">
169175 < svg xmlns ="http://www.w3.org/2000/svg " width ="18 " height ="18 " viewBox ="0 0 24 24 " fill ="none " stroke ="currentColor " stroke-width ="2.5 " stroke-linecap ="round " stroke-linejoin ="round "> < line x1 ="18 " y1 ="6 " x2 ="6 " y2 ="18 "> </ line > < line x1 ="6 " y1 ="6 " x2 ="18 " y2 ="18 "> </ line > </ svg >
170176 </ button >
@@ -181,7 +187,7 @@ <h2 id="side-panel-title" class="font-black text-[10px] tracking-widest uppercas
181187 < div class ="h-8 bg-black text-[9px] font-bold uppercase tracking-[0.2em] text-zinc-600 px-5 flex items-center justify-between border-t border-white/5 z-30 ">
182188 < div class ="flex items-center gap-6 ">
183189 < div class ="flex items-center gap-2 ">
184- < div class ="w-1.5 h-1.5 rounded-full bg-purple-500 animate-pulse "> </ div >
190+ < div class ="w-1.5 h-1.5 rounded-full bg-accent animate-pulse "> </ div >
185191 < span class ="text-zinc-400 "> Vision System Active</ span >
186192 </ div >
187193 < div id ="status-url " class ="truncate max-w-sm lowercase opacity-50 italic "> vision://home</ div >
@@ -207,19 +213,57 @@ <h2 id="side-panel-title" class="font-black text-[10px] tracking-widest uppercas
207213 const closeSidePanel = document . getElementById ( 'close-side-panel' ) ;
208214 const zoomDisplay = document . getElementById ( 'zoom-display' ) ;
209215
216+ function applySystemSettings ( ) {
217+ const theme = localStorage . getItem ( 'vision_theme' ) || 'Deep Void Purple (Default)' ;
218+ const shape = localStorage . getItem ( 'vision_tab_shape' ) || 'Modern Rounded (8px)' ;
219+
220+ let accent = '#8b5cf6' ;
221+ let bgAlt = '#0a0a0b' ;
222+
223+ if ( theme === 'Midnight Gray' ) {
224+ accent = '#71717a' ;
225+ bgAlt = '#121214' ;
226+ } else if ( theme === 'Obsidian Blue' ) {
227+ accent = '#3b82f6' ;
228+ bgAlt = '#0a0c12' ;
229+ }
230+
231+ let radius = '8px' ;
232+ if ( shape === 'Classic Block' ) radius = '4px' ;
233+ else if ( shape === 'Sharp Edge' ) radius = '0px' ;
234+
235+ document . documentElement . style . setProperty ( '--accent-color' , accent ) ;
236+ document . documentElement . style . setProperty ( '--bg-toolbar' , bgAlt ) ;
237+ document . documentElement . style . setProperty ( '--tab-radius' , radius ) ;
238+ }
239+
210240 function getFavicon ( url ) {
211- if ( ! url || url === 'newtab.html' ) return null ;
241+ if ( ! url || url === 'newtab.html' || url . startsWith ( 'vision://' ) ) return null ;
212242 try {
213- // If it's a proxy URL, extract the real domain for the icon
214243 let targetUrl = url ;
215244 if ( url . includes ( 'url=' ) ) {
216- targetUrl = decodeURIComponent ( url . split ( 'url=' ) [ 1 ] ) ;
245+ targetUrl = url . split ( 'url=' ) [ 1 ] . split ( '&' ) [ 0 ] ;
246+ targetUrl = decodeURIComponent ( targetUrl ) ;
217247 }
218248 const domain = new URL ( targetUrl . startsWith ( 'http' ) ? targetUrl : 'https://' + targetUrl ) . hostname ;
219249 return `https://www.google.com/s2/favicons?domain=${ domain } &sz=64` ;
220250 } catch ( e ) { return null ; }
221251 }
222252
253+ function saveHistory ( url , title ) {
254+ if ( ! url || url === 'newtab.html' || url . startsWith ( 'vision://' ) ) return ;
255+ const history = JSON . parse ( localStorage . getItem ( 'vision_history' ) || '[]' ) ;
256+ const entry = {
257+ id : Date . now ( ) . toString ( ) ,
258+ url : url ,
259+ title : title || url ,
260+ time : new Date ( ) . toISOString ( ) ,
261+ favicon : getFavicon ( url )
262+ } ;
263+ history . unshift ( entry ) ;
264+ localStorage . setItem ( 'vision_history' , JSON . stringify ( history . slice ( 0 , 200 ) ) ) ;
265+ }
266+
223267 function saveSession ( ) {
224268 localStorage . setItem ( 'vision_session' , JSON . stringify ( {
225269 tabs : tabs . map ( t => ( { id : t . id , title : t . title , zoom : t . zoom , favicon : t . favicon , url : t . url , proxyUrl : t . proxyUrl } ) ) ,
@@ -314,28 +358,23 @@ <h2 id="side-panel-title" class="font-black text-[10px] tracking-widest uppercas
314358 if ( ! input ) return ;
315359 input = input . trim ( ) ;
316360
361+ const searchEngine = localStorage . getItem ( 'vision_search_engine' ) || 'https://www.google.com/search?q=' ;
362+
317363 let proxyUrl = '' ;
318364 const internalPages = [ 'history.html' , 'bookmarks.html' , 'newtab.html' , 'devtools.html' , 'settings.html' , 'music.html' ] ;
319365
320366 if ( internalPages . includes ( input ) ) {
321367 proxyUrl = input ;
322368 } else {
323- // Routing logic as requested:
324- // 1. Is it a URL or search query?
325- const isUrl = / ^ h t t p s ? : \/ \/ / . test ( input ) || ( ! input . includes ( ' ' ) && input . includes ( '.' ) ) ;
326-
369+ const isUrl = / ^ h t t p s ? : \/ \/ / . test ( input ) || ( ! input . includes ( ' ' ) && input . includes ( '.' ) && input . length > 3 ) ;
327370 let targetUrl = '' ;
328371 if ( isUrl ) {
329- // Normalize to https://domain
330- let domain = input . replace ( / ^ h t t p s ? : \/ \/ / , '' ) ;
331- targetUrl = 'https://' + domain ;
372+ targetUrl = input . startsWith ( 'http' ) ? input : 'https://' + input ;
332373 } else {
333- // Search query: route to Google search via the custom embed method
334- targetUrl = 'https://www.google.com/search?q=' + encodeURIComponent ( input ) ;
374+ targetUrl = searchEngine + encodeURIComponent ( input ) ;
335375 }
336-
337- // Construct the GAWD damn custom method URL
338- proxyUrl = '/active/embed.html?url=' + targetUrl ;
376+ proxyUrl = 'active/embed.html?url=' + encodeURIComponent ( targetUrl ) ;
377+ saveHistory ( targetUrl , input ) ;
339378 }
340379
341380 updateTab ( activeTabId , {
@@ -369,12 +408,10 @@ <h2 id="side-panel-title" class="font-black text-[10px] tracking-widest uppercas
369408 tabStrip . insertBefore ( tabEl , addBtn ) ;
370409 } ) ;
371410
372- // Clean up removed viewports
373411 viewportContainer . querySelectorAll ( '.viewport' ) . forEach ( vp => {
374412 if ( ! tabs . find ( t => t . id === vp . dataset . id ) ) vp . remove ( ) ;
375413 } ) ;
376414
377- // Handle iframe generation and synchronization
378415 tabs . forEach ( tab => {
379416 let vp = viewportContainer . querySelector ( `.viewport[data-id="${ tab . id } "]` ) ;
380417 if ( ! vp ) {
@@ -393,7 +430,6 @@ <h2 id="side-panel-title" class="font-black text-[10px] tracking-widest uppercas
393430 zoomCont . style . height = ( 100 / tab . zoom ) + '%' ;
394431
395432 const iframe = vp . querySelector ( 'iframe' ) ;
396- // Only update src if it's different and we are currently active
397433 if ( isActive && iframe . getAttribute ( 'src' ) !== tab . proxyUrl ) {
398434 iframe . src = tab . proxyUrl ;
399435 }
@@ -419,26 +455,28 @@ <h2 id="side-panel-title" class="font-black text-[10px] tracking-widest uppercas
419455
420456 closeSidePanel . onclick = ( ) => sidePanel . classList . remove ( 'open' ) ;
421457
422- // Notification handler for iFrame content to update shell UI
423458 window . addEventListener ( 'message' , ( e ) => {
424459 if ( e . data . type === 'navigate' ) {
425460 navigate ( e . data . query ) ;
426461 }
427462 if ( e . data . type === 'metadata' ) {
428- // If the message contains a URL, update the address bar and favicon
429463 const updates = { } ;
430464 if ( e . data . title ) {
431465 updates . title = e . data . title ;
432- updates . favicon = getFavicon ( e . data . title ) ; // Update favicon based on the target URL
466+ updates . favicon = getFavicon ( e . data . title ) ;
433467 }
434468 updateTab ( activeTabId , updates ) ;
435469 }
436470 if ( e . data . type === 'request-current-tab' ) {
437471 const tab = tabs . find ( t => t . id === activeTabId ) ;
438472 e . source . postMessage ( { type : 'response-current-tab' , url : tab . url , title : tab . title , favicon : tab . favicon } , '*' ) ;
439473 }
474+ if ( e . data . type === 'update-settings' ) {
475+ applySystemSettings ( ) ;
476+ }
440477 } ) ;
441478
479+ applySystemSettings ( ) ;
442480 if ( ! loadSession ( ) ) createTab ( ) ;
443481 setInterval ( ( ) => {
444482 const now = new Date ( ) ;
0 commit comments