diff --git a/apps/desktop/src/agent-pet-controller.ts b/apps/desktop/src/agent-pet-controller.ts index 117bf016..49d49da3 100644 --- a/apps/desktop/src/agent-pet-controller.ts +++ b/apps/desktop/src/agent-pet-controller.ts @@ -4,7 +4,7 @@ import { getAppStateSnapshot } from "./app-state.js"; import { defaultPetWindowSize, getDefaultPetInitialPosition } from "./display.js"; import { debug, info } from "./logger.js"; import { transientDisplayMs, type OpenPetsReaction } from "./local-ipc-protocol.js"; -import { clearTransientReaction, createAgentPetWindow, getTransientReactionAnimationMs, loadExplicitPetContent, mergePetTransientDisplay, setPetReactionState, type PetStatusBadgeReaction, type PetTransientDisplay } from "./pet-window.js"; +import { clearTransientReaction, createAgentPetWindow, getTransientDisplayDurationMs, getTransientReactionAnimationMs, loadExplicitPetContent, mergePetTransientDisplay, setPetReactionState, type PetStatusBadgeReaction, type PetTransientDisplay } from "./pet-window.js"; const agentPetWindows = new Map(); const transientDisplays = new Map(); @@ -129,7 +129,8 @@ function setAgentDisplay(petId: string, display: PetTransientDisplay): void { if (existingAnimationTimer) clearTimeout(existingAnimationTimer); const animationMs = getTransientReactionAnimationMs(preparedDisplay); - if (animationMs !== null && animationMs < transientDisplayMs) { + const displayDurationMs = getTransientDisplayDurationMs(preparedDisplay); + if (animationMs !== null && animationMs < displayDurationMs) { const animationTimer = setTimeout(() => { const current = transientDisplays.get(petId); if (!current) return; @@ -150,7 +151,7 @@ function setAgentDisplay(petId: string, display: PetTransientDisplay): void { transientAnimationTimers.delete(petId); const window = agentPetWindows.get(petId); if (window && !window.isDestroyed()) void loadExplicitPetContent(window, petId, null, statusBadges.get(petId) ?? null); - }, transientDisplayMs); + }, displayDurationMs); transientTimers.set(petId, timer); const window = agentPetWindows.get(petId); if (window && !window.isDestroyed()) void loadExplicitPetContent(window, petId, preparedDisplay, statusBadges.get(petId) ?? null); diff --git a/apps/desktop/src/check-packaging-contract.ts b/apps/desktop/src/check-packaging-contract.ts index e7a4930d..36ab17ee 100644 --- a/apps/desktop/src/check-packaging-contract.ts +++ b/apps/desktop/src/check-packaging-contract.ts @@ -105,6 +105,9 @@ assert.match(reactionMessagesSource, /satisfies Record { if (!transientDisplay) return; transientDisplay = clearTransientReaction(transientDisplay); @@ -166,7 +167,7 @@ function setTransientDisplay(display: PetTransientDisplay): void { transientAnimationTimeout = null; } refreshDefaultPetContent(); - }, transientDisplayMs); + }, displayDurationMs); refreshDefaultPetContent(); } diff --git a/apps/desktop/src/pet-window.ts b/apps/desktop/src/pet-window.ts index 57b5aae4..19111528 100644 --- a/apps/desktop/src/pet-window.ts +++ b/apps/desktop/src/pet-window.ts @@ -359,6 +359,13 @@ export function getTransientReactionAnimationMs(display: PetTransientDisplay): n return typeof iterations === "number" ? row.durationMs * iterations : null; } +export function getTransientDisplayDurationMs(display: PetTransientDisplay): number { + const baseMs = display.reaction === "success" || display.reaction === "error" ? 5_000 : 4_000; + const message = display.message ?? display.reactionMessage; + if (!message) return baseMs; + return Math.min(12_000, Math.max(baseMs, message.length * 70)); +} + export function clearTransientReaction(display: PetTransientDisplay): PetTransientDisplay { if (!display.reaction) return display; return { ...display, reaction: undefined };