From c47f8b6a2c01d21361590d839ae4fb6d199d3e62 Mon Sep 17 00:00:00 2001 From: Patryk Sztuczka Date: Fri, 9 Oct 2026 10:55:45 +0200 Subject: [PATCH] fix(web): usage limit recovery errors no longer crush the composer banner Show Resume/Snooze failures as an error toast instead of inline text in the banner's auto-sized actions column, which widened to fit the message and squeezed the title down to a few characters. Co-Authored-By: Claude Opus 5.5 --- .../chat/UsageLimitRecoveryBanner.tsx | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/apps/web/src/components/chat/UsageLimitRecoveryBanner.tsx b/apps/web/src/components/chat/UsageLimitRecoveryBanner.tsx index 9e2a809835b0..dc7ea2de2d57 100644 --- a/apps/web/src/components/chat/UsageLimitRecoveryBanner.tsx +++ b/apps/web/src/components/chat/UsageLimitRecoveryBanner.tsx @@ -6,6 +6,7 @@ import { import { GaugeIcon } from "lucide-react"; import { useEffect, useState } from "react"; import { Button } from "../ui/button"; +import { toastManager } from "../ui/toast"; import type { ComposerBannerStackItem } from "./ComposerBannerStack"; type RecoveryProps = { @@ -35,7 +36,6 @@ export function usageLimitRecoveryBannerItem(props: RecoveryProps): ComposerBann function RecoveryActions({ runId, resetAt, recovery, snoozedUntil, onChange }: RecoveryProps) { const [pending, setPending] = useState(false); - const [error, setError] = useState(null); const [nowMs, setNowMs] = useState(() => Date.now()); useEffect(() => { const delay = Date.parse(resetAt ?? "") - Math.max(nowMs, Date.now()); @@ -56,12 +56,15 @@ function RecoveryActions({ runId, resetAt, recovery, snoozedUntil, onChange }: R async function toggle(action: "resume" | "snooze") { if (resetAt === null) return; if (action === "snooze" && !snoozed && Date.parse(resetAt) <= Date.now()) { - setError("The reset time has passed. Retry the thread manually."); + toastManager.add({ + type: "error", + title: "Could not snooze thread", + description: "The reset time has passed. Retry the thread manually.", + }); setNowMs(Date.now()); return; } setPending(true); - setError(null); try { await onChange({ runId, @@ -69,12 +72,18 @@ function RecoveryActions({ runId, resetAt, recovery, snoozedUntil, onChange }: R ...(action === "resume" ? { autoResume: !scheduled } : { snooze: !snoozed }), }); } catch (cause) { - setError(cause instanceof Error ? cause.message : "Could not change limit recovery."); + toastManager.add({ + type: "error", + title: action === "resume" ? "Could not change auto-resume" : "Could not snooze thread", + description: cause instanceof Error ? cause.message : "An error occurred.", + }); } setPending(false); } + // Failures go to a toast: the actions column is auto-sized, so inline error + // text would widen it and crush the title. return ( -
+ <> @@ -88,11 +97,6 @@ function RecoveryActions({ runId, resetAt, recovery, snoozedUntil, onChange }: R {pending ? "Saving..." : "Snooze until reset"} ) : null} - {error ? ( -

- {error} -

- ) : null} -
+ ); }