diff --git a/src/gui/src/helpers/launch_app.js b/src/gui/src/helpers/launch_app.js index 90660c46f..76ea57aff 100644 --- a/src/gui/src/helpers/launch_app.js +++ b/src/gui/src/helpers/launch_app.js @@ -558,6 +558,13 @@ const launch_app = async (options) => { // Add locale to URL iframe_url.searchParams.append('puter.locale', window.locale); + // Newer IPC dialogs this GUI can answer, comma-separated. The SDK + // consults this before posting a message an older GUI has no handler + // for: such a message is never replied to, and a reply timeout can't + // stand in for the check because legitimate replies only arrive when + // the user closes the dialog. + iframe_url.searchParams.append('puter.gui_features', 'feedback-dialog'); + // Add options.args to URL iframe_url.searchParams.append('puter.args', JSON.stringify(options.args ?? {})); diff --git a/src/puter-js/src/modules/UI.js b/src/puter-js/src/modules/UI.js index efaa65aa4..14b327b78 100644 --- a/src/puter-js/src/modules/UI.js +++ b/src/puter-js/src/modules/UI.js @@ -1688,6 +1688,18 @@ class UI extends EventListener { */ async showFeedbackDialog () { if ( this.env === 'app' ) { + // The host GUI advertises the IPC dialogs it can answer via + // `puter.gui_features` on the app iframe's URL (see + // launch_app.js). A GUI that predates this feature has no + // handler for the message and would never reply, hanging this + // never-rejecting promise forever — and a reply timeout can't + // stand in for the check, because a legitimate reply only + // arrives when the user closes the dialog. + const features = new URLSearchParams(globalThis.location?.search ?? '') + .get('puter.gui_features')?.split(',') ?? []; + if ( ! features.includes('feedback-dialog') ) { + return false; + } const result = await this.#postMessageAsync('showFeedbackDialog', {}); return result?.sent === true; }