From 7230580b18119ceb715392e61087bfefd5696057 Mon Sep 17 00:00:00 2001 From: Nariman Jelveh Date: Tue, 11 Aug 2026 18:19:46 -0700 Subject: [PATCH] fix: settle showFeedbackDialog instead of hanging on older host GUIs In the app environment showFeedbackDialog awaited an IPC reply with no capability check. A host GUI that predates this feature (self-hosted Puter running the live js.puter.com SDK) has no handler for the message and never replies, so the promise documented as 'never rejects' also never resolved. The GUI now advertises the IPC dialogs it can answer via a puter.gui_features param on the app iframe URL, and the SDK resolves false when 'feedback-dialog' isn't listed. A reply timeout could not substitute: legitimate replies only arrive when the user closes the dialog, so any timeout would false-negative while the user is typing. Older SDKs ignore the extra param. --- src/gui/src/helpers/launch_app.js | 7 +++++++ src/puter-js/src/modules/UI.js | 12 ++++++++++++ 2 files changed, 19 insertions(+) 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; }