Add OpenRouter to setup banner (#1242)
<!-- This is an auto-generated description by cubic. --> ## Summary by cubic Added OpenRouter as a first-class option in the setup banner and introduced a reusable provider card component. This streamlines provider selection and adds E2E coverage for the setup flow. - **New Features** - Added SetupProviderCard and used it for Google and OpenRouter in SetupBanner. - Clicking Google or OpenRouter routes to the correct provider settings and logs PostHog events. - Kept “Other providers” link to Settings. - Added setup.spec.ts E2E test to verify Google, OpenRouter, and Other navigation; introduced test config flag showSetupScreen to control the OPENAI_API_KEY shortcut. <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -11,6 +11,7 @@ import {
|
||||
} from "lucide-react";
|
||||
import { providerSettingsRoute } from "@/routes/settings/providers/$provider";
|
||||
import { settingsRoute } from "@/routes/settings";
|
||||
import SetupProviderCard from "@/components/SetupProviderCard";
|
||||
|
||||
import { useState, useEffect, useCallback } from "react";
|
||||
import { IpcClient } from "@/ipc/ipc_client";
|
||||
@@ -58,7 +59,7 @@ export function SetupBanner() {
|
||||
checkNode();
|
||||
}, [checkNode]);
|
||||
|
||||
const handleAiSetupClick = () => {
|
||||
const handleGoogleSetupClick = () => {
|
||||
posthog.capture("setup-flow:ai-provider-setup:google:click");
|
||||
navigate({
|
||||
to: providerSettingsRoute.id,
|
||||
@@ -66,6 +67,14 @@ export function SetupBanner() {
|
||||
});
|
||||
};
|
||||
|
||||
const handleOpenRouterSetupClick = () => {
|
||||
posthog.capture("setup-flow:ai-provider-setup:openrouter:click");
|
||||
navigate({
|
||||
to: providerSettingsRoute.id,
|
||||
params: { provider: "openrouter" },
|
||||
});
|
||||
};
|
||||
|
||||
const handleOtherProvidersClick = () => {
|
||||
posthog.capture("setup-flow:ai-provider-setup:other:click");
|
||||
navigate({
|
||||
@@ -226,30 +235,38 @@ export function SetupBanner() {
|
||||
<p className="text-sm mb-3">
|
||||
Connect your preferred AI provider to start generating code.
|
||||
</p>
|
||||
<div
|
||||
className="p-3 bg-blue-50 dark:bg-blue-900/50 border border-blue-200 dark:border-blue-700 rounded-lg cursor-pointer hover:bg-blue-100 dark:hover:bg-blue-900/70 transition-colors"
|
||||
onClick={handleAiSetupClick}
|
||||
role="button"
|
||||
<SetupProviderCard
|
||||
variant="google"
|
||||
onClick={handleGoogleSetupClick}
|
||||
tabIndex={isNodeSetupComplete ? 0 : -1}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="bg-blue-100 dark:bg-blue-800 p-1.5 rounded-full">
|
||||
<Sparkles className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
<div>
|
||||
<h4 className="font-medium text-sm text-blue-800 dark:text-blue-300">
|
||||
Setup Google Gemini API Key
|
||||
</h4>
|
||||
<p className="text-xs text-blue-600 dark:text-blue-400 flex items-center gap-1">
|
||||
<GiftIcon className="w-3 h-3" />
|
||||
Use Google Gemini for free
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRight className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||
</div>
|
||||
</div>
|
||||
leadingIcon={
|
||||
<Sparkles className="w-4 h-4 text-blue-600 dark:text-blue-400" />
|
||||
}
|
||||
title="Setup Google Gemini API Key"
|
||||
subtitle={
|
||||
<>
|
||||
<GiftIcon className="w-3 h-3" />
|
||||
Use Google Gemini for free
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<SetupProviderCard
|
||||
className="mt-2"
|
||||
variant="openrouter"
|
||||
onClick={handleOpenRouterSetupClick}
|
||||
tabIndex={isNodeSetupComplete ? 0 : -1}
|
||||
leadingIcon={
|
||||
<Sparkles className="w-4 h-4 text-purple-600 dark:text-purple-400" />
|
||||
}
|
||||
title="Setup OpenRouter API Key"
|
||||
subtitle={
|
||||
<>
|
||||
<GiftIcon className="w-3 h-3" />
|
||||
Free models available
|
||||
</>
|
||||
}
|
||||
/>
|
||||
|
||||
<div
|
||||
className="mt-2 p-3 bg-gray-50 dark:bg-gray-800/50 border border-gray-200 dark:border-gray-700 rounded-lg cursor-pointer hover:bg-gray-100 dark:hover:bg-gray-800/70 transition-colors"
|
||||
|
||||
87
src/components/SetupProviderCard.tsx
Normal file
87
src/components/SetupProviderCard.tsx
Normal file
@@ -0,0 +1,87 @@
|
||||
import { ChevronRight } from "lucide-react";
|
||||
import { cn } from "@/lib/utils";
|
||||
import { ReactNode } from "react";
|
||||
|
||||
type SetupProviderVariant = "google" | "openrouter";
|
||||
|
||||
export function SetupProviderCard({
|
||||
variant,
|
||||
title,
|
||||
subtitle,
|
||||
leadingIcon,
|
||||
onClick,
|
||||
tabIndex = 0,
|
||||
className,
|
||||
}: {
|
||||
variant: SetupProviderVariant;
|
||||
title: string;
|
||||
subtitle?: ReactNode;
|
||||
leadingIcon: ReactNode;
|
||||
onClick: () => void;
|
||||
tabIndex?: number;
|
||||
className?: string;
|
||||
}) {
|
||||
const styles = getVariantStyles(variant);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"p-3 border rounded-lg cursor-pointer transition-colors",
|
||||
styles.container,
|
||||
className,
|
||||
)}
|
||||
onClick={onClick}
|
||||
role="button"
|
||||
tabIndex={tabIndex}
|
||||
>
|
||||
<div className="flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-2">
|
||||
<div className={cn("p-1.5 rounded-full", styles.iconWrapper)}>
|
||||
{leadingIcon}
|
||||
</div>
|
||||
<div>
|
||||
<h4 className={cn("font-medium text-sm", styles.titleColor)}>
|
||||
{title}
|
||||
</h4>
|
||||
{subtitle ? (
|
||||
<div
|
||||
className={cn(
|
||||
"text-xs flex items-center gap-1",
|
||||
styles.subtitleColor,
|
||||
)}
|
||||
>
|
||||
{subtitle}
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</div>
|
||||
<ChevronRight className={cn("w-4 h-4", styles.chevronColor)} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
function getVariantStyles(variant: SetupProviderVariant) {
|
||||
switch (variant) {
|
||||
case "google":
|
||||
return {
|
||||
container:
|
||||
"bg-blue-50 dark:bg-blue-900/50 border-blue-200 dark:border-blue-700 hover:bg-blue-100 dark:hover:bg-blue-900/70",
|
||||
iconWrapper: "bg-blue-100 dark:bg-blue-800",
|
||||
titleColor: "text-blue-800 dark:text-blue-300",
|
||||
subtitleColor: "text-blue-600 dark:text-blue-400",
|
||||
chevronColor: "text-blue-600 dark:text-blue-400",
|
||||
} as const;
|
||||
case "openrouter":
|
||||
return {
|
||||
container:
|
||||
"bg-purple-50 dark:bg-purple-900/50 border-purple-200 dark:border-purple-700 hover:bg-purple-100 dark:hover:bg-purple-900/70",
|
||||
iconWrapper: "bg-purple-100 dark:bg-purple-800",
|
||||
titleColor: "text-purple-800 dark:text-purple-300",
|
||||
subtitleColor: "text-purple-600 dark:text-purple-400",
|
||||
chevronColor: "text-purple-600 dark:text-purple-400",
|
||||
} as const;
|
||||
}
|
||||
}
|
||||
|
||||
export default SetupProviderCard;
|
||||
Reference in New Issue
Block a user