Fix desktop quit hang

This commit is contained in:
Alvin Unreal 2026-05-16 11:28:13 +02:00
parent 066bd07b5f
commit f1c01f1f21
3 changed files with 39 additions and 0 deletions

View file

@ -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);

View file

@ -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";
}

View file

@ -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?.();
}