@@ -90,6 +90,160 @@ export default function Browser() {
9090 } ;
9191 } , [ activeTab , url ] ) ;
9292
93+ useEffect ( ( ) => {
94+ if ( ! activeTab ) return ;
95+ const iframe = iframeRefs . current [ activeTab . id ] ;
96+ if ( ! iframe ) return ;
97+
98+ const setupIntercept = ( ) => {
99+ try {
100+ const iframeWindow = iframe . contentWindow as any ;
101+ if ( ! iframeWindow || iframeWindow . __tabInterceptSetup ) return ;
102+
103+ iframeWindow . __tabInterceptSetup = true ;
104+
105+ const config = ( window as any ) . __uv$config ;
106+
107+ const originalOpen = iframeWindow . open ;
108+ iframeWindow . open = function ( url ?: string , target ?: string , features ?: string ) {
109+ if ( ! target || target === "_blank" || target === "_new" ) {
110+ try {
111+ const fullUrl = url ? new URL ( url , iframeWindow . location . href ) . href : "about:blank" ;
112+
113+ let actualUrl = fullUrl ;
114+ if ( config && fullUrl . includes ( config . prefix ) ) {
115+ const encoded = fullUrl . substring ( fullUrl . indexOf ( config . prefix ) + config . prefix . length ) ;
116+ actualUrl = config . decodeUrl ( encoded ) ;
117+ }
118+
119+ const newId = Date . now ( ) ;
120+ setTabs ( ( prev ) => [
121+ ...prev . map ( ( t ) => ( { ...t , active : false } ) ) ,
122+ { id : newId , title : "New Tab" , url : actualUrl , active : true , reloadKey : 0 }
123+ ] ) ;
124+
125+ return null ;
126+ } catch ( err ) {
127+ console . warn ( "Failed to intercept window.open:" , err ) ;
128+ }
129+ }
130+ return originalOpen . call ( iframeWindow , url , target , features ) ;
131+ } ;
132+
133+ iframeWindow . addEventListener ( "click" , ( e : MouseEvent ) => {
134+ const target = e . target as HTMLElement ;
135+ const anchor = target . closest ( "a" ) ;
136+
137+ if ( anchor ) {
138+ const linkTarget = anchor . getAttribute ( "target" ) ;
139+ const hasModifier = e . ctrlKey || e . metaKey ;
140+
141+ if ( linkTarget === "_blank" || linkTarget === "_new" || hasModifier ) {
142+ e . preventDefault ( ) ;
143+ e . stopPropagation ( ) ;
144+
145+ const href = anchor . getAttribute ( "href" ) ;
146+ if ( href ) {
147+ try {
148+ const fullUrl = new URL ( href , iframeWindow . location . href ) . href ;
149+
150+ let actualUrl = fullUrl ;
151+ if ( config && fullUrl . includes ( config . prefix ) ) {
152+ const encoded = fullUrl . substring ( fullUrl . indexOf ( config . prefix ) + config . prefix . length ) ;
153+ actualUrl = config . decodeUrl ( encoded ) ;
154+ }
155+
156+ const newId = Date . now ( ) ;
157+ setTabs ( ( prev ) => [
158+ ...prev . map ( ( t ) => ( { ...t , active : false } ) ) ,
159+ { id : newId , title : "New Tab" , url : actualUrl , active : true , reloadKey : 0 }
160+ ] ) ;
161+ } catch ( err ) {
162+ console . warn ( "Failed to intercept link click:" , err ) ;
163+ }
164+ }
165+ }
166+ }
167+ } , true ) ;
168+
169+ iframeWindow . addEventListener ( "auxclick" , ( e : MouseEvent ) => {
170+ if ( e . button !== 1 ) return ;
171+
172+ const target = e . target as HTMLElement ;
173+ const anchor = target . closest ( "a" ) ;
174+
175+ if ( anchor ) {
176+ e . preventDefault ( ) ;
177+ e . stopPropagation ( ) ;
178+
179+ const href = anchor . getAttribute ( "href" ) ;
180+ if ( href ) {
181+ try {
182+ const fullUrl = new URL ( href , iframeWindow . location . href ) . href ;
183+
184+ let actualUrl = fullUrl ;
185+ if ( config && fullUrl . includes ( config . prefix ) ) {
186+ const encoded = fullUrl . substring ( fullUrl . indexOf ( config . prefix ) + config . prefix . length ) ;
187+ actualUrl = config . decodeUrl ( encoded ) ;
188+ }
189+
190+ const newId = Date . now ( ) ;
191+ setTabs ( ( prev ) => [
192+ ...prev . map ( ( t ) => ( { ...t , active : false } ) ) ,
193+ { id : newId , title : "New Tab" , url : actualUrl , active : true , reloadKey : 0 }
194+ ] ) ;
195+ } catch ( err ) {
196+ console . warn ( "Failed to intercept middle-click:" , err ) ;
197+ }
198+ }
199+ }
200+ } , true ) ;
201+
202+ } catch ( err ) {
203+ }
204+ } ;
205+
206+ const handleLoad = ( ) => {
207+ setupIntercept ( ) ;
208+ } ;
209+
210+ iframe . addEventListener ( "load" , handleLoad ) ;
211+ setupIntercept ( ) ;
212+
213+ return ( ) => {
214+ iframe . removeEventListener ( "load" , handleLoad ) ;
215+ } ;
216+ } , [ activeTab ] ) ;
217+
218+ useEffect ( ( ) => {
219+ const handleKeyDown = ( e : KeyboardEvent ) => {
220+ if ( ( e . ctrlKey || e . metaKey ) && ( e . key === "t" || e . key === "n" ) ) {
221+ e . preventDefault ( ) ;
222+ }
223+ } ;
224+
225+ const handleAuxClick = ( e : MouseEvent ) => {
226+ if ( e . button === 1 ) {
227+ e . preventDefault ( ) ;
228+ }
229+ } ;
230+
231+ window . addEventListener ( "keydown" , handleKeyDown ) ;
232+ window . addEventListener ( "auxclick" , handleAuxClick ) ;
233+
234+ const originalWindowOpen = window . open ;
235+ window . open = function ( ) {
236+ console . warn ( "Blocked external window.open call" ) ;
237+ return null ;
238+ } ;
239+
240+ return ( ) => {
241+ window . removeEventListener ( "keydown" , handleKeyDown ) ;
242+ window . removeEventListener ( "auxclick" , handleAuxClick ) ;
243+ window . open = originalWindowOpen ;
244+ } ;
245+ } , [ ] ) ;
246+
93247 const setActiveTab = ( id : number ) => {
94248 setTabs ( ( prev ) => prev . map ( ( tab ) => ( { ...tab , active : tab . id === id } ) ) ) ;
95249 } ;
0 commit comments