messiasads's picture
refactor to local use only
8185bfc
raw
history blame
4.79 kB
import { PiGearSixFill } from "react-icons/pi";
import { useState, useEffect } from "react";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/components/ui/popover";
import { PROVIDERS, MODELS } from "@/lib/providers";
import { Button } from "@/components/ui/button";
import { useMemo } from "react";
import { useUpdateEffect } from "react-use";
import { Input } from "@/components/ui/input";
import { toast } from "sonner";
export function Settings({
open,
onClose,
provider,
model,
error,
onChange,
}: {
open: boolean;
provider: string;
model: string;
error?: string;
isFollowUp?: boolean;
onClose: React.Dispatch<React.SetStateAction<boolean>>;
onChange: (provider: string) => void;
onModelChange: (model: string) => void;
}) {
const [apiKey, setApiKey] = useState("");
const [baseUrl, setBaseUrl] = useState("");
const [customModel, setCustomModel] = useState("");
useEffect(() => {
setApiKey(localStorage.getItem("openai_api_key") || "");
setBaseUrl(localStorage.getItem("openai_base_url") || "");
setCustomModel(localStorage.getItem("openai_model") || "");
}, [open]);
const modelAvailableProviders = useMemo(() => {
const availableProviders = MODELS.find(
(m: { value: string }) => m.value === model
)?.providers;
if (!availableProviders) return Object.keys(PROVIDERS);
return Object.keys(PROVIDERS).filter((id) =>
availableProviders.includes(id)
);
}, [model]);
useUpdateEffect(() => {
if (provider !== "auto" && !modelAvailableProviders.includes(provider)) {
onChange("auto");
}
}, [model, provider]);
const handleSaveSettings = () => {
localStorage.setItem("openai_api_key", apiKey);
localStorage.setItem("openai_base_url", baseUrl);
localStorage.setItem("openai_model", customModel);
toast.success("Settings saved!");
onClose(false);
};
return (
<div className="">
<Popover open={open} onOpenChange={onClose}>
<PopoverTrigger asChild>
<Button variant="black" size="sm">
<PiGearSixFill className="size-4" />
Settings
</Button>
</PopoverTrigger>
<PopoverContent
className="!rounded-2xl p-0 !w-96 overflow-hidden !bg-neutral-900"
align="center"
>
<header className="flex items-center justify-center text-sm px-4 py-3 border-b gap-2 bg-neutral-950 border-neutral-800 font-semibold text-neutral-200">
Customize Settings
</header>
<main className="px-4 pt-5 pb-6 space-y-5">
{error !== "" && (
<p className="text-red-500 text-sm font-medium mb-2 flex items-center justify-between bg-red-500/10 p-2 rounded-md">
{error}
</p>
)}
<label className="block">
<p className="text-neutral-300 text-sm mb-2.5">
API Key
</p>
<Input
type="password"
placeholder="Enter your OpenAI API key"
value={apiKey}
onChange={(e) => setApiKey(e.target.value)}
className="!bg-neutral-800 !border-neutral-700 !text-neutral-200"
/>
</label>
<label className="block">
<p className="text-neutral-300 text-sm mb-2.5">
Base URL (optional)
</p>
<Input
type="text"
placeholder="e.g., https://api.openai.com/v1"
value={baseUrl}
onChange={(e) => setBaseUrl(e.target.value)}
className="!bg-neutral-800 !border-neutral-700 !text-neutral-200"
/>
</label>
<label className="block">
<p className="text-neutral-300 text-sm mb-2.5">
Custom Model
</p>
<Input
type="text"
placeholder="e.g., gpt-4o-mini"
value={customModel || "gpt-4.1"}
onChange={(e) => setCustomModel(e.target.value)}
className="!bg-neutral-800 !border-neutral-700 !text-neutral-200"
/>
</label>
<Button
variant="default"
size="sm"
onClick={handleSaveSettings}
className="mt-2 w-full"
>
Save Settings
</Button>
<div className="bg-amber-500/10 border-amber-500/10 p-3 text-xs text-amber-500 border rounded-lg">
Accepts any OpenAI-compatible provider. Enter the corresponding API key and base URL (e.g., OpenRouter, DeepSeek, etc.).
</div>
</main>
</PopoverContent>
</Popover>
</div>
);
}