From 7acbe73c7397f934fefa48b966cee253d01da928 Mon Sep 17 00:00:00 2001 From: Will Chen Date: Mon, 13 Oct 2025 17:05:04 -0700 Subject: [PATCH] Improve links/buttons for AI errors (#1524) drive-by: update links for pro banners --- > [!NOTE] > Revamps ChatErrorBox with styled CTA buttons, UTM-tracked links, broader error matching, and adds default help CTAs; updates Pro banners to point to /pro URLs. > > - **Chat**: > - **Error handling/UI**: Expands rate-limit matching to include `"Provider returned error"` and appends default CTAs on generic errors. > - **CTAs**: Replaces underlined links with styled buttons via `ExternalLink` (supports `primary|secondary` variants and icons), and adds upgrade/docs buttons across error states. > - **Links**: Adds UTM parameters to Pro/Docs links; updates exceeded budget CTA to `"Reload or upgrade your subscription"` with new subscription URL. > - **Pro Banners**: > - Adjusts banner click-through URLs in `AiAccessBanner`, `SmartContextBanner`, and `TurboBanner` to `https://www.dyad.sh/pro?...` (removes `#ai`). > > Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 2111954ac9a98dd66f5b3b9e628270ac19713a2d. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot). --- src/components/ProBanner.tsx | 6 +- src/components/chat/ChatErrorBox.tsx | 84 ++++++++++++++++++++++------ 2 files changed, 71 insertions(+), 19 deletions(-) diff --git a/src/components/ProBanner.tsx b/src/components/ProBanner.tsx index 056b3dd..de5c602 100644 --- a/src/components/ProBanner.tsx +++ b/src/components/ProBanner.tsx @@ -85,7 +85,7 @@ export function AiAccessBanner() { className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-white via-indigo-50 to-sky-100 dark:from-indigo-700 dark:via-indigo-700 dark:to-indigo-900 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-black/5 dark:ring-white/10 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]" onClick={() => { IpcClient.getInstance().openExternalUrl( - "https://www.dyad.sh/pro#ai?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-ai-access", + "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-ai-access", ); }} > @@ -151,7 +151,7 @@ export function SmartContextBanner() { className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-emerald-50 via-emerald-100 to-emerald-200 dark:from-emerald-700 dark:via-emerald-700 dark:to-emerald-900 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-emerald-900/10 dark:ring-white/10 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]" onClick={() => { IpcClient.getInstance().openExternalUrl( - "https://www.dyad.sh/pro#ai?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-smart-context", + "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-smart-context", ); }} > @@ -192,7 +192,7 @@ export function TurboBanner() { className="w-full py-2 sm:py-2.5 md:py-3 rounded-lg bg-gradient-to-br from-rose-50 via-rose-100 to-rose-200 dark:from-rose-800 dark:via-fuchsia-800 dark:to-rose-800 flex items-center justify-center relative overflow-hidden ring-1 ring-inset ring-rose-900/10 dark:ring-white/5 shadow-sm cursor-pointer transition-all duration-200 hover:shadow-md hover:-translate-y-[1px]" onClick={() => { IpcClient.getInstance().openExternalUrl( - "https://www.dyad.sh/pro#ai?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-turbo", + "https://www.dyad.sh/pro?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-turbo", ); }} > diff --git a/src/components/chat/ChatErrorBox.tsx b/src/components/chat/ChatErrorBox.tsx index 22059aa..0ffd43e 100644 --- a/src/components/chat/ChatErrorBox.tsx +++ b/src/components/chat/ChatErrorBox.tsx @@ -1,5 +1,9 @@ import { IpcClient } from "@/ipc/ipc_client"; -import { X } from "lucide-react"; +import { + X, + ExternalLink as ExternalLinkIcon, + CircleArrowUp, +} from "lucide-react"; import ReactMarkdown from "react-markdown"; import remarkGfm from "remark-gfm"; @@ -17,7 +21,10 @@ export function ChatErrorBox({ {error} - + Access with Dyad Pro {" "} @@ -30,22 +37,24 @@ export function ChatErrorBox({ // because it also includes this URL in the error message if ( error.includes("Resource has been exhausted") || - error.includes("https://ai.google.dev/gemini-api/docs/rate-limits") + error.includes("https://ai.google.dev/gemini-api/docs/rate-limits") || + error.includes("Provider returned error") ) { return ( {error} - - +
+ Upgrade to Dyad Pro - {" "} - or read the - + - Rate limit troubleshooting guide. + Troubleshooting guide - +
); } @@ -55,7 +64,10 @@ export function ChatErrorBox({ Looks like you don't have a valid Dyad Pro key.{" "} - + Upgrade to Dyad Pro {" "} today. @@ -68,8 +80,11 @@ export function ChatErrorBox({ You have used all of your Dyad AI credits this month.{" "} - - Upgrade to Dyad Max + + Reload or upgrade your subscription {" "} and get more AI credits @@ -80,22 +95,59 @@ export function ChatErrorBox({ if (error.includes("Fallbacks=")) { error = error.split("Fallbacks=")[0]; } - return {error}; + return ( + + {error} +
+ + Upgrade to Dyad 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} + {children} + {iconElement} ); }