Files
moreminimore-vibe/src/components/ProBanner.tsx
Will Chen 7acbe73c73 Improve links/buttons for AI errors (#1524)
drive-by: update links for pro banners

<!-- CURSOR_SUMMARY -->
---

> [!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`).
> 
> <sup>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).</sup>
<!-- /CURSOR_SUMMARY -->
2025-10-13 17:05:04 -07:00

229 lines
8.9 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// @ts-ignore
import openAiLogo from "../../assets/ai-logos/openai-logo.svg";
// @ts-ignore
import googleLogo from "../../assets/ai-logos/google-logo.svg";
// @ts-ignore
import anthropicLogo from "../../assets/ai-logos/anthropic-logo.svg";
import { IpcClient } from "@/ipc/ipc_client";
import { useState } from "react";
import { KeyRound } from "lucide-react";
import { useSettings } from "@/hooks/useSettings";
import { useUserBudgetInfo } from "@/hooks/useUserBudgetInfo";
import { Button } from "./ui/button";
export function ProBanner() {
const { settings } = useSettings();
const { userBudget } = useUserBudgetInfo();
const [selectedBanner] = useState<"ai" | "smart" | "turbo">(() => {
const options = ["ai", "smart", "turbo"] as const;
return options[Math.floor(Math.random() * options.length)];
});
if (settings?.enableDyadPro || userBudget) {
return (
<div className="mt-6 max-w-2xl mx-auto">
<ManageDyadProButton />
</div>
);
}
return (
<div className="mt-6 max-w-2xl mx-auto">
{selectedBanner === "ai" ? (
<AiAccessBanner />
) : selectedBanner === "smart" ? (
<SmartContextBanner />
) : (
<TurboBanner />
)}
<SetupDyadProButton />
</div>
);
}
export function ManageDyadProButton() {
return (
<Button
variant="outline"
size="lg"
className="w-full mt-4 bg-(--background-lighter) text-primary"
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://academy.dyad.sh/subscription",
);
}}
>
<KeyRound aria-hidden="true" />
Manage Dyad Pro subscription
</Button>
);
}
export function SetupDyadProButton() {
return (
<Button
variant="outline"
size="lg"
className="w-full mt-4 bg-(--background-lighter) text-primary"
onClick={() => {
IpcClient.getInstance().openExternalUrl(
"https://academy.dyad.sh/settings",
);
}}
>
<KeyRound aria-hidden="true" />
Already have Dyad Pro? Add your key
</Button>
);
}
export function AiAccessBanner() {
return (
<div
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?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-ai-access",
);
}}
>
<div
className="absolute inset-0 z-0 bg-gradient-to-tr from-white/60 via-transparent to-transparent pointer-events-none dark:from-white/10"
aria-hidden="true"
/>
<div className="absolute inset-0 z-0 pointer-events-none dark:hidden">
<div className="absolute -top-8 -left-6 h-40 w-40 rounded-full blur-2xl bg-violet-200/40" />
<div className="absolute -bottom-10 -right-6 h-48 w-48 rounded-full blur-3xl bg-sky-200/40" />
</div>
<div className="relative z-10 text-center flex flex-col items-center gap-0.5 sm:gap-1 md:gap-1.5 px-4 md:px-6 pr-6 md:pr-8">
<div className="mt-0.5 sm:mt-1 flex items-center gap-2 sm:gap-3 justify-center">
<div className="text-xl font-semibold tracking-tight text-indigo-900 dark:text-indigo-100">
Access leading AI models with one plan
</div>
<button
type="button"
aria-label="Subscribe to Dyad Pro"
className="inline-flex items-center rounded-md bg-white/90 text-indigo-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
>
Get Dyad Pro
</button>
</div>
<div className="mt-1.5 sm:mt-2 grid grid-cols-3 gap-6 md:gap-8 items-center justify-items-center opacity-90">
<div className="flex items-center justify-center">
<img
src={openAiLogo}
alt="OpenAI"
width={96}
height={28}
className="h-4 md:h-5 w-auto dark:invert"
/>
</div>
<div className="flex items-center justify-center">
<img
src={googleLogo}
alt="Google"
width={110}
height={30}
className="h-4 md:h-5 w-auto"
/>
</div>
<div className="flex items-center justify-center">
<img
src={anthropicLogo}
alt="Anthropic"
width={110}
height={30}
className="h-3 w-auto dark:invert"
/>
</div>
</div>
</div>
</div>
);
}
export function SmartContextBanner() {
return (
<div
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?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-smart-context",
);
}}
>
<div
className="absolute inset-0 z-0 bg-gradient-to-tr from-white/60 via-transparent to-transparent pointer-events-none dark:from-white/10"
aria-hidden="true"
/>
<div className="absolute inset-0 z-0 pointer-events-none dark:hidden">
<div className="absolute -top-10 -left-8 h-44 w-44 rounded-full blur-2xl bg-emerald-200/50" />
<div className="absolute -bottom-12 -right-8 h-56 w-56 rounded-full blur-3xl bg-teal-200/50" />
</div>
<div className="relative z-10 px-4 md:px-6 pr-6 md:pr-8">
<div className="mt-0.5 sm:mt-1 flex items-center gap-2 sm:gap-3 justify-center">
<div className="flex flex-col items-center text-center">
<div className="text-xl font-semibold tracking-tight text-emerald-900 dark:text-emerald-100">
Up to 5x cheaper
</div>
<div className="text-sm sm:text-base mt-1 text-emerald-700 dark:text-emerald-200/80">
by using Smart Context
</div>
</div>
<button
type="button"
aria-label="Get Dyad Pro"
className="inline-flex items-center rounded-md bg-white/90 text-emerald-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
>
Get Dyad Pro
</button>
</div>
</div>
</div>
);
}
export function TurboBanner() {
return (
<div
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?utm_source=dyad-app&utm_medium=app&utm_campaign=in-app-banner-turbo",
);
}}
>
<div
className="absolute inset-0 z-0 bg-gradient-to-tr from-white/60 via-transparent to-transparent pointer-events-none dark:from-white/10"
aria-hidden="true"
/>
<div className="absolute inset-0 z-0 pointer-events-none dark:hidden">
<div className="absolute -top-10 -left-8 h-44 w-44 rounded-full blur-2xl bg-rose-200/50" />
<div className="absolute -bottom-12 -right-8 h-56 w-56 rounded-full blur-3xl bg-fuchsia-200/50" />
</div>
<div className="relative z-10 px-4 md:px-6 pr-6 md:pr-8">
<div className="mt-0.5 sm:mt-1 flex items-center gap-2 sm:gap-3 justify-center">
<div className="flex flex-col items-center text-center">
<div className="text-xl font-semibold tracking-tight text-rose-900 dark:text-rose-100">
Generate code 410x faster
</div>
<div className="text-sm sm:text-base mt-1 text-rose-700 dark:text-rose-200/80">
with Turbo Models & Turbo Edits
</div>
</div>
<button
type="button"
aria-label="Get Dyad Pro"
className="inline-flex items-center rounded-md bg-white/90 text-rose-800 hover:bg-white shadow px-3 py-1.5 text-xs sm:text-sm font-semibold focus:outline-none focus:ring-2 focus:ring-white/50"
>
Get Dyad Pro
</button>
</div>
</div>
</div>
);
}