77 lines
2.6 KiB
TypeScript
77 lines
2.6 KiB
TypeScript
|
|
"use client";
|
|||
|
|
|
|||
|
|
import { ExternalLink, KeyRound } from "lucide-react";
|
|||
|
|
import { Badge } from "@/components/ui/badge";
|
|||
|
|
import { Input } from "@/components/ui/input";
|
|||
|
|
import { Label } from "@/components/ui/label";
|
|||
|
|
import { PROVIDER_KEY_LABELS, type ProviderId } from "@/lib/ai/keys";
|
|||
|
|
|
|||
|
|
const PROVIDER_ORDER: ProviderId[] = [
|
|||
|
|
"opencode-go",
|
|||
|
|
"xai",
|
|||
|
|
"openai",
|
|||
|
|
"anthropic",
|
|||
|
|
"google",
|
|||
|
|
];
|
|||
|
|
|
|||
|
|
interface ApiKeysFormProps {
|
|||
|
|
values: Partial<Record<ProviderId, string>>;
|
|||
|
|
configured: Partial<Record<ProviderId, boolean>>;
|
|||
|
|
onChange: (provider: ProviderId, value: string) => void;
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
export function ApiKeysForm({ values, configured, onChange }: ApiKeysFormProps) {
|
|||
|
|
return (
|
|||
|
|
<div className="space-y-4">
|
|||
|
|
{PROVIDER_ORDER.map((provider) => {
|
|||
|
|
const meta = PROVIDER_KEY_LABELS[provider];
|
|||
|
|
const isConfigured = configured[provider];
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div
|
|||
|
|
key={provider}
|
|||
|
|
className="rounded-lg border border-border bg-muted p-4"
|
|||
|
|
>
|
|||
|
|
<div className="mb-3 flex items-center justify-between gap-3">
|
|||
|
|
<div className="flex items-center gap-2">
|
|||
|
|
<KeyRound className="h-4 w-4 text-muted-foreground" />
|
|||
|
|
<Label className="normal-case text-sm font-semibold text-foreground">
|
|||
|
|
{meta.label}
|
|||
|
|
</Label>
|
|||
|
|
</div>
|
|||
|
|
<Badge variant={isConfigured ? "success" : "secondary"}>
|
|||
|
|
{isConfigured ? "已設定" : "未設定"}
|
|||
|
|
</Badge>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
<p className="mb-3 text-xs leading-relaxed text-muted-foreground">{meta.hint}</p>
|
|||
|
|
|
|||
|
|
<Input
|
|||
|
|
type="password"
|
|||
|
|
value={values[provider] ?? ""}
|
|||
|
|
onChange={(e) => onChange(provider, e.target.value)}
|
|||
|
|
placeholder={isConfigured ? "留空則保留現有 key" : "貼上 API key"}
|
|||
|
|
autoComplete="off"
|
|||
|
|
/>
|
|||
|
|
|
|||
|
|
{meta.docsUrl && (
|
|||
|
|
<a
|
|||
|
|
href={meta.docsUrl}
|
|||
|
|
target="_blank"
|
|||
|
|
rel="noopener noreferrer"
|
|||
|
|
className="mt-2 inline-flex items-center gap-1 text-xs text-muted-foreground hover:text-foreground hover:underline"
|
|||
|
|
>
|
|||
|
|
<ExternalLink className="h-3 w-3" />
|
|||
|
|
取得 {meta.label} API key
|
|||
|
|
</a>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
})}
|
|||
|
|
|
|||
|
|
<p className="text-xs leading-relaxed text-muted-foreground">
|
|||
|
|
AI Key 綁定你登入巡樓的帳號,所有 Threads 經營帳號共用。只存在本機資料庫;也可在 `.env` 設定環境變數作為備援。
|
|||
|
|
</p>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|