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+ }
75123} ;
76124
125+ const enqueueDeepLink = ( url : string ) => {
126+ pendingDeepLinks . push ( url ) ;
127+ processPendingDeepLinks ( ) ;
128+ } ;
129+
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,17 +186,50 @@ const agents = [
100186 }
101187] ;
102188
189+ const registerDeepLinkProtocol = ( ) => {
190+ try {
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 ) ;
197+ }
198+ } catch ( error ) {
199+ log . error ( '注册深度链接协议失败:' , error ) ;
200+ }
201+ } ;
202+
203+ for ( const arg of process . argv ) {
204+ if ( isDeepLinkUrl ( arg ) ) {
205+ pendingDeepLinks . push ( arg ) ;
206+ }
207+ }
208+
103209// 单例模式
104210const gotTheLock = app . requestSingleInstanceLock ( ) ;
105211if ( ! gotTheLock ) {
106212 doQuit ( )
107213} else {
108214 // 试图启动第二个应用实例
109- app . on ( 'second-instance' , showWindow ) ;
215+ app . on ( 'second-instance' , ( _event , commandLine ) => {
216+ const urls = commandLine . filter ( isDeepLinkUrl ) ;
217+ if ( urls . length > 0 ) {
218+ urls . forEach ( handleDeepLink ) ;
219+ }
220+ showWindow ( ) ;
221+ } ) ;
110222
111223 // 监听应用被激活
112224 app . on ( 'activate' , showWindow ) ;
113225
226+ if ( process . platform === 'darwin' ) {
227+ app . on ( 'open-url' , ( event , url ) => {
228+ event . preventDefault ( ) ;
229+ handleDeepLink ( url ) ;
230+ } ) ;
231+ }
232+
114233 app . whenReady ( ) . then ( async ( ) => {
115234 // 判断是否开机启动
116235 const isBoot = await isBootAutoLaunch ( ) ;
@@ -139,6 +258,8 @@ if (!gotTheLock) {
139258 // 等待后端启动
140259 await waitForReady ;
141260
261+ registerDeepLinkProtocol ( ) ;
262+
142263 // 设置请求头 Referer
143264 const agent = agents [ Math . floor ( Math . random ( ) * agents . length ) ] ;
144265 session . defaultSession . webRequest . onBeforeSendHeaders ( ( details , callback ) => {
@@ -156,4 +277,4 @@ if (!gotTheLock) {
156277 // 更新开机自启路径
157278 await updateAutoLaunchRegistration ( )
158279 } ) ;
159- }
280+ }
0 commit comments