import { memo } from "react"; import { Alert, AlertAction, AlertDescription } from "../ui/alert"; import { Button } from "../ui/button"; import { CircleAlertIcon, XIcon } from "lucide-react"; import { Tooltip, TooltipPopup, TooltipTrigger } from "../ui/tooltip"; export function getThreadErrorBannerMessage(error: string): string { let payload: unknown; try { payload = JSON.parse(error); } catch { return error; } if (typeof payload !== "object" || payload === null || !("error" in payload)) { return error; } const nestedError = payload.error; if (typeof nestedError !== "object" || nestedError === null || !("message" in nestedError)) { return error; } return typeof nestedError.message === "string" && nestedError.message.trim().length > 0 ? nestedError.message : error; } export function getThreadErrorBannerKey(threadKey: string, error: string | null): string | null { return error === null ? null : `${threadKey}\u0000${error}`; } export function shouldShowThreadErrorBanner( threadKey: string, error: string | null, isDismissed: boolean, ): boolean { return getThreadErrorBannerKey(threadKey, error) !== null && !isDismissed; } // Session-scoped (module-level so it survives ChatView remounts, e.g. route // changes between threads). Mirrors the branch-mismatch banner: a dismissal // is remembered per thread key plus message, so navigating away to a thread // with no error cannot resurrect the banner, while a different error message // on the same thread still appears. const sessionDismissedThreadErrorBannerKeys = new Set(); export function dismissThreadErrorBannerForSession(bannerKey: string | null): void { if (bannerKey !== null) { sessionDismissedThreadErrorBannerKeys.add(bannerKey); } } export function isThreadErrorBannerDismissedForSession(bannerKey: string | null): boolean { return bannerKey !== null && sessionDismissedThreadErrorBannerKeys.has(bannerKey); } export const ThreadErrorBanner = memo(function ThreadErrorBanner({ error, onDismiss, }: { error: string | null; onDismiss?: () => void; }) { if (!error) return null; return (
}> {getThreadErrorBannerMessage(error)} {error} {onDismiss && ( )}
); });