diff --git a/apps/desktop/src/agent-pet-controller.ts b/apps/desktop/src/agent-pet-controller.ts index cbfc6432..4132738e 100644 --- a/apps/desktop/src/agent-pet-controller.ts +++ b/apps/desktop/src/agent-pet-controller.ts @@ -36,6 +36,7 @@ export function closeAgentPetIfOpen(petId: string): void { info("pet.agent", "close requested", { petId, windowId: window.id, activeWindows: agentPetWindows.size }); agentPetWindows.delete(petId); clearAgentDisplay(petId); + window.setIgnoreMouseEvents(false); window.destroy(); } @@ -77,6 +78,7 @@ export function closeAllAgentPets(): void { for (const petId of [...agentPetWindows.keys()]) { closeAgentPetIfOpen(petId); } + clearAllAgentDisplayTimers(); } export function refreshAgentPetContent(): void { @@ -173,6 +175,17 @@ function clearAgentDisplay(petId: string): void { statusBadges.delete(petId); } +function clearAllAgentDisplayTimers(): void { + for (const timer of transientTimers.values()) clearTimeout(timer); + for (const timer of transientAnimationTimers.values()) clearTimeout(timer); + for (const timer of statusBadgeTimers.values()) clearTimeout(timer); + transientTimers.clear(); + transientAnimationTimers.clear(); + statusBadgeTimers.clear(); + transientDisplays.clear(); + statusBadges.clear(); +} + function setStatusBadge(petId: string, reaction: OpenPetsReaction): void { if (reaction === "idle") { clearStatusBadge(petId); diff --git a/apps/desktop/src/default-pet-controller.ts b/apps/desktop/src/default-pet-controller.ts index a8b1f780..ac79747e 100644 --- a/apps/desktop/src/default-pet-controller.ts +++ b/apps/desktop/src/default-pet-controller.ts @@ -91,6 +91,8 @@ export function applyExternalPetSay(message: string, reaction?: OpenPetsReaction } export function destroyDefaultPet(): void { + clearDefaultPetDisplayTimers(); + if (!defaultPetWindow || defaultPetWindow.isDestroyed()) { debug("pet.default", "destroy skipped", { reason: "no-window" }); defaultPetWindow = null; @@ -101,6 +103,7 @@ export function destroyDefaultPet(): void { setDefaultPetPosition(readWindowPosition(defaultPetWindow)); const window = defaultPetWindow; defaultPetWindow = null; + window.setIgnoreMouseEvents(false); window.destroy(); } @@ -202,6 +205,17 @@ function clearStatusBadge(): void { statusBadgeTimeout = null; } +function clearDefaultPetDisplayTimers(): void { + if (transientDisplayTimeout) clearTimeout(transientDisplayTimeout); + if (transientAnimationTimeout) clearTimeout(transientAnimationTimeout); + if (statusBadgeTimeout) clearTimeout(statusBadgeTimeout); + transientDisplayTimeout = null; + transientAnimationTimeout = null; + statusBadgeTimeout = null; + transientDisplay = null; + statusBadge = null; +} + function isBusyStatusBadgeReaction(reaction: OpenPetsReaction): boolean { return reaction === "thinking" || reaction === "working" || reaction === "editing" || reaction === "running" || reaction === "testing" || reaction === "waiting"; } diff --git a/apps/desktop/src/lifecycle.ts b/apps/desktop/src/lifecycle.ts index 6d931eac..88eb3b92 100644 --- a/apps/desktop/src/lifecycle.ts +++ b/apps/desktop/src/lifecycle.ts @@ -7,6 +7,7 @@ import { stopLocalIpcServer } from "./local-ipc.js"; import { focusOpenTaskWindows } from "./windows.js"; let intentionalQuit = false; +let hardExitTimer: NodeJS.Timeout | null = null; export function installAppLifecycle(): void { app.on("second-instance", () => { @@ -30,6 +31,7 @@ export function installAppLifecycle(): void { app.on("before-quit", () => { intentionalQuit = true; info("app", "before quit cleanup begin"); + scheduleHardExitFallback("before-quit"); stopLocalIpcServer(); closeAllAgentPets(); destroyDefaultPet(); @@ -39,5 +41,15 @@ export function installAppLifecycle(): void { export function quitOpenPets(): void { intentionalQuit = true; info("app", "quit requested"); + scheduleHardExitFallback("quit-requested"); app.quit(); } + +function scheduleHardExitFallback(reason: string): void { + if (hardExitTimer) return; + hardExitTimer = setTimeout(() => { + info("app", "hard exit fallback", { reason }); + app.exit(0); + }, 2_000); + hardExitTimer.unref?.(); +}