From d61965ce6bec865fa101ff85cc468a0f730522c9 Mon Sep 17 00:00:00 2001 From: Brace Sproul Date: Mon, 28 Jul 2025 16:28:10 -0700 Subject: [PATCH] feat: Wait on cancel, better UX when canceling (#578) --- apps/web/package.json | 2 +- .../src/components/v2/cancel-stream-button.tsx | 18 ++++++++++++++---- apps/web/src/hooks/useCancelStream.tsx | 15 +++++++++++++-- yarn.lock | 10 +++++----- 4 files changed, 33 insertions(+), 12 deletions(-) diff --git a/apps/web/package.json b/apps/web/package.json index ba5f9aba..e7f3db3f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -49,7 +49,7 @@ "katex": "^0.16.21", "langgraph-nextjs-api-passthrough": "^0.1.4", "lodash": "^4.17.21", - "lucide-react": "^0.476.0", + "lucide-react": "^0.532.0", "next-themes": "^0.4.4", "nuqs": "^2.4.1", "react": "^19.0.0", diff --git a/apps/web/src/components/v2/cancel-stream-button.tsx b/apps/web/src/components/v2/cancel-stream-button.tsx index cbf166ff..aec4c39a 100644 --- a/apps/web/src/components/v2/cancel-stream-button.tsx +++ b/apps/web/src/components/v2/cancel-stream-button.tsx @@ -1,5 +1,5 @@ import { Button } from "@/components/ui/button"; -import { Loader2 } from "lucide-react"; +import { Loader2, SquareStop } from "lucide-react"; import { UseStream } from "@langchain/langgraph-sdk/react"; import { useCancelStream } from "@/hooks/useCancelStream"; @@ -16,7 +16,7 @@ export function CancelStreamButton({ runId, streamName, }: CancelStreamButtonProps) { - const { cancelRun } = useCancelStream({ + const { cancelRun, cancelLoading } = useCancelStream({ stream, threadId, runId, @@ -35,9 +35,19 @@ export function CancelStreamButton({ size="sm" variant="destructive" className="h-8 px-3 text-xs" + disabled={cancelLoading} > - - Stop {streamName} + {cancelLoading ? ( + <> + +

Stopping {streamName}

+ + ) : ( + <> + + Stop {streamName} + + )} ); } diff --git a/apps/web/src/hooks/useCancelStream.tsx b/apps/web/src/hooks/useCancelStream.tsx index bc5e0ce1..e10a801f 100644 --- a/apps/web/src/hooks/useCancelStream.tsx +++ b/apps/web/src/hooks/useCancelStream.tsx @@ -1,6 +1,7 @@ import { UseStream } from "@langchain/langgraph-sdk/react"; import { PlannerGraphState } from "@open-swe/shared/open-swe/planner/types"; import { GraphState } from "@open-swe/shared/open-swe/types"; +import { useState } from "react"; import { toast } from "sonner"; interface UseCancelStreamProps { @@ -16,6 +17,7 @@ export function useCancelStream({ runId, streamName, }: UseCancelStreamProps) { + const [cancelLoading, setCancelLoading] = useState(false); const cancelRun = async () => { if (!threadId || !runId) { toast.error(`Cannot cancel ${streamName}: Missing thread or run ID`); @@ -23,9 +25,12 @@ export function useCancelStream({ } try { - await stream.client.runs.cancel(threadId, runId); + setCancelLoading(true); + await stream.client.runs.cancel(threadId, runId, true); toast.success(`${streamName} cancelled successfully`, { description: "The running operation has been stopped", + duration: 5000, + richColors: true, }); } catch (error) { const errorStr = String(error); @@ -34,15 +39,21 @@ export function useCancelStream({ if (isAbortError) { toast.info(`${streamName} operation cancelled`, { description: "The stream was successfully stopped", + duration: 5000, + richColors: true, }); } else { console.error(`Error cancelling ${streamName} run:`, error); toast.error(`Failed to cancel ${streamName}`, { description: errorStr || "Unknown error occurred", + duration: 5000, + richColors: true, }); } + } finally { + setCancelLoading(false); } }; - return { cancelRun }; + return { cancelRun, cancelLoading }; } diff --git a/yarn.lock b/yarn.lock index f61d0233..b56bbbd8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4257,7 +4257,7 @@ __metadata: katex: ^0.16.21 langgraph-nextjs-api-passthrough: ^0.1.4 lodash: ^4.17.21 - lucide-react: ^0.476.0 + lucide-react: ^0.532.0 next: ^15.2.3 next-themes: ^0.4.4 nuqs: ^2.4.1 @@ -14594,12 +14594,12 @@ __metadata: languageName: node linkType: hard -"lucide-react@npm:^0.476.0": - version: 0.476.0 - resolution: "lucide-react@npm:0.476.0" +"lucide-react@npm:^0.532.0": + version: 0.532.0 + resolution: "lucide-react@npm:0.532.0" peerDependencies: react: ^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0 - checksum: fcfb5a4ce7be4d6184cca9cd1641bc9c967b70e8a29d2704d3a71a4c08d58b89aaabfbc436f323e7a93b2d2ef5f46782e99a88118267ed6f0670df70d35805d8 + checksum: 5d2312b8836f8502f45bf6dbc355c7ab980b9f1f87ad8d12b81bc2f2de51d6176326aecf5bef09f56f65a50bef4d183b6df875a9d0b50777994f1effc27036e8 languageName: node linkType: hard