diff --git a/src/components/chat/ChatErrorBox.tsx b/src/components/chat/ChatErrorBox.tsx
new file mode 100644
index 0000000..88cde88
--- /dev/null
+++ b/src/components/chat/ChatErrorBox.tsx
@@ -0,0 +1,128 @@
+import { IpcClient } from "@/ipc/ipc_client";
+import { X } from "lucide-react";
+export function ChatErrorBox({
+ onDismiss,
+ error,
+ isDyadProEnabled,
+}: {
+ onDismiss: () => void;
+ error: string;
+ isDyadProEnabled: boolean;
+}) {
+ if (error.includes("doesn't have a free quota tier")) {
+ return (
+
+ {error}
+
+
+ Access with Dyad Pro.
+
+
+
+ );
+ }
+
+ // Important, this needs to come after the "free quota tier" check
+ // because it also includes this URL in the error message
+ if (error.includes("https://ai.google.dev/gemini-api/docs/rate-limits")) {
+ return (
+
+ {error}
+
+
+ Upgrade to Dyad Pro.
+
+
+
+ );
+ }
+
+ if (error.includes("LiteLLM Virtual Key expected")) {
+ return (
+
+
+ Looks like you don't have a valid Dyad Pro key.{" "}
+
+ Upgrade to Dyad Pro
+ {" "}
+ today.
+
+
+ );
+ }
+ if (isDyadProEnabled && error.includes("ExceededBudget:")) {
+ return (
+
+
+ You have used all of your Dyad AI credits this month.{" "}
+
+ Upgrade to Dyad Max
+ {" "}
+ and get more AI credits
+
+
+ );
+ }
+ return {error};
+}
+
+function ExternalLink({
+ href,
+ children,
+}: {
+ href: string;
+ children: React.ReactNode;
+}) {
+ return (
+ IpcClient.getInstance().openExternalUrl(href)}
+ >
+ {children}
+
+ );
+}
+
+function ChatErrorContainer({
+ onDismiss,
+ children,
+}: {
+ onDismiss: () => void;
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
+
+function ChatInfoContainer({
+ onDismiss,
+ children,
+}: {
+ onDismiss: () => void;
+ children: React.ReactNode;
+}) {
+ return (
+
+ );
+}
diff --git a/src/components/chat/ChatInput.tsx b/src/components/chat/ChatInput.tsx
index 0a552b8..de8f5b2 100644
--- a/src/components/chat/ChatInput.tsx
+++ b/src/components/chat/ChatInput.tsx
@@ -60,6 +60,7 @@ import { AttachmentsList } from "./AttachmentsList";
import { DragDropOverlay } from "./DragDropOverlay";
import { showError, showExtraFilesToast } from "@/lib/toast";
import { ChatInputControls } from "../ChatInputControls";
+import { ChatErrorBox } from "./ChatErrorBox";
const showTokenBarAtom = atom(false);
export function ChatInput({ chatId }: { chatId?: number }) {
@@ -235,17 +236,11 @@ export function ChatInput({ chatId }: { chatId?: number }) {
return (
<>
{error && showError && (
-
+
)}
{/* Display loading or error state for proposal */}
{isProposalLoading && (