1- import { app , BrowserWindow , BrowserWindowConstructorOptions , session } from 'electron' ;
1+ import { app , BrowserWindow , BrowserWindowConstructorOptions , ipcMain , session } from 'electron' ;
22import path from 'node:path' ;
33import { startServer , storeInfo } from "./server" ;
44import { doQuit , initTray , showWindow } from "./tray" ;
@@ -11,7 +11,15 @@ import {isBootAutoLaunch, updateAutoLaunchRegistration, waitForNetworkReady} fro
1111const isDev = ! app . isPackaged ;
1212
1313// 主窗口
14- let mainWindow : BrowserWindow ;
14+ let mainWindow : BrowserWindow | null = null ;
15+
16+ // 深度链接相关
17+ const DEEP_LINK_SCHEME = 'pandora-box' ;
18+ const DEEP_LINK_HOST_INSTALL = 'install-config' ;
19+ const DEEP_LINK_EVENT = 'import-profile-from-deeplink' ;
20+ const DEEP_LINK_READY_EVENT = 'deeplink-handler-ready' ;
21+ const pendingDeepLinks : string [ ] = [ ] ;
22+ let deepLinkHandlerReady = false ;
1523// 屏蔽安全警告
1624process . env [ "ELECTRON_DISABLE_SECURITY_WARNINGS" ] = "true" ;
1725const createWindow = ( isBoot : boolean ) => {
@@ -44,6 +52,7 @@ const createWindow = (isBoot: boolean) => {
4452 } ;
4553 }
4654
55+ deepLinkHandlerReady = false ;
4756 mainWindow = new BrowserWindow ( windowOptions ) ;
4857
4958 // 隐藏菜单栏
@@ -62,6 +71,14 @@ const createWindow = (isBoot: boolean) => {
6271 log . error ( '页面加载失败:' , err ) ;
6372 } ) ;
6473
74+ mainWindow . webContents . on ( 'did-start-loading' , ( ) => {
75+ deepLinkHandlerReady = false ;
76+ } ) ;
77+
78+ mainWindow . webContents . on ( 'did-finish-load' , ( ) => {
79+ processPendingDeepLinks ( ) ;
80+ } ) ;
81+
6582 // 页面加载完成再显示,避免白屏
6683 mainWindow . webContents . once ( 'did-finish-load' , ( ) => {
6784 if ( isBoot ) {
@@ -72,8 +89,77 @@ const createWindow = (isBoot: boolean) => {
7289 log . info ( '页面加载成功' ) ;
7390 }
7491 } ) ;
92+
93+ mainWindow . on ( 'closed' , ( ) => {
94+ deepLinkHandlerReady = false ;
95+ mainWindow = null ;
96+ } ) ;
97+ } ;
98+
99+ const isDeepLinkUrl = ( arg : string | undefined ) : arg is string => {
100+ return typeof arg === 'string' && arg . startsWith ( `${ DEEP_LINK_SCHEME } ://` ) ;
101+ } ;
102+
103+ const processPendingDeepLinks = ( ) => {
104+ if ( ! mainWindow || mainWindow . isDestroyed ( ) || ! deepLinkHandlerReady ) {
105+ return ;
106+ }
107+
108+ if ( pendingDeepLinks . length === 0 ) {
109+ return ;
110+ }
111+
112+ const queue = pendingDeepLinks . splice ( 0 , pendingDeepLinks . length ) ;
113+ showWindow ( ) ;
114+
115+ for ( const url of queue ) {
116+ if ( ! url ) {
117+ continue ;
118+ }
119+
120+ log . info ( '处理深度链接队列:' , url ) ;
121+ mainWindow . webContents . send ( DEEP_LINK_EVENT , { rawUrl : url } ) ;
122+ }
123+ } ;
124+
125+ const enqueueDeepLink = ( url : string ) => {
126+ pendingDeepLinks . push ( url ) ;
127+ processPendingDeepLinks ( ) ;
75128} ;
76129
130+ function handleDeepLink ( url : string ) {
131+ const trimmed = url ?. trim ( ) ;
132+ if ( ! trimmed ) {
133+ return ;
134+ }
135+
136+ try {
137+ const parsedUrl = new URL ( trimmed ) ;
138+ if ( parsedUrl . protocol !== `${ DEEP_LINK_SCHEME } :` ) {
139+ return ;
140+ }
141+
142+ const host = parsedUrl . hostname || parsedUrl . host ;
143+ if ( host && host . toLowerCase ( ) === DEEP_LINK_HOST_INSTALL ) {
144+ log . info ( '收到深度链接:' , trimmed ) ;
145+ enqueueDeepLink ( trimmed ) ;
146+ } else {
147+ log . warn ( '未知深度链接:' , trimmed ) ;
148+ }
149+ } catch ( error ) {
150+ log . error ( '解析深度链接失败:' , error ) ;
151+ }
152+ }
153+
154+ ipcMain . on ( DEEP_LINK_READY_EVENT , ( event ) => {
155+ if ( ! mainWindow || event . sender !== mainWindow . webContents ) {
156+ return ;
157+ }
158+
159+ deepLinkHandlerReady = true ;
160+ processPendingDeepLinks ( ) ;
161+ } ) ;
162+
77163// 等待 backend 传来的 port 和 secret
78164let resolveReady : ( ) => void ;
79165const waitForReady = new Promise < void > ( ( resolve ) => {
@@ -100,37 +186,23 @@ const agents = [
100186 }
101187] ;
102188
103- // 注册自定义协议
104- if ( ! app . isDefaultProtocolClient ( 'pandora-box' ) ) {
105- app . setAsDefaultProtocolClient ( 'pandora-box' ) ;
106- }
107-
108- // 处理深度链接的函数
109- function handleDeepLink ( url : string ) {
110- log . info ( '收到深度链接:' , url ) ;
111-
112- // 解析URL
189+ const registerDeepLinkProtocol = ( ) => {
113190 try {
114- const parsedUrl = new URL ( url ) ;
115- if ( parsedUrl . protocol === 'pandora-box:' && parsedUrl . hostname === 'install-config' ) {
116- const subUrl = parsedUrl . searchParams . get ( 'url' ) ;
117-
118- if ( subUrl ) {
119- log . info ( '准备导入配置, URL:' , subUrl ) ;
120-
121- // 显示窗口
122- showWindow ( ) ;
123-
124- // 向渲染进程发送导入配置的消息
125- if ( mainWindow && mainWindow . webContents ) {
126- mainWindow . webContents . send ( 'import-profile-from-deeplink' , {
127- url : subUrl
128- } ) ;
129- }
130- }
191+ if ( process . defaultApp && process . argv . length >= 2 ) {
192+ const exePath = process . execPath ;
193+ const resolvedPath = path . resolve ( process . argv [ 1 ] ) ;
194+ app . setAsDefaultProtocolClient ( DEEP_LINK_SCHEME , exePath , [ resolvedPath ] ) ;
195+ } else if ( ! app . isDefaultProtocolClient ( DEEP_LINK_SCHEME ) ) {
196+ app . setAsDefaultProtocolClient ( DEEP_LINK_SCHEME ) ;
131197 }
132198 } catch ( error ) {
133- log . error ( '解析深度链接失败:' , error ) ;
199+ log . error ( '注册深度链接协议失败:' , error ) ;
200+ }
201+ } ;
202+
203+ for ( const arg of process . argv ) {
204+ if ( isDeepLinkUrl ( arg ) ) {
205+ pendingDeepLinks . push ( arg ) ;
134206 }
135207}
136208
@@ -139,20 +211,25 @@ const gotTheLock = app.requestSingleInstanceLock();
139211if ( ! gotTheLock ) {
140212 doQuit ( )
141213} else {
142- // 试图启动第二个应用实例,也需要处理深度链接
143- app . on ( 'second-instance' , ( event , commandLine ) => {
144- // Windows/Linux 下从命令行参数获取深度链接
145- const url = commandLine . find ( arg => arg . startsWith ( 'pandora-box://' ) ) ;
146- if ( url ) {
147- handleDeepLink ( url ) ;
148- } else {
149- showWindow ( ) ;
214+ // 试图启动第二个应用实例
215+ app . on ( 'second-instance' , ( _event , commandLine ) => {
216+ const urls = commandLine . filter ( isDeepLinkUrl ) ;
217+ if ( urls . length > 0 ) {
218+ urls . forEach ( handleDeepLink ) ;
150219 }
220+ showWindow ( ) ;
151221 } ) ;
152222
153223 // 监听应用被激活
154224 app . on ( 'activate' , showWindow ) ;
155225
226+ if ( process . platform === 'darwin' ) {
227+ app . on ( 'open-url' , ( event , url ) => {
228+ event . preventDefault ( ) ;
229+ handleDeepLink ( url ) ;
230+ } ) ;
231+ }
232+
156233 app . whenReady ( ) . then ( async ( ) => {
157234 // 判断是否开机启动
158235 const isBoot = await isBootAutoLaunch ( ) ;
@@ -181,6 +258,8 @@ if (!gotTheLock) {
181258 // 等待后端启动
182259 await waitForReady ;
183260
261+ registerDeepLinkProtocol ( ) ;
262+
184263 // 设置请求头 Referer
185264 const agent = agents [ Math . floor ( Math . random ( ) * agents . length ) ] ;
186265 session . defaultSession . webRequest . onBeforeSendHeaders ( ( details , callback ) => {
@@ -197,21 +276,5 @@ if (!gotTheLock) {
197276
198277 // 更新开机自启路径
199278 await updateAutoLaunchRegistration ( )
200-
201- // 处理 macOS 下的深度链接
202- if ( process . platform === 'darwin' ) {
203- app . on ( 'open-url' , ( event , url ) => {
204- event . preventDefault ( ) ;
205- handleDeepLink ( url ) ;
206- } ) ;
207- }
208-
209- // 处理 Windows/Linux 下启动时的深度链接
210- if ( process . platform !== 'darwin' ) {
211- const url = process . argv . find ( arg => arg . startsWith ( 'pandora-box://' ) ) ;
212- if ( url ) {
213- handleDeepLink ( url ) ;
214- }
215- }
216279 } ) ;
217- }
280+ }
0 commit comments