From 618fed8523194fab25ac8eb73a1348f8f987c633 Mon Sep 17 00:00:00 2001 From: Will Chen Date: Wed, 21 May 2025 15:23:51 -0700 Subject: [PATCH] Show promo messages for relevant AI errors (#216) --- src/components/chat/ChatErrorBox.tsx | 128 +++++++++++++++++++++++++++ src/components/chat/ChatInput.tsx | 17 ++-- 2 files changed, 134 insertions(+), 11 deletions(-) create mode 100644 src/components/chat/ChatErrorBox.tsx 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 ( +
+ +
+
{children}
+
+
+ ); +} + +function ChatInfoContainer({ + onDismiss, + children, +}: { + onDismiss: () => void; + children: React.ReactNode; +}) { + return ( +
+ +
+
{children}
+
+
+ ); +} 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 && ( -
- -
-
{error}
-
-
+ )} {/* Display loading or error state for proposal */} {isProposalLoading && (