Amiel's picture
Upload folder using huggingface_hub
676fc08 verified
"use client";
import {
useLayoutEffect,
useState,
useCallback,
useMemo,
type ReactNode,
} from "react";
import { useTranslation } from "react-i18next";
import { usePWAInstall } from "react-use-pwa-install";
import { RefreshCw, CircleHelp, MonitorDown } from "lucide-react";
import { zodResolver } from "@hookform/resolvers/zod";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { toast } from "sonner";
import { Password } from "@/components/Internal/PasswordInput";
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogFooter,
DialogTitle,
} from "@/components/ui/dialog";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
} from "@/components/ui/form";
import { Input } from "@/components/ui/input";
import {
Select,
SelectContent,
SelectGroup,
SelectItem,
SelectTrigger,
SelectLabel,
SelectValue,
} from "@/components/ui/select";
import { Button } from "@/components/ui/button";
import { Label } from "@/components/ui/label";
import { Slider } from "@/components/ui/slider";
import {
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
} from "@/components/ui/tooltip";
import useModel from "@/hooks/useModelList";
import { useSettingStore } from "@/store/setting";
import {
GEMINI_BASE_URL,
OPENROUTER_BASE_URL,
OPENAI_BASE_URL,
ANTHROPIC_BASE_URL,
DEEPSEEK_BASE_URL,
XAI_BASE_URL,
MISTRAL_BASE_URL,
POLLINATIONS_BASE_URL,
OLLAMA_BASE_URL,
TAVILY_BASE_URL,
FIRECRAWL_BASE_URL,
EXA_BASE_URL,
BOCHA_BASE_URL,
SEARXNG_BASE_URL,
} from "@/constants/urls";
import locales from "@/constants/locales";
import {
filterThinkingModelList,
filterNetworkingModelList,
filterOpenRouterModelList,
filterDeepSeekModelList,
filterOpenAIModelList,
filterMistralModelList,
filterPollinationsModelList,
getCustomModelList,
} from "@/utils/model";
import { researchStore } from "@/utils/storage";
import { cn } from "@/utils/style";
import { omit, capitalize } from "radash";
type SettingProps = {
open: boolean;
onClose: () => void;
};
const BUILD_MODE = process.env.NEXT_PUBLIC_BUILD_MODE;
const VERSION = process.env.NEXT_PUBLIC_VERSION;
const DISABLED_AI_PROVIDER = process.env.NEXT_PUBLIC_DISABLED_AI_PROVIDER || "";
const DISABLED_SEARCH_PROVIDER =
process.env.NEXT_PUBLIC_DISABLED_SEARCH_PROVIDER || "";
const MODEL_LIST = process.env.NEXT_PUBLIC_MODEL_LIST || "";
const formSchema = z.object({
provider: z.string(),
mode: z.string().optional(),
apiKey: z.string().optional(),
apiProxy: z.string().optional(),
thinkingModel: z.string().optional(),
networkingModel: z.string().optional(),
openRouterApiKey: z.string().optional(),
openRouterApiProxy: z.string().optional(),
openRouterThinkingModel: z.string().optional(),
openRouterNetworkingModel: z.string().optional(),
openAIApiKey: z.string().optional(),
openAIApiProxy: z.string().optional(),
openAIThinkingModel: z.string().optional(),
openAINetworkingModel: z.string().optional(),
anthropicApiKey: z.string().optional(),
anthropicApiProxy: z.string().optional(),
anthropicThinkingModel: z.string().optional(),
anthropicNetworkingModel: z.string().optional(),
deepseekApiKey: z.string().optional(),
deepseekApiProxy: z.string().optional(),
deepseekThinkingModel: z.string().optional(),
deepseekNetworkingModel: z.string().optional(),
xAIApiKey: z.string().optional(),
xAIApiProxy: z.string().optional(),
xAIThinkingModel: z.string().optional(),
xAINetworkingModel: z.string().optional(),
mistralApiKey: z.string().optional(),
mistralApiProxy: z.string().optional(),
mistralThinkingModel: z.string().optional(),
mistralNetworkingModel: z.string().optional(),
azureApiKey: z.string().optional(),
azureResourceName: z.string().optional(),
azureApiVersion: z.string().optional(),
azureThinkingModel: z.string().optional(),
azureNetworkingModel: z.string().optional(),
openAICompatibleApiKey: z.string().optional(),
openAICompatibleApiProxy: z.string().optional(),
openAICompatibleThinkingModel: z.string().optional(),
openAICompatibleNetworkingModel: z.string().optional(),
pollinationsApiProxy: z.string().optional(),
pollinationsThinkingModel: z.string().optional(),
pollinationsNetworkingModel: z.string().optional(),
ollamaApiProxy: z.string().optional(),
ollamaThinkingModel: z.string().optional(),
ollamaNetworkingModel: z.string().optional(),
accessPassword: z.string().optional(),
enableSearch: z.string(),
searchProvider: z.string().optional(),
tavilyApiKey: z.string().optional(),
tavilyApiProxy: z.string().optional(),
tavilyScope: z.string().optional(),
firecrawlApiKey: z.string().optional(),
firecrawlApiProxy: z.string().optional(),
exaApiKey: z.string().optional(),
exaApiProxy: z.string().optional(),
exaScope: z.string().optional(),
bochaApiKey: z.string().optional(),
bochaApiProxy: z.string().optional(),
searxngApiProxy: z.string().optional(),
searxngScope: z.string().optional(),
parallelSearch: z.number().min(1).max(5),
searchMaxResult: z.number().min(1).max(10),
language: z.string().optional(),
theme: z.string().optional(),
debug: z.enum(["enable", "disable"]).optional(),
references: z.enum(["enable", "disable"]).optional(),
citationImage: z.enum(["enable", "disable"]).optional(),
smoothTextStreamType: z.enum(["character", "word", "line"]).optional(),
onlyUseLocalResource: z.enum(["enable", "disable"]).optional(),
});
function convertModelName(name: string) {
return name
.replaceAll("/", "-")
.split("-")
.map((word) => capitalize(word))
.join(" ");
}
let preLoading = false;
function HelpTip({ children, tip }: { children: ReactNode; tip: string }) {
const [open, setOpen] = useState<boolean>(false);
const handleOpen = () => {
setOpen(true);
setTimeout(() => {
setOpen(false);
}, 2000);
};
return (
<div className="flex items-center">
<span className="flex-1">{children}</span>
<TooltipProvider delayDuration={100}>
<Tooltip open={open} onOpenChange={(opened) => setOpen(opened)}>
<TooltipTrigger asChild>
<CircleHelp
className="cursor-help w-4 h-4 ml-1 opacity-50 max-sm:ml-0"
onClick={(ev) => {
ev.preventDefault();
ev.stopPropagation();
handleOpen();
}}
/>
</TooltipTrigger>
<TooltipContent className="max-w-52">
<p>{tip}</p>
</TooltipContent>
</Tooltip>
</TooltipProvider>
</div>
);
}
function Setting({ open, onClose }: SettingProps) {
const { t } = useTranslation();
const { mode, provider, searchProvider, update } = useSettingStore();
const { modelList, refresh } = useModel();
const pwaInstall = usePWAInstall();
const [isRefreshing, setIsRefreshing] = useState<boolean>(false);
const thinkingModelList = useMemo(() => {
const { provider } = useSettingStore.getState();
if (provider === "google") {
return filterThinkingModelList(modelList);
} else if (provider === "openrouter") {
return filterOpenRouterModelList(modelList);
} else if (provider === "deepseek") {
return filterDeepSeekModelList(modelList);
} else if (provider === "mistral") {
return filterMistralModelList(modelList);
} else if (provider === "pollinations") {
return filterPollinationsModelList(modelList);
}
return [[], modelList];
}, [modelList]);
const networkingModelList = useMemo(() => {
const { provider } = useSettingStore.getState();
if (provider === "google") {
return filterNetworkingModelList(modelList);
} else if (provider === "openrouter") {
return filterOpenRouterModelList(modelList);
} else if (provider === "openai") {
return filterOpenAIModelList(modelList);
} else if (provider === "mistral") {
return filterMistralModelList(modelList);
} else if (provider === "pollinations") {
return filterPollinationsModelList(modelList);
}
return [[], modelList];
}, [modelList]);
const form = useForm<z.infer<typeof formSchema>>({
resolver: zodResolver(formSchema),
defaultValues: async () => {
return new Promise((resolve) => {
const state = useSettingStore.getState();
resolve({ ...omit(state, ["update"]) });
});
},
});
const isDisabledAIProvider = useCallback(
(provider: string) => {
const disabledAIProviders =
mode === "proxy" && DISABLED_AI_PROVIDER.length > 0
? DISABLED_AI_PROVIDER.split(",")
: [];
return disabledAIProviders.includes(provider);
},
[mode]
);
const isDisabledAIModel = useCallback(
(model: string) => {
if (mode === "local") return false;
const { availableModelList, disabledModelList } = getCustomModelList(
MODEL_LIST.length > 0 ? MODEL_LIST.split(",") : []
);
const isAvailableModel = availableModelList.some(
(availableModel) => availableModel === model
);
if (isAvailableModel) return false;
if (disabledModelList.includes("all")) return true;
return disabledModelList.some((disabledModel) => disabledModel === model);
},
[mode]
);
const isDisabledSearchProvider = useCallback(
(provider: string) => {
const disabledSearchProviders =
mode === "proxy" && DISABLED_SEARCH_PROVIDER.length > 0
? DISABLED_SEARCH_PROVIDER.split(",")
: [];
return disabledSearchProviders.includes(provider);
},
[mode]
);
const installPWA = async () => {
if ("serviceWorker" in navigator) {
await window.serwist?.register();
}
if (pwaInstall) await pwaInstall();
};
function handleClose(open: boolean) {
if (!open) onClose();
}
function handleSubmit(values: z.infer<typeof formSchema>) {
update(values);
onClose();
}
const fetchModelList = useCallback(async () => {
const { provider } = useSettingStore.getState();
try {
setIsRefreshing(true);
await refresh(provider);
} finally {
setIsRefreshing(false);
}
}, [refresh]);
function handleModeChange(mode: string) {
update({ mode });
}
async function handleProviderChange(provider: string) {
update({ provider });
await fetchModelList();
}
async function handleSearchProviderChange(searchProvider: string) {
update({ searchProvider });
}
async function updateSetting(key: string, value?: string | number) {
update({ [key]: value });
await fetchModelList();
}
function handleReset() {
toast.warning(t("setting.resetSetting"), {
description: t("setting.resetSettingWarning"),
duration: 5000,
action: {
label: t("setting.confirm"),
onClick: async () => {
const { reset } = useSettingStore.getState();
reset();
await researchStore.clear();
},
},
});
}
useLayoutEffect(() => {
if (open && !preLoading) {
preLoading = true;
fetchModelList();
}
}, [open, fetchModelList]);
useLayoutEffect(() => {
if (open && mode === "") {
const { apiKey, accessPassword, update } = useSettingStore.getState();
const requestMode = !apiKey && accessPassword ? "proxy" : "local";
update({ mode: requestMode });
form.setValue("mode", requestMode);
}
}, [open, mode, form]);
return (
<Dialog open={open} onOpenChange={handleClose}>
<DialogContent className="max-w-lg max-lg:max-w-md print:hidden">
<DialogHeader>
<DialogTitle>{t("setting.title")}</DialogTitle>
<DialogDescription>{t("setting.description")}</DialogDescription>
</DialogHeader>
<Form {...form}>
<form className="space-y-4">
<Tabs defaultValue="llm">
<TabsList className="w-full mb-2">
<TabsTrigger className="flex-1" value="llm">
{t("setting.model")}
</TabsTrigger>
<TabsTrigger className="flex-1" value="search">
{t("setting.search")}
</TabsTrigger>
<TabsTrigger className="flex-1" value="general">
{t("setting.general")}
</TabsTrigger>
<TabsTrigger className="flex-1" value="experimental">
{t("setting.experimental")}
</TabsTrigger>
</TabsList>
<TabsContent className="space-y-4 min-h-[250px]" value="llm">
<div className={BUILD_MODE === "export" ? "hidden" : ""}>
<FormField
control={form.control}
name="mode"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.modeTip")}>
{t("setting.mode")}
</HelpTip>
</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={(value) => {
field.onChange(value);
handleModeChange(value);
}}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent className="max-sm:max-h-48">
<SelectItem value="local">
{t("setting.local")}
</SelectItem>
<SelectItem value="proxy">
{t("setting.proxy")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="provider"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.providerTip")}>
{t("setting.provider")}
</HelpTip>
</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={(value) => {
field.onChange(value);
handleProviderChange(value);
}}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{!isDisabledAIProvider("google") ? (
<SelectItem value="google">
Google AI Studio
</SelectItem>
) : null}
{!isDisabledAIProvider("openai") ? (
<SelectItem value="openai">OpenAI</SelectItem>
) : null}
{!isDisabledAIProvider("anthropic") ? (
<SelectItem value="anthropic">
Anthropic
</SelectItem>
) : null}
{!isDisabledAIProvider("deepseek") ? (
<SelectItem value="deepseek">DeepSeek</SelectItem>
) : null}
{!isDisabledAIProvider("xai") ? (
<SelectItem value="xai">xAI Grok</SelectItem>
) : null}
{!isDisabledAIProvider("mistral") ? (
<SelectItem value="mistral">Mistral</SelectItem>
) : null}
{!isDisabledAIProvider("azure") ? (
<SelectItem value="azure">
Azure OpenAI
</SelectItem>
) : null}
{!isDisabledAIProvider("openrouter") ? (
<SelectItem value="openrouter">
OpenRouter
</SelectItem>
) : null}
{!isDisabledAIProvider("openaicompatible") ? (
<SelectItem value="openaicompatible">
{t("setting.openAICompatible")}
</SelectItem>
) : null}
{!isDisabledAIProvider("pollinations") ? (
<SelectItem value="pollinations">
Pollinations ({t("setting.free")})
</SelectItem>
) : null}
{!isDisabledAIProvider("ollama") ? (
<SelectItem value="ollama">Ollama</SelectItem>
) : null}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<div className={mode === "proxy" ? "hidden" : ""}>
<div
className={cn("space-y-4", {
hidden: provider !== "google",
})}
>
<FormField
control={form.control}
name="apiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"apiKey",
form.getValues("apiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="apiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={GEMINI_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"apiProxy",
form.getValues("apiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "openrouter",
})}
>
<FormField
control={form.control}
name="openRouterApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"openRouterApiKey",
form.getValues("openRouterApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="openRouterApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={OPENROUTER_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"openRouterApiProxy",
form.getValues("openRouterApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "openai",
})}
>
<FormField
control={form.control}
name="openAIApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"openAIApiKey",
form.getValues("openAIApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="openAIApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={OPENAI_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"openAIApiProxy",
form.getValues("openAIApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "anthropic",
})}
>
<FormField
control={form.control}
name="anthropicApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"anthropicApiKey",
form.getValues("anthropicApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="anthropicApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={ANTHROPIC_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"anthropicApiProxy",
form.getValues("anthropicApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "deepseek",
})}
>
<FormField
control={form.control}
name="deepseekApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"deepseekApiKey",
form.getValues("deepseekApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="deepseekApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={DEEPSEEK_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"deepseekApiProxy",
form.getValues("deepseekApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "xai",
})}
>
<FormField
control={form.control}
name="xAIApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"xAIApiKey",
form.getValues("xAIApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="xAIApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={XAI_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"xAIApiProxy",
form.getValues("xAIApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "mistral",
})}
>
<FormField
control={form.control}
name="mistralApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"mistralApiKey",
form.getValues("mistralApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="mistralApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={MISTRAL_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"mistralApiProxy",
form.getValues("mistralApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "azure",
})}
>
<FormField
control={form.control}
name="azureApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"azureApiKey",
form.getValues("azureApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="azureResourceName"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.resourceNameLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={t("setting.resourceNamePlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"azureResourceName",
form.getValues("azureResourceName")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="azureApiVersion"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiVersionLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={t("setting.apiVersionPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"azureApiVersion",
form.getValues("azureApiVersion")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "openaicompatible",
})}
>
<FormField
control={form.control}
name="openAICompatibleApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.apiKeyPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"openAICompatibleApiKey",
form.getValues("openAICompatibleApiKey")
)
}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="openAICompatibleApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={t("setting.apiUrlPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"openAICompatibleApiProxy",
form.getValues("openAICompatibleApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "pollinations",
})}
>
<FormField
control={form.control}
name="pollinationsApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={POLLINATIONS_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"pollinationsApiProxy",
form.getValues("pollinationsApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "ollama",
})}
>
<FormField
control={form.control}
name="ollamaApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={OLLAMA_BASE_URL}
{...field}
onBlur={() =>
updateSetting(
"ollamaApiProxy",
form.getValues("ollamaApiProxy")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<div
className={cn("space-y-4", {
hidden: mode === "local" || BUILD_MODE === "export",
})}
>
<FormField
control={form.control}
name="accessPassword"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.accessPasswordTip")}>
{t("setting.accessPassword")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.accessPasswordPlaceholder")}
{...field}
onBlur={() =>
updateSetting(
"accessPassword",
form.getValues("accessPassword")
)
}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "google",
})}
>
<FormField
control={form.control}
name="thinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{thinkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{thinkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{thinkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="networkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{networkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{networkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{networkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "openrouter",
})}
>
<FormField
control={form.control}
name="openRouterThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{thinkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{thinkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{thinkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="openRouterNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{networkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{networkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{networkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "openai",
})}
>
<FormField
control={form.control}
name="openAIThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{thinkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{thinkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{thinkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="openAINetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{networkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{networkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{networkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "anthropic",
})}
>
<FormField
control={form.control}
name="anthropicThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="anthropicNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "deepseek",
})}
>
<FormField
control={form.control}
name="deepseekThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{thinkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{thinkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{thinkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="deepseekNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{networkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{networkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{networkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "xai",
})}
>
<FormField
control={form.control}
name="xAIThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="xAINetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "mistral",
})}
>
<FormField
control={form.control}
name="mistralThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{thinkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{thinkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{thinkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="mistralNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{networkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{networkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{networkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "azure",
})}
>
<FormField
control={form.control}
name="azureThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Input
placeholder={t("setting.modelListPlaceholder")}
{...field}
/>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="azureNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Input
placeholder={t("setting.modelListPlaceholder")}
{...field}
/>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "openaicompatible",
})}
>
<FormField
control={form.control}
name="openAICompatibleThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field flex gap-2">
<Input
className={cn("flex-1", {
hidden: modelList.length > 0,
})}
placeholder={t("setting.modelListPlaceholder")}
{...field}
/>
<div
className={cn("flex-1", {
hidden: modelList.length === 0,
})}
>
<Select
defaultValue={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
</div>
<Button
type="button"
variant="outline"
size="icon"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
<RefreshCw
className={isRefreshing ? "animate-spin" : ""}
/>
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="openAICompatibleNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full flex gap-2">
<Input
className={cn("flex-1", {
hidden: modelList.length > 0,
})}
placeholder={t("setting.modelListPlaceholder")}
{...field}
/>
<div
className={cn("flex-1", {
hidden: modelList.length === 0,
})}
>
<Select
defaultValue={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
</div>
<Button
type="button"
variant="outline"
size="icon"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
<RefreshCw
className={isRefreshing ? "animate-spin" : ""}
/>
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "pollinations",
})}
>
<FormField
control={form.control}
name="pollinationsThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{thinkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{thinkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{thinkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="pollinationsNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger
className={cn({
hidden: modelList.length === 0,
})}
>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{networkingModelList[0].length > 0 ? (
<SelectGroup>
<SelectLabel>
{t("setting.recommendedModels")}
</SelectLabel>
{networkingModelList[0].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
) : null}
<SelectGroup>
<SelectLabel>
{t("setting.basicModels")}
</SelectLabel>
{networkingModelList[1].map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectGroup>
</SelectContent>
</Select>
<Button
className={cn("w-full", {
hidden: modelList.length > 0,
})}
type="button"
variant="outline"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
{isRefreshing ? (
<>
<RefreshCw className="animate-spin" />{" "}
{t("setting.modelListLoading")}
</>
) : (
<>
<RefreshCw /> {t("setting.refresh")}
</>
)}
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: provider !== "ollama",
})}
>
<FormField
control={form.control}
name="ollamaThinkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.thinkingModelTip")}>
{t("setting.thinkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field flex gap-2">
<Input
className={cn("flex-1", {
hidden: modelList.length > 0,
})}
placeholder={t("setting.modelListPlaceholder")}
{...field}
/>
<div
className={cn("flex-1", {
hidden: modelList.length === 0,
})}
>
<Select
defaultValue={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
</div>
<Button
type="button"
variant="outline"
size="icon"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
<RefreshCw
className={isRefreshing ? "animate-spin" : ""}
/>
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="ollamaNetworkingModel"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.networkingModelTip")}>
{t("setting.networkingModel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</HelpTip>
</FormLabel>
<FormControl>
<div className="form-field w-full flex gap-2">
<Input
className={cn("flex-1", {
hidden: modelList.length > 0,
})}
placeholder={t("setting.modelListPlaceholder")}
{...field}
/>
<div
className={cn("flex-1", {
hidden: modelList.length === 0,
})}
>
<Select
defaultValue={field.value}
onValueChange={field.onChange}
>
<SelectTrigger>
<SelectValue
placeholder={t(
"setting.modelListLoadingPlaceholder"
)}
/>
</SelectTrigger>
<SelectContent className="max-sm:max-h-72">
{modelList.map((name) => {
return !isDisabledAIModel(name) ? (
<SelectItem key={name} value={name}>
{convertModelName(name)}
</SelectItem>
) : null;
})}
</SelectContent>
</Select>
</div>
<Button
type="button"
variant="outline"
size="icon"
disabled={isRefreshing}
onClick={() => fetchModelList()}
>
<RefreshCw
className={isRefreshing ? "animate-spin" : ""}
/>
</Button>
</div>
</FormControl>
</FormItem>
)}
/>
</div>
</TabsContent>
<TabsContent className="space-y-4 min-h-[250px]" value="search">
<FormField
control={form.control}
name="enableSearch"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.webSearchTip")}>
{t("setting.webSearch")}
</HelpTip>
</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="1">
{t("setting.enable")}
</SelectItem>
<SelectItem value="0">
{t("setting.disable")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="searchProvider"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.searchProviderTip")}>
{t("setting.searchProvider")}
</HelpTip>
</FormLabel>
<FormControl>
<Select
value={field.value}
disabled={form.getValues("enableSearch") === "0"}
onValueChange={(value) => {
field.onChange(value);
handleSearchProviderChange(value);
}}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="model">
{t("setting.modelBuiltin")}
</SelectItem>
{!isDisabledSearchProvider("tavily") ? (
<SelectItem value="tavily">Tavily</SelectItem>
) : null}
{!isDisabledSearchProvider("firecrawl") ? (
<SelectItem value="firecrawl">
Firecrawl
</SelectItem>
) : null}
{!isDisabledSearchProvider("exa") &&
mode === "proxy" ? (
<SelectItem value="exa">Exa</SelectItem>
) : null}
{!isDisabledSearchProvider("bocha") ? (
<SelectItem value="bocha">
{t("setting.bocha")}
</SelectItem>
) : null}
{!isDisabledSearchProvider("searxng") ? (
<SelectItem value="searxng">SearXNG</SelectItem>
) : null}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<div className={mode === "proxy" ? "hidden" : ""}>
<div
className={cn("space-y-4", {
hidden: searchProvider !== "tavily",
})}
>
<FormField
control={form.control}
name="tavilyApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.searchApiKeyPlaceholder")}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="tavilyApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={TAVILY_BASE_URL}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="tavilyScope"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.searchScope")}
</FormLabel>
<FormControl className="form-field">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="general">
{t("setting.scopeValue.general")}
</SelectItem>
<SelectItem value="news">
{t("setting.scopeValue.news")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: searchProvider !== "firecrawl",
})}
>
<FormField
control={form.control}
name="firecrawlApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.searchApiKeyPlaceholder")}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="firecrawlApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={FIRECRAWL_BASE_URL}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: searchProvider !== "exa",
})}
>
<FormField
control={form.control}
name="exaApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.searchApiKeyPlaceholder")}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="exaApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={EXA_BASE_URL}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="exaScope"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.searchScope")}
</FormLabel>
<FormControl className="form-field">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="research paper">
{t("setting.scopeValue.researchPaper")}
</SelectItem>
<SelectItem value="financial">
{t("setting.scopeValue.financial")}
</SelectItem>
<SelectItem value="news">
{t("setting.scopeValue.news")}
</SelectItem>
<SelectItem value="company">
{t("setting.scopeValue.company")}
</SelectItem>
<SelectItem value="personal site">
{t("setting.scopeValue.personalSite")}
</SelectItem>
<SelectItem value="github">
{t("setting.scopeValue.github")}
</SelectItem>
<SelectItem value="linkedin">
{t("setting.scopeValue.linkedin")}
</SelectItem>
<SelectItem value="pdf">
{t("setting.scopeValue.pdf")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: searchProvider !== "bocha",
})}
>
<FormField
control={form.control}
name="bochaApiKey"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiKeyLabel")}
<span className="ml-1 text-red-500 max-sm:hidden">
*
</span>
</FormLabel>
<FormControl className="form-field">
<Password
type="text"
placeholder={t("setting.searchApiKeyPlaceholder")}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="bochaApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={BOCHA_BASE_URL}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
</div>
<div
className={cn("space-y-4", {
hidden: searchProvider !== "searxng",
})}
>
<FormField
control={form.control}
name="searxngApiProxy"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.apiUrlLabel")}
</FormLabel>
<FormControl className="form-field">
<Input
placeholder={SEARXNG_BASE_URL}
disabled={form.getValues("enableSearch") === "0"}
{...field}
/>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="searxngScope"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
{t("setting.searchScope")}
</FormLabel>
<FormControl className="form-field">
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="all">
{t("setting.scopeValue.all")}
</SelectItem>
<SelectItem value="academic">
{t("setting.scopeValue.academic")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
</div>
</div>
<FormField
control={form.control}
name="parallelSearch"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.parallelSearchTip")}>
{t("setting.parallelSearch")}
</HelpTip>
</FormLabel>
<FormControl className="form-field">
<div className="flex h-9">
<Slider
className="flex-1"
value={[field.value]}
max={5}
min={1}
step={1}
disabled={form.getValues("enableSearch") === "0"}
onValueChange={(values) =>
field.onChange(values[0])
}
/>
<span className="w-[14%] text-center text-sm leading-10">
{field.value}
</span>
</div>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="searchMaxResult"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.searchResultsTip")}>
{t("setting.searchResults")}
</HelpTip>
</FormLabel>
<FormControl className="form-field">
<div className="flex h-9">
<Slider
className="flex-1"
value={[field.value]}
max={10}
min={1}
step={1}
disabled={form.getValues("enableSearch") === "0"}
onValueChange={(values) =>
field.onChange(values[0])
}
/>
<span className="w-[14%] text-center text-sm leading-10">
{field.value}
</span>
</div>
</FormControl>
</FormItem>
)}
/>
</TabsContent>
<TabsContent className="space-y-4 min-h-[250px]" value="general">
<FormField
control={form.control}
name="language"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.languageTip")}>
{t("setting.language")}
</HelpTip>
</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
{Object.entries(locales).map(([code, name]) => {
return (
<SelectItem key={code} value={code}>
{name}
</SelectItem>
);
})}
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="theme"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">{t("theme")}</FormLabel>
<FormControl>
<Select
value={field.value}
onValueChange={field.onChange}
>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="system">
{t("setting.system")}
</SelectItem>
<SelectItem value="light">
{t("setting.light")}
</SelectItem>
<SelectItem value="dark">
{t("setting.dark")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="debug"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.debugTip")}>
{t("setting.debug")}
</HelpTip>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="enable">
{t("setting.enable")}
</SelectItem>
<SelectItem value="disable">
{t("setting.disable")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
{pwaInstall ? (
<div className="from-item">
<Label className="from-label">
<HelpTip tip={t("setting.PWATip")}>
{t("setting.PWA")}
</HelpTip>
</Label>
<Button
className="form-field"
type="button"
variant="ghost"
onClick={() => installPWA()}
>
<MonitorDown className="mr-1.5 h-4 w-4" />
{t("setting.installlPWA")}
</Button>
</div>
) : null}
<div className="from-item">
<Label className="from-label">{t("setting.version")}</Label>
<div className="form-field text-center leading-9">
{`v${VERSION}`}
<small className="ml-1">
(
<a
className="hover:underline hover:underline-offset-4 hover:text-blue-500"
href="https://github.com/u14app/deep-research"
target="_blank"
>
{t("setting.checkForUpdate")}
</a>
)
</small>
</div>
</div>
<div className="from-item">
<Label className="from-label">
{t("setting.resetSetting")}
</Label>
<Button
className="form-field hover:text-red-500"
type="button"
variant="ghost"
onClick={() => handleReset()}
>
{t("setting.resetAllSettings")}
</Button>
</div>
</TabsContent>
<TabsContent
className="space-y-4 min-h-[250px]"
value="experimental"
>
<FormField
control={form.control}
name="references"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.referencesTip")}>
{t("setting.references")}
</HelpTip>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="enable">
{t("setting.enable")}
</SelectItem>
<SelectItem value="disable">
{t("setting.disable")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="citationImage"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.citationImageTip")}>
{t("setting.citationImage")}
</HelpTip>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="enable">
{t("setting.enable")}
</SelectItem>
<SelectItem value="disable">
{t("setting.disable")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="smoothTextStreamType"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.textOutputModeTip")}>
{t("setting.textOutputMode")}
</HelpTip>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="character">
{t("setting.character")}
</SelectItem>
<SelectItem value="word">
{t("setting.word")}
</SelectItem>
<SelectItem value="line">
{t("setting.line")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
<FormField
control={form.control}
name="onlyUseLocalResource"
render={({ field }) => (
<FormItem className="from-item">
<FormLabel className="from-label">
<HelpTip tip={t("setting.useLocalResourceTip")}>
{t("setting.useLocalResource")}
</HelpTip>
</FormLabel>
<FormControl>
<Select {...field} onValueChange={field.onChange}>
<SelectTrigger className="form-field">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="enable">
{t("setting.enable")}
</SelectItem>
<SelectItem value="disable">
{t("setting.disable")}
</SelectItem>
</SelectContent>
</Select>
</FormControl>
</FormItem>
)}
/>
</TabsContent>
</Tabs>
</form>
</Form>
<DialogFooter className="mt-2 flex-row sm:justify-between sm:space-x-0 gap-3">
<Button className="flex-1" variant="outline" onClick={onClose}>
{t("setting.cancel")}
</Button>
<Button
className="flex-1"
type="submit"
onClick={form.handleSubmit(handleSubmit)}
>
{t("setting.save")}
</Button>
</DialogFooter>
</DialogContent>
</Dialog>
);
}
export default Setting;