mirror of
https://gitee.com/wanwujie/deer-flow
synced 2026-04-22 21:54:45 +08:00
183 lines
5.7 KiB
TypeScript
183 lines
5.7 KiB
TypeScript
// Copyright (c) 2025 Bytedance Ltd. and/or its affiliates
|
|
// SPDX-License-Identifier: MIT
|
|
|
|
import { zodResolver } from "@hookform/resolvers/zod";
|
|
import { Settings } from "lucide-react";
|
|
import { useTranslations } from "next-intl";
|
|
import { useEffect, useMemo } from "react";
|
|
import { useForm } from "react-hook-form";
|
|
import { z } from "zod";
|
|
|
|
import {
|
|
Form,
|
|
FormControl,
|
|
FormDescription,
|
|
FormField,
|
|
FormItem,
|
|
FormLabel,
|
|
FormMessage,
|
|
} from "~/components/ui/form";
|
|
import { Input } from "~/components/ui/input";
|
|
import { Label } from "~/components/ui/label";
|
|
import { Switch } from "~/components/ui/switch";
|
|
import type { SettingsState } from "~/core/store";
|
|
|
|
import type { Tab } from "./types";
|
|
|
|
const generalFormSchema = z.object({
|
|
autoAcceptedPlan: z.boolean(),
|
|
maxPlanIterations: z.number().min(1, {
|
|
message: "Max plan iterations must be at least 1.",
|
|
}),
|
|
maxStepNum: z.number().min(1, {
|
|
message: "Max step number must be at least 1.",
|
|
}),
|
|
maxSearchResults: z.number().min(1, {
|
|
message: "Max search results must be at least 1.",
|
|
}),
|
|
// Others
|
|
enableBackgroundInvestigation: z.boolean(),
|
|
enableDeepThinking: z.boolean(),
|
|
reportStyle: z.enum(["academic", "popular_science", "news", "social_media","strategic_investment"]),
|
|
});
|
|
|
|
export const GeneralTab: Tab = ({
|
|
settings,
|
|
onChange,
|
|
}: {
|
|
settings: SettingsState;
|
|
onChange: (changes: Partial<SettingsState>) => void;
|
|
}) => {
|
|
const t = useTranslations("settings.general");
|
|
const generalSettings = useMemo(() => settings.general, [settings]);
|
|
const form = useForm<z.infer<typeof generalFormSchema>>({
|
|
resolver: zodResolver(generalFormSchema, undefined, undefined),
|
|
defaultValues: generalSettings,
|
|
mode: "all",
|
|
reValidateMode: "onBlur",
|
|
});
|
|
|
|
const currentSettings = form.watch();
|
|
useEffect(() => {
|
|
let hasChanges = false;
|
|
for (const key in currentSettings) {
|
|
if (
|
|
currentSettings[key as keyof typeof currentSettings] !==
|
|
settings.general[key as keyof SettingsState["general"]]
|
|
) {
|
|
hasChanges = true;
|
|
break;
|
|
}
|
|
}
|
|
if (hasChanges) {
|
|
onChange({ general: currentSettings });
|
|
}
|
|
}, [currentSettings, onChange, settings]);
|
|
|
|
return (
|
|
<div className="flex flex-col gap-4">
|
|
<header>
|
|
<h1 className="text-lg font-medium">{t("title")}</h1>
|
|
</header>
|
|
<main>
|
|
<Form {...form}>
|
|
<form className="space-y-8">
|
|
<FormField
|
|
control={form.control}
|
|
name="autoAcceptedPlan"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormControl>
|
|
<div className="flex items-center gap-2">
|
|
<Switch
|
|
id="autoAcceptedPlan"
|
|
checked={field.value}
|
|
onCheckedChange={field.onChange}
|
|
/>
|
|
<Label className="text-sm" htmlFor="autoAcceptedPlan">
|
|
{t("autoAcceptPlan")}
|
|
</Label>
|
|
</div>
|
|
</FormControl>
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="maxPlanIterations"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>{t("maxPlanIterations")}</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
className="w-60"
|
|
type="number"
|
|
defaultValue={field.value}
|
|
min={1}
|
|
onChange={(event) =>
|
|
field.onChange(parseInt(event.target.value || "0"))
|
|
}
|
|
/>
|
|
</FormControl>
|
|
<FormDescription>
|
|
{t("maxPlanIterationsDescription")}
|
|
</FormDescription>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="maxStepNum"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>{t("maxStepsOfPlan")}</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
className="w-60"
|
|
type="number"
|
|
defaultValue={field.value}
|
|
min={1}
|
|
onChange={(event) =>
|
|
field.onChange(parseInt(event.target.value || "0"))
|
|
}
|
|
/>
|
|
</FormControl>
|
|
<FormDescription>{t("maxStepsDescription")}</FormDescription>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
<FormField
|
|
control={form.control}
|
|
name="maxSearchResults"
|
|
render={({ field }) => (
|
|
<FormItem>
|
|
<FormLabel>{t("maxSearchResults")}</FormLabel>
|
|
<FormControl>
|
|
<Input
|
|
className="w-60"
|
|
type="number"
|
|
defaultValue={field.value}
|
|
min={1}
|
|
onChange={(event) =>
|
|
field.onChange(parseInt(event.target.value || "0"))
|
|
}
|
|
/>
|
|
</FormControl>
|
|
<FormDescription>
|
|
{t("maxSearchResultsDescription")}
|
|
</FormDescription>
|
|
<FormMessage />
|
|
</FormItem>
|
|
)}
|
|
/>
|
|
</form>
|
|
</Form>
|
|
</main>
|
|
</div>
|
|
);
|
|
};
|
|
GeneralTab.displayName = "General";
|
|
GeneralTab.icon = Settings;
|