From 5b06118ec87081e1dbf8de754094d064dce46075 Mon Sep 17 00:00:00 2001 From: Forgotten Date: Fri, 20 Feb 2026 13:53:51 -0600 Subject: [PATCH] feat: add severity-based TTL defaults for toast notifications Info/success toasts auto-dismiss faster (3.5-4s) while warn/error toasts persist longer (8-10s). Callers can still override with explicit ttlMs. Dedupe, cooldown window, and MAX_TOASTS cap were already in place from Phase 1. Co-Authored-By: Claude Opus 4.6 --- ui/src/context/ToastContext.tsx | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ui/src/context/ToastContext.tsx b/ui/src/context/ToastContext.tsx index ce33bf1a..ec2c4a74 100644 --- a/ui/src/context/ToastContext.tsx +++ b/ui/src/context/ToastContext.tsx @@ -43,7 +43,12 @@ interface ToastContextValue { clearToasts: () => void; } -const DEFAULT_TTL_MS = 5000; +const DEFAULT_TTL_BY_TONE: Record = { + info: 4000, + success: 3500, + warn: 8000, + error: 10000, +}; const MIN_TTL_MS = 1500; const MAX_TTL_MS = 15000; const MAX_TOASTS = 5; @@ -52,8 +57,8 @@ const DEDUPE_MAX_AGE_MS = 20000; const ToastContext = createContext(null); -function normalizeTtl(value: number | undefined) { - const fallback = DEFAULT_TTL_MS; +function normalizeTtl(value: number | undefined, tone: ToastTone) { + const fallback = DEFAULT_TTL_BY_TONE[tone]; if (typeof value !== "number" || !Number.isFinite(value)) return fallback; return Math.max(MIN_TTL_MS, Math.min(MAX_TTL_MS, Math.floor(value))); } @@ -95,7 +100,7 @@ export function ToastProvider({ children }: { children: ReactNode }) { (input: ToastInput) => { const now = Date.now(); const tone = input.tone ?? "info"; - const ttlMs = normalizeTtl(input.ttlMs); + const ttlMs = normalizeTtl(input.ttlMs, tone); const dedupeKey = input.dedupeKey ?? input.id ?? `${tone}|${input.title}|${input.body ?? ""}|${input.action?.href ?? ""}`;