File size: 4,771 Bytes
13ae717 8185bfc 13ae717 8185bfc 13ae717 8185bfc 13ae717 8185bfc 13ae717 8185bfc 13ae717 8185bfc d62d36c 8185bfc 13ae717 8185bfc aaa67c2 8185bfc d62d36c 8185bfc d62d36c 8185bfc 13ae717 8185bfc 13ae717 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 |
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 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
</p>
<Input
type="text"
placeholder="e.g., http://127.0.0.1:11434/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., gemma3:1b"
value={customModel || "gemma3:1b"}
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>
);
}
|