@@ -36,6 +36,7 @@ window.pathApp = {
3636 } ,
3737 bridgeLanguageListenerRegistered : false ,
3838 bridgeMermaidRenderQueue : Promise . resolve ( ) ,
39+ pendingWindowVisibility : null ,
3940 semanticA11yLastSummaryKey : '' ,
4041 semanticA11yLastAnnouncementAt : 0 ,
4142
@@ -101,6 +102,7 @@ window.pathApp = {
101102 this . _sendBridgeMessage ( 'identify' , this . _getBridgeIdentifyPayload ( 'frontend' ) ) ;
102103 this . _ensureLanguageSyncListener ( ) ;
103104 this . syncLanguageWithBridge ( ) ;
105+ this . _flushPendingWindowVisibility ( 'socket-open' ) ;
104106 } ;
105107 this . ws . onmessage = ( e ) => {
106108 try {
@@ -254,6 +256,7 @@ window.pathApp = {
254256 this . _sendBridgeMessage ( 'identify' , this . _getBridgeIdentifyPayload ( 'frontend' ) ) ;
255257 this . _ensureLanguageSyncListener ( ) ;
256258 this . syncLanguageWithBridge ( ) ;
259+ this . _flushPendingWindowVisibility ( 'socket-reuse' ) ;
257260 }
258261 } ,
259262
@@ -497,6 +500,68 @@ window.pathApp = {
497500 return true ;
498501 } ,
499502
503+ _waitForBridgeSocketOpen : async function ( timeoutMs = 2500 ) {
504+ const budget = Math . max ( 0 , Number ( timeoutMs ) || 0 ) ;
505+ if ( this . ws && this . ws . readyState === WebSocket . OPEN ) {
506+ return true ;
507+ }
508+ if ( budget === 0 ) {
509+ return false ;
510+ }
511+ this . _connectBridgeSocket ( ) ;
512+ const startedAt = Date . now ( ) ;
513+ while ( Date . now ( ) - startedAt < budget ) {
514+ if ( this . ws && this . ws . readyState === WebSocket . OPEN ) {
515+ return true ;
516+ }
517+ await new Promise ( ( resolve ) => setTimeout ( resolve , 80 ) ) ;
518+ this . _connectBridgeSocket ( ) ;
519+ }
520+ return this . ws && this . ws . readyState === WebSocket . OPEN ;
521+ } ,
522+
523+ _flushPendingWindowVisibility : function ( reason = 'flush' ) {
524+ if ( typeof this . pendingWindowVisibility !== 'boolean' ) {
525+ return false ;
526+ }
527+ const visible = this . pendingWindowVisibility ;
528+ const sent = this . _sendBridgeMessage ( 'setWindowVisible' , { visible } ) ;
529+ if ( sent ) {
530+ console . log ( `[PathApp] Flushed pending setWindowVisible(${ visible } ) via ${ reason } .` ) ;
531+ this . pendingWindowVisibility = null ;
532+ return true ;
533+ }
534+ return false ;
535+ } ,
536+
537+ requestBridgeWindowVisibility : async function ( visible , options = { } ) {
538+ const targetVisible = visible === true ;
539+ const waitMs = Math . max ( 0 , Number ( options . waitMs ) || 0 ) ;
540+ const reason = String ( options . reason || 'manual' ) ;
541+
542+ const sentImmediately = this . _sendBridgeMessage ( 'setWindowVisible' , { visible : targetVisible } ) ;
543+ if ( sentImmediately ) {
544+ console . log ( `[PathApp] Sent setWindowVisible(${ targetVisible } ) immediately. reason=${ reason } ` ) ;
545+ this . pendingWindowVisibility = null ;
546+ return true ;
547+ }
548+
549+ this . pendingWindowVisibility = targetVisible ;
550+ this . _connectBridgeSocket ( ) ;
551+ if ( waitMs === 0 ) {
552+ console . warn ( `[PathApp] Deferred setWindowVisible(${ targetVisible } ) until socket opens. reason=${ reason } ` ) ;
553+ return false ;
554+ }
555+
556+ const ready = await this . _waitForBridgeSocketOpen ( waitMs ) ;
557+ if ( ! ready ) {
558+ console . warn ( `[PathApp] Bridge socket not ready for setWindowVisible(${ targetVisible } ). reason=${ reason } ` ) ;
559+ return false ;
560+ }
561+ const flushed = this . _flushPendingWindowVisibility ( `wait-${ reason } ` ) ;
562+ return flushed ;
563+ } ,
564+
500565 _getBridgeMermaidConfig : function ( theme = 'dark' ) {
501566 return {
502567 startOnLoad : false ,
@@ -1656,15 +1721,11 @@ window.pathApp = {
16561721 // 单窗口切换:隐藏 Godot,显示 Tauri。
16571722 if ( window . __TAURI__ && window . __TAURI__ . core && typeof window . __TAURI__ . core . invoke === 'function' ) {
16581723 // 1. Hide Godot window via PathBridge WebSocket.
1659- if (
1660- multiWindowOptions . singleWindowMode &&
1661- this . ws &&
1662- this . ws . readyState === WebSocket . OPEN
1663- ) {
1664- this . ws . send ( JSON . stringify ( {
1665- type : 'setWindowVisible' ,
1666- payload : { visible : false }
1667- } ) ) ;
1724+ if ( multiWindowOptions . singleWindowMode ) {
1725+ void this . requestBridgeWindowVisibility ( false , {
1726+ waitMs : 1200 ,
1727+ reason : 'exit-pathmode'
1728+ } ) ;
16681729 }
16691730 // 2. Restore Tauri window via Rust IPC.
16701731 if ( multiWindowOptions . restoreTauriWhenPathmodeExits ) {
0 commit comments