authorgravatar for crazywolf132@gmail.com0x1f99d <crazywolf132@gmail.com> 2026-03-09 12:24:56+11:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-03-08 18:24:56-07:00
log59e2944fb72d8ef8c3b55fec3be766abe48df820
treec400de0c1380f8aa29e0cc293ea3632c43784cb9
parent17942787aa06070aa21ad6ae357e08f674383c4d
signature Signed by PGP key B5690EEEBB952194

fix(web): add dismiss button to thread error banner (#588)

The thread error banner had no way to be dismissed. Users hitting unrecoverable errors (e.g. missing gh CLI during PR creation) were stuck with a persistent error banner and no way to clear it. Add a dismiss (X) button to ThreadErrorBanner using the existing AlertAction component, wired to clear the thread error via setThreadError(id, null). Closes #496

1 files changed, 24 insertions(+), 3 deletions(-)

apps/web/src/components/ChatView.tsx+24-3
......@@ -123,7 +123,7 @@ import {
123123} from "../keybindings";
124124import ChatMarkdown from "./ChatMarkdown";
125125import ThreadTerminalDrawer from "./ThreadTerminalDrawer";
126import { Alert, AlertDescription, AlertTitle } from "./ui/alert";
126import { Alert, AlertAction, AlertDescription, AlertTitle } from "./ui/alert";
127127import {
128128 BotIcon,
129129 ChevronDownIcon,
......@@ -3434,7 +3434,10 @@ export default function ChatView({ threadId }: ChatViewProps) {
34343434
34353435 {/* Error banner */}
34363436 <ProviderHealthBanner status={activeProviderStatus} />
3437 <ThreadErrorBanner error={activeThread.error} />
3437 <ThreadErrorBanner
3438 error={activeThread.error}
3439 onDismiss={() => setThreadError(activeThread.id, null)}
3440 />
34383441 <PlanModePanel activePlan={activePlan} />
34393442
34403443 {/* Messages */}
......@@ -4095,7 +4098,13 @@ const ChatHeader = memo(function ChatHeader({
40954098 );
40964099});
40974100
4098const ThreadErrorBanner = memo(function ThreadErrorBanner({ error }: { error: string | null }) {
4101const ThreadErrorBanner = memo(function ThreadErrorBanner({
4102 error,
4103 onDismiss,
4104}: {
4105 error: string | null;
4106 onDismiss?: () => void;
4107}) {
40994108 if (!error) return null;
41004109 return (
41014110 <div className="pt-3 mx-auto max-w-3xl">
......@@ -4104,6 +4113,18 @@ const ThreadErrorBanner = memo(function ThreadErrorBanner({ error }: { error: st
41044113 <AlertDescription className="line-clamp-3" title={error}>
41054114 {error}
41064115 </AlertDescription>
4116 {onDismiss && (
4117 <AlertAction>
4118 <button
4119 type="button"
4120 aria-label="Dismiss error"
4121 className="inline-flex size-6 items-center justify-center rounded-md text-destructive/60 transition-colors hover:text-destructive"
4122 onClick={onDismiss}
4123 >
4124 <XIcon className="size-3.5" />
4125 </button>
4126 </AlertAction>
4127 )}
41074128 </Alert>
41084129 </div>
41094130 );