diff --git a/src/components/ModelPicker.tsx b/src/components/ModelPicker.tsx index 845c883..728ff13 100644 --- a/src/components/ModelPicker.tsx +++ b/src/components/ModelPicker.tsx @@ -24,6 +24,7 @@ import { useLanguageModelsByProviders } from "@/hooks/useLanguageModelsByProvide import { LocalModel } from "@/ipc/ipc_types"; import { useLanguageModelProviders } from "@/hooks/useLanguageModelProviders"; import { useSettings } from "@/hooks/useSettings"; +import { PriceBadge } from "@/components/PriceBadge"; export function ModelPicker() { const { settings, updateSettings } = useSettings(); @@ -106,7 +107,16 @@ export function ModelPicker() { // Get auto provider models (if any) const autoModels = !loading && modelsByProviders && modelsByProviders["auto"] - ? modelsByProviders["auto"] + ? modelsByProviders["auto"].filter((model) => { + if ( + settings && + isDyadProEnabled(settings) && + model.apiName === "free" + ) { + return false; + } + return true; + }) : []; // Determine availability of local models @@ -251,6 +261,18 @@ export function ModelPicker() { {/* Primary providers as submenus */} {primaryProviders.map(([providerId, models]) => { + models = models.filter((model) => { + // Don't show free models if Dyad Pro is enabled because + // we will use the paid models (in Dyad Pro backend) which + // don't have the free limitations. + if ( + isDyadProEnabled(settings) && + model.apiName.endsWith(":free") + ) { + return false; + } + return true; + }); const provider = providers?.find((p) => p.id === providerId); return ( @@ -304,11 +326,7 @@ export function ModelPicker() { >
{model.displayName} - {model.dollarSigns && ( - - {"$".repeat(model.dollarSigns)} - - )} + {model.tag && ( {model.tag} diff --git a/src/components/PriceBadge.tsx b/src/components/PriceBadge.tsx new file mode 100644 index 0000000..522ac80 --- /dev/null +++ b/src/components/PriceBadge.tsx @@ -0,0 +1,20 @@ +import React from "react"; + +export function PriceBadge({ + dollarSigns, +}: { + dollarSigns: number | undefined; +}) { + if (dollarSigns === undefined || dollarSigns === null) return null; + + const label = dollarSigns === 0 ? "Free" : "$".repeat(dollarSigns); + + const className = + dollarSigns === 0 + ? "text-[10px] text-primary border border-primary px-1.5 py-0.5 rounded-full font-medium" + : "text-[10px] bg-primary/10 text-primary px-1.5 py-0.5 rounded-full font-medium"; + + return {label}; +} + +export default PriceBadge; diff --git a/src/components/SetupBanner.tsx b/src/components/SetupBanner.tsx index f9dd64e..fdbec81 100644 --- a/src/components/SetupBanner.tsx +++ b/src/components/SetupBanner.tsx @@ -133,9 +133,7 @@ export function SetupBanner() { return ( <> -

- Follow these steps and you'll be ready to start building with Dyad... -

+

Setup Dyad

{ + const posthog = usePostHog(); + const navigate = useNavigate(); + return ( + { + posthog.capture("setup-flow:ai-provider-setup:openrouter:click"); + navigate({ + to: providerSettingsRoute.id, + params: { provider: "openrouter" }, + }); + }} + tabIndex={0} + leadingIcon={ + + } + title="Setup OpenRouter API Key" + subtitle={ + <> + + Free models available + + } + /> + ); +}; diff --git a/src/components/chat/MessagesList.tsx b/src/components/chat/MessagesList.tsx index e77cc6e..4f5a9ca 100644 --- a/src/components/chat/MessagesList.tsx +++ b/src/components/chat/MessagesList.tsx @@ -2,7 +2,7 @@ import type React from "react"; import type { Message } from "@/ipc/ipc_types"; import { forwardRef, useState } from "react"; import ChatMessage from "./ChatMessage"; -import { SetupBanner } from "../SetupBanner"; +import { OpenRouterSetupBanner, SetupBanner } from "../SetupBanner"; import { useStreamChat } from "@/hooks/useStreamChat"; import { selectedChatIdAtom } from "@/atoms/chatAtoms"; @@ -29,7 +29,7 @@ export const MessagesList = forwardRef( const appId = useAtomValue(selectedAppIdAtom); const { versions, revertVersion } = useVersions(appId); const { streamMessage, isStreaming } = useStreamChat(); - const { isAnyProviderSetup } = useLanguageModelProviders(); + const { isAnyProviderSetup, isProviderSetup } = useLanguageModelProviders(); const { settings } = useSettings(); const setMessages = useSetAtom(chatMessagesAtom); const [isUndoLoading, setIsUndoLoading] = useState(false); @@ -37,28 +37,42 @@ export const MessagesList = forwardRef( const selectedChatId = useAtomValue(selectedChatIdAtom); const { userBudget } = useUserBudgetInfo(); + const renderSetupBanner = () => { + const selectedModel = settings?.selectedModel; + if ( + selectedModel?.name === "free" && + selectedModel?.provider === "auto" && + !isProviderSetup("openrouter") + ) { + return ; + } + if (!isAnyProviderSetup()) { + return ; + } + return null; + }; + return (
- {messages.length > 0 ? ( - messages.map((message, index) => ( - - )) - ) : ( -
-
- No messages yet -
- {!isAnyProviderSetup() && } -
- )} + {messages.length > 0 + ? messages.map((message, index) => ( + + )) + : !renderSetupBanner() && ( +
+
+ No messages yet +
+
+ )} {!isStreaming && (
{!!messages.length && @@ -230,6 +244,7 @@ export const MessagesList = forwardRef( /> )}
+ {renderSetupBanner()}
); }, diff --git a/src/components/settings/ProviderSettingsHeader.tsx b/src/components/settings/ProviderSettingsHeader.tsx index 9b90e7d..e463f7e 100644 --- a/src/components/settings/ProviderSettingsHeader.tsx +++ b/src/components/settings/ProviderSettingsHeader.tsx @@ -98,8 +98,8 @@ export function ProviderSettingsHeader({ {providerWebsiteUrl && !isLoading && (