import { IpcClient } from "@/ipc/ipc_client";
import { AI_STREAMING_ERROR_MESSAGE_PREFIX } from "@/shared/texts";
import {
X,
ExternalLink as ExternalLinkIcon,
CircleArrowUp,
} from "lucide-react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
export function ChatErrorBox({
onDismiss,
error,
isMoreMinimoreProEnabled,
}: {
onDismiss: () => void;
error: string;
isMoreMinimoreProEnabled: boolean;
}) {
if (error.includes("doesn't have a free quota tier")) {
return (
{error}
Access with MoreMinimore Pro
{" "}
or switch to another model.
);
}
// Important, this needs to come after the "free quota tier" check
// because it also includes this URL in the error message
//
// Sometimes MoreMinimore Pro can return rate limit errors and we do not want to
// show the upgrade to MoreMinimore Pro link in that case because they are
// already on the MoreMinimore Pro plan.
if (
!isMoreMinimoreProEnabled &&
(error.includes("Resource has been exhausted") ||
error.includes("https://ai.google.dev/gemini-api/docs/rate-limits") ||
error.includes("Provider returned error"))
) {
return (
{error}
Upgrade to MoreMinimore Pro
Troubleshooting guide
);
}
if (error.includes("LiteLLM Virtual Key expected")) {
return (
Looks like you don't have a valid MoreMinimore Pro key.{" "}
Upgrade to MoreMinimore Pro
{" "}
today.
);
}
if (isMoreMinimoreProEnabled && error.includes("ExceededBudget:")) {
return (
You have used all of your MoreMinimore AI credits this month.{" "}
Reload or upgrade your subscription
{" "}
and get more AI credits
);
}
// This is a very long list of model fallbacks that clutters the error message.
//
// We are matching "Fallbacks=[{" and not just "Fallbacks=" because the fallback
// model itself can error and we want to include the fallback model error in the error message.
// Example: https://github.com/kunthawat/moreminimore-vibe/issues/1849#issuecomment-3590685911
const fallbackPrefix = "Fallbacks=[{";
if (error.includes(fallbackPrefix)) {
error = error.split(fallbackPrefix)[0];
}
return (
{error}
{!isMoreMinimoreProEnabled &&
error.includes(AI_STREAMING_ERROR_MESSAGE_PREFIX) &&
!error.includes("TypeError: terminated") && (
Upgrade to MoreMinimore Pro
)}
Read docs
);
}
function ExternalLink({
href,
children,
variant = "secondary",
icon,
}: {
href: string;
children: React.ReactNode;
variant?: "primary" | "secondary";
icon?: React.ReactNode;
}) {
const baseClasses =
"cursor-pointer inline-flex items-center gap-1.5 rounded-md px-3 py-1.5 text-sm font-medium shadow-sm focus:outline-none focus:ring-2";
const primaryClasses =
"bg-blue-600 text-white hover:bg-blue-700 focus:ring-blue-500";
const secondaryClasses =
"bg-blue-50 text-blue-700 border border-blue-200 hover:bg-blue-100 hover:border-blue-300 focus:ring-blue-200";
const iconElement =
icon ??
(variant === "primary" ? (
) : (
));
return (
IpcClient.getInstance().openExternalUrl(href)}
>
{children}
{iconElement}
);
}
function ChatErrorContainer({
onDismiss,
children,
}: {
onDismiss: () => void;
children: React.ReactNode | string;
}) {
return (
);
}
function ChatInfoContainer({
onDismiss,
children,
}: {
onDismiss: () => void;
children: React.ReactNode;
}) {
return (
);
}