From 043cdc7cb24a62ac1da427b47520b453fff51116 Mon Sep 17 00:00:00 2001 From: Gagan Date: Tue, 7 Jul 2026 14:58:47 +0530 Subject: [PATCH] fix(apps): stop reinstall from yanking the user out of the agent dialog MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two races around the post-install prompt: - AppsView kept a stale selectedFolder after the open app was uninstalled; on reinstall the 4s apps:list poll matched it again and instantly swapped in the app frame, unmounting the catalog and the enable-agents dialog mid-choice. Clear the selection when its app disappears from the list. - Bundled agents materialized on the NEXT apps:list poll, not at install — so the dialog's bg-task:get/patch hit a task that didn't exist yet if the user acted within ~4s. Install handlers now call syncAppAgents synchronously. --- apps/x/apps/main/src/ipc.ts | 4 ++++ apps/x/apps/renderer/src/components/apps/apps-view.tsx | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/apps/x/apps/main/src/ipc.ts b/apps/x/apps/main/src/ipc.ts index 607dc213..6c60ea63 100644 --- a/apps/x/apps/main/src/ipc.ts +++ b/apps/x/apps/main/src/ipc.ts @@ -1646,6 +1646,9 @@ export function setupIpcHandlers() { const preview = appInstallPreviews.get(args.name) ?? await appsInstaller.previewInstall(record); const result = await appsInstaller.installFromRegistry(record, preview); appInstallPreviews.delete(args.name); + // Materialize bundled agents NOW, not on the next apps:list poll — the + // renderer's post-install enable dialog patches these tasks immediately. + if (result.app) await appsAgents.syncAppAgents(result.app); capture('app_installed', { name: args.name }); return result; }, @@ -1654,6 +1657,7 @@ export function setupIpcHandlers() { return appsInstaller.previewUrlInstall(args.url); } const result = await appsInstaller.confirmUrlInstall(args.url); + if (result.app) await appsAgents.syncAppAgents(result.app); capture('app_installed', { name: result.app.manifest?.name ?? result.app.folder }); return result; }, diff --git a/apps/x/apps/renderer/src/components/apps/apps-view.tsx b/apps/x/apps/renderer/src/components/apps/apps-view.tsx index 44db99fa..9fdb89de 100644 --- a/apps/x/apps/renderer/src/components/apps/apps-view.tsx +++ b/apps/x/apps/renderer/src/components/apps/apps-view.tsx @@ -186,6 +186,11 @@ export function AppsView({ initialAppFolder, initialVersion, onNewApp }: { const r = await window.ipc.invoke('apps:list', {}) if (cancelled) return setApps(r.apps) + // Drop a selection whose app no longer exists (uninstalled while + // open). Left stale, a later reinstall makes this view yank the user + // into the app frame mid-flow — e.g. while they're in the catalog's + // post-install agent dialog. + setSelectedFolder((cur) => (cur && !r.apps.some((a) => a.folder === cur) ? null : cur)) setServerError(r.serverRunning ? null : (r.serverError ?? 'Apps server is not running.')) } catch (e) { if (!cancelled) setServerError(e instanceof Error ? e.message : String(e))