@@ -76,6 +76,9 @@ let currentFile: DriveFile | null = null;
7676let pendingOpen : { filename : string ; bytes : ArrayBuffer } | null = null ;
7777let pickerApiLoaded = false ;
7878let tokenClient : TokenClient | null = null ;
79+ // Set while waiting for the embedded app to ack a kw-close-request, so
80+ // handleFrameMessage knows what to run once it's safe to tear the iframe down.
81+ let pendingClose : ( ( ) => void ) | null = null ;
7982// Cached so the GIS script loads at most once, and concurrent callers share it.
8083let gisReady : Promise < void > | null = null ;
8184
@@ -264,17 +267,33 @@ function showHost(file: DriveFile, bytes: ArrayBuffer): void {
264267 setRoot ( cloneTemplate ( 'tpl-host' ) ) ;
265268 qs ( '#host-filename' ) . textContent = file . name ;
266269 qs ( '[data-action="back-to-drive"]' ) . addEventListener ( 'click' , ( ) => {
267- window . removeEventListener ( 'message' , handleFrameMessage ) ;
268- currentFile = null ;
269- pendingOpen = null ;
270- showChooser ( ) ;
270+ requestCloseIframe ( ( ) => {
271+ window . removeEventListener ( 'message' , handleFrameMessage ) ;
272+ currentFile = null ;
273+ pendingOpen = null ;
274+ showChooser ( ) ;
275+ } ) ;
271276 } ) ;
272277 window . addEventListener ( 'message' , handleFrameMessage ) ;
273278 // Setting src last means the iframe's script (and its kw-ready handshake)
274279 // can't fire before the listener above is attached.
275280 qs < HTMLIFrameElement > ( '#app-frame' ) . src = '0x67.html' ;
276281}
277282
283+ /** Ask the embedded app whether it's safe to remove the iframe — it may have
284+ * unsaved edits, in which case it shows its own discard-confirmation dialog
285+ * and only acks if the user agrees. `afterClose` runs once that ack arrives
286+ * (see handleFrameMessage's isCloseAckMessage branch); if the user cancels,
287+ * no ack ever comes and nothing happens, exactly like cancelling the same
288+ * dialog standalone. */
289+ function requestCloseIframe ( afterClose : ( ) => void ) : void {
290+ pendingClose = afterClose ;
291+ must ( qs < HTMLIFrameElement > ( '#app-frame' ) . contentWindow ) . postMessage (
292+ { type : 'kw-close-request' } ,
293+ APP_ORIGIN ,
294+ ) ;
295+ }
296+
278297function handleFrameMessage ( event : MessageEvent ) : void {
279298 if ( event . origin !== APP_ORIGIN ) return ;
280299 const iframe = document . getElementById ( 'app-frame' ) as HTMLIFrameElement | null ;
@@ -286,6 +305,10 @@ function handleFrameMessage(event: MessageEvent): void {
286305 source . postMessage ( { type : 'kw-open' , filename : open . filename , bytes : open . bytes } , APP_ORIGIN ) ;
287306 } else if ( isSaveMessage ( event . data ) ) {
288307 void saveToDrive ( event . data . bytes , source ) ;
308+ } else if ( isCloseAckMessage ( event . data ) ) {
309+ const afterClose = pendingClose ;
310+ pendingClose = null ;
311+ afterClose ?.( ) ;
289312 }
290313}
291314
0 commit comments