Make provider flow more obvious (#1493)
<!-- This is an auto-generated description by cubic. --> ## Summary by cubic Improved the provider setup flow by adding a clipboard paste-and-save action and a clearer “Get API key” prompt when a provider isn’t configured. - **New Features** - Added a clipboard “Paste and Save” button with tooltip and error handling. - Highlighted the “Get API key” button with a popover prompt and visual emphasis when not configured. - **Refactors** - Changed onSaveKey to accept the key value (string) and updated usage to save the provided value. <!-- End of auto-generated description by cubic. -->
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { Info, KeyRound, Trash2 } from "lucide-react";
|
||||
import { Info, KeyRound, Trash2, Clipboard } from "lucide-react";
|
||||
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";
|
||||
import {
|
||||
Accordion,
|
||||
@@ -11,6 +11,8 @@ import { VertexConfiguration } from "./VertexConfiguration";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { UserSettings } from "@/lib/schemas";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "../ui/tooltip";
|
||||
import { showError } from "@/lib/toast";
|
||||
|
||||
// Helper function to mask ENV API keys (move or duplicate if needed elsewhere)
|
||||
const maskEnvApiKey = (key: string | undefined): string => {
|
||||
@@ -29,7 +31,7 @@ interface ApiKeyConfigurationProps {
|
||||
saveError: string | null;
|
||||
apiKeyInput: string;
|
||||
onApiKeyInputChange: (value: string) => void;
|
||||
onSaveKey: () => Promise<void>;
|
||||
onSaveKey: (value: string) => Promise<void>;
|
||||
onDeleteKey: () => Promise<void>;
|
||||
isDyad: boolean;
|
||||
updateSettings: (settings: Partial<UserSettings>) => Promise<UserSettings>;
|
||||
@@ -144,7 +146,36 @@ export function ApiKeyConfiguration({
|
||||
placeholder={`Enter new ${providerDisplayName} API Key here`}
|
||||
className={`flex-grow ${saveError ? "border-red-500" : ""}`}
|
||||
/>
|
||||
<Button onClick={onSaveKey} disabled={isSaving || !apiKeyInput}>
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button
|
||||
onClick={async () => {
|
||||
try {
|
||||
const text = await navigator.clipboard.readText();
|
||||
if (text) {
|
||||
onSaveKey(text);
|
||||
}
|
||||
} catch (error) {
|
||||
showError("Failed to paste from clipboard");
|
||||
console.error("Failed to paste from clipboard", error);
|
||||
}
|
||||
}}
|
||||
disabled={isSaving}
|
||||
variant="outline"
|
||||
size="icon"
|
||||
title="Paste from clipboard and save"
|
||||
aria-label="Paste from clipboard and save"
|
||||
>
|
||||
<Clipboard className="h-4 w-4" />
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>Paste from clipboard and save</TooltipContent>
|
||||
</Tooltip>
|
||||
|
||||
<Button
|
||||
onClick={() => onSaveKey(apiKeyInput)}
|
||||
disabled={isSaving || !apiKeyInput}
|
||||
>
|
||||
{isSaving ? "Saving..." : "Save Key"}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
import {
|
||||
ArrowLeft,
|
||||
ArrowUp,
|
||||
Circle,
|
||||
ExternalLink,
|
||||
GiftIcon,
|
||||
KeyRound,
|
||||
Settings as SettingsIcon,
|
||||
} from "lucide-react";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { IpcClient } from "@/ipc/ipc_client";
|
||||
import {
|
||||
Popover,
|
||||
PopoverContent,
|
||||
PopoverTrigger,
|
||||
} from "@/components/ui/popover";
|
||||
import {} from "react";
|
||||
|
||||
interface ProviderSettingsHeaderProps {
|
||||
providerDisplayName: string;
|
||||
@@ -51,6 +57,17 @@ export function ProviderSettingsHeader({
|
||||
}
|
||||
};
|
||||
|
||||
const ConfigureButton = (
|
||||
<Button
|
||||
onClick={handleGetApiKeyClick}
|
||||
className="mb-4 cursor-pointer py-5 w-full ring-4 ring-primary/60 shadow-lg shadow-primary/30 border-primary/60"
|
||||
>
|
||||
<KeyRound className="mr-2 h-4 w-4" />
|
||||
{getKeyButtonText({ isConfigured, isDyad })}
|
||||
<ExternalLink className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Button
|
||||
@@ -95,21 +112,24 @@ export function ProviderSettingsHeader({
|
||||
)}
|
||||
</div>
|
||||
|
||||
{providerWebsiteUrl && !isLoading && (
|
||||
<Button
|
||||
onClick={handleGetApiKeyClick}
|
||||
className="mb-4 cursor-pointer py-5 w-full"
|
||||
// variant="primary"
|
||||
>
|
||||
{isConfigured ? (
|
||||
<SettingsIcon className="mr-2 h-4 w-4" />
|
||||
) : (
|
||||
<KeyRound className="mr-2 h-4 w-4" />
|
||||
)}
|
||||
{getKeyButtonText({ isConfigured, isDyad })}
|
||||
<ExternalLink className="ml-2 h-4 w-4" />
|
||||
</Button>
|
||||
)}
|
||||
{providerWebsiteUrl &&
|
||||
!isLoading &&
|
||||
(!isConfigured ? (
|
||||
<Popover defaultOpen>
|
||||
<PopoverTrigger asChild>{ConfigureButton}</PopoverTrigger>
|
||||
<PopoverContent
|
||||
side="bottom"
|
||||
align="center"
|
||||
className="w-fit py-2 px-3 bg-background text-primary shadow-lg ring-1 ring-primary/40"
|
||||
>
|
||||
<div className="text-sm font-semibold flex items-center gap-1">
|
||||
<ArrowUp /> Create your API key with {providerDisplayName}
|
||||
</div>
|
||||
</PopoverContent>
|
||||
</Popover>
|
||||
) : (
|
||||
ConfigureButton
|
||||
))}
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -118,8 +118,8 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
: isValidUserKey || hasEnvKey; // Configured if either is set
|
||||
|
||||
// --- Save Handler ---
|
||||
const handleSaveKey = async () => {
|
||||
if (!apiKeyInput) {
|
||||
const handleSaveKey = async (value: string) => {
|
||||
if (!value.trim()) {
|
||||
setSaveError("API Key cannot be empty.");
|
||||
return;
|
||||
}
|
||||
@@ -132,7 +132,7 @@ export function ProviderSettingsPage({ provider }: ProviderSettingsPageProps) {
|
||||
[provider]: {
|
||||
...settings?.providerSettings?.[provider],
|
||||
apiKey: {
|
||||
value: apiKeyInput,
|
||||
value,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user