Files
deer-flow/web/src/app/settings/tabs/general-tab.tsx

181 lines
5.6 KiB
TypeScript
Raw Normal View History

2025-04-24 15:41:33 +08:00
// 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 { 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";
2025-04-24 15:41:33 +08:00
import type { SettingsState } from "~/core/store";
import type { Tab } from "./types";
const generalFormSchema = z.object({
autoAcceptedPlan: z.boolean(),
enableBackgroundInvestigation: z.boolean(),
2025-04-24 15:41:33 +08:00
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.",
}),
2025-04-24 15:41:33 +08:00
});
export const GeneralTab: Tab = ({
settings,
onChange,
}: {
settings: SettingsState;
onChange: (changes: Partial<SettingsState>) => void;
}) => {
const generalSettings = useMemo(() => settings.general, [settings]);
const form = useForm<z.infer<typeof generalFormSchema>>({
resolver: zodResolver(generalFormSchema, undefined, undefined),
2025-05-07 13:22:21 +08:00
defaultValues: generalSettings,
2025-05-07 12:09:02 +08:00
mode: "all",
2025-05-07 13:22:21 +08:00
reValidateMode: "onBlur",
2025-04-24 15:41:33 +08:00
});
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">General</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">
2025-04-24 17:26:16 +08:00
Allow automatic acceptance of plans
</Label>
</div>
</FormControl>
</FormItem>
)}
/>
2025-04-24 15:41:33 +08:00
<FormField
control={form.control}
name="maxPlanIterations"
render={({ field }) => (
<FormItem>
<FormLabel>Max plan iterations</FormLabel>
<FormControl>
<Input
className="w-60"
type="number"
2025-05-07 13:22:21 +08:00
defaultValue={field.value}
2025-04-24 15:41:33 +08:00
min={1}
onChange={(event) =>
2025-05-07 12:09:02 +08:00
field.onChange(parseInt(event.target.value || "0"))
2025-04-24 15:41:33 +08:00
}
/>
</FormControl>
<FormDescription>
2025-04-24 17:35:38 +08:00
Set to 1 for single-step planning. Set to 2 or more to
enable re-planning.
2025-04-24 15:41:33 +08:00
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="maxStepNum"
render={({ field }) => (
<FormItem>
<FormLabel>Max steps of a research plan</FormLabel>
<FormControl>
<Input
className="w-60"
type="number"
2025-05-07 13:22:21 +08:00
defaultValue={field.value}
2025-04-24 15:41:33 +08:00
min={1}
onChange={(event) =>
2025-05-07 12:09:02 +08:00
field.onChange(parseInt(event.target.value || "0"))
2025-04-24 15:41:33 +08:00
}
/>
</FormControl>
<FormDescription>
By default, each research plan has 3 steps.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="maxSearchResults"
render={({ field }) => (
<FormItem>
<FormLabel>Max search results</FormLabel>
<FormControl>
<Input
className="w-60"
type="number"
defaultValue={field.value}
min={1}
onChange={(event) =>
field.onChange(parseInt(event.target.value || "0"))
}
/>
</FormControl>
<FormDescription>
By default, each search step has 3 results.
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
2025-04-24 15:41:33 +08:00
</form>
</Form>
</main>
</div>
);
};
GeneralTab.displayName = "";
GeneralTab.icon = Settings;