返回 JoyAI-Echo
StreamErrorNotice.tsx
根目录 / echo_longvideo / Director_Agent / webui / src / components / thread / StreamErrorNotice.tsx
1 import { AlertTriangle, X } from "lucide-react";
2 import { useTranslation } from "react-i18next";
3
4 import { Button } from "@/components/ui/button";
5 import { cn } from "@/lib/utils";
6 import type { StreamError } from "@/lib/nanobot-client";
7
8 interface StreamErrorNoticeProps {
9 error: StreamError;
10 onDismiss: () => void;
11 }
12
13 /**
14 * Dismissible banner that surfaces transport-level faults the user needs to
15 * know about. Rendered above the composer so the message the fault referred
16 * to remains in view just above. ``role="alert"`` + ``aria-live="assertive"``
17 * ensures screen readers announce the failure.
18 */
19 export function StreamErrorNotice({ error, onDismiss }: StreamErrorNoticeProps) {
20 const { t } = useTranslation();
21
22 const { title, body } = resolveCopy(error, t);
23
24 return (
25 <div
26 role="alert"
27 aria-live="assertive"
28 className={cn(
29 "mb-2 flex items-start gap-2 rounded-lg border border-destructive/30",
30 "bg-destructive/10 px-3 py-2 text-[12px] leading-5 text-destructive",
31 "animate-in fade-in-0 slide-in-from-bottom-1",
32 )}
33 >
34 <AlertTriangle
35 className="mt-0.5 h-4 w-4 shrink-0"
36 aria-hidden
37 />
38 <div className="flex-1">
39 <p className="font-medium">{title}</p>
40 <p className="mt-0.5 text-destructive/80">{body}</p>
41 </div>
42 <Button
43 variant="ghost"
44 size="icon"
45 onClick={onDismiss}
46 aria-label={t("common.dismiss")}
47 className="h-6 w-6 shrink-0 text-destructive hover:bg-destructive/15 hover:text-destructive"
48 >
49 <X className="h-3.5 w-3.5" />
50 </Button>
51 </div>
52 );
53 }
54
55 function resolveCopy(
56 error: StreamError,
57 t: (key: string) => string,
58 ): { title: string; body: string } {
59 switch (error.kind) {
60 case "message_too_big":
61 return {
62 title: t("errors.messageTooBig.title"),
63 body: t("errors.messageTooBig.body"),
64 };
65 default: {
66 // Exhaustiveness guard: if a new StreamError kind is added, TS will
67 // complain here until we add a corresponding i18n branch.
68 const _exhaustive: never = error.kind;
69 return { title: String(_exhaustive), body: "" };
70 }
71 }
72 }
73
73 lines Plain Text