"use client"; import { useState, useEffect, useRef } from "react"; import { useTranslation } from "react-i18next"; import { LoaderCircle, SquarePlus, FilePlus, BookText, Paperclip, Link, } from "lucide-react"; import { useForm } from "react-hook-form"; import { z } from "zod"; import { zodResolver } from "@hookform/resolvers/zod"; import { toast } from "sonner"; import ResourceList from "@/components/Knowledge/ResourceList"; import Crawler from "@/components/Knowledge/Crawler"; import { Button } from "@/components/Internal/Button"; import { Form, FormField } from "@/components/ui/form"; import { Textarea } from "@/components/ui/textarea"; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, } from "@/components/ui/dropdown-menu"; import useDeepResearch from "@/hooks/useDeepResearch"; import useAiProvider from "@/hooks/useAiProvider"; import useKnowledge from "@/hooks/useKnowledge"; import useAccurateTimer from "@/hooks/useAccurateTimer"; import useSubmitShortcut from "@/hooks/useSubmitShortcut"; import { useGlobalStore } from "@/store/global"; import { useSettingStore } from "@/store/setting"; import { useTaskStore } from "@/store/task"; import { useHistoryStore } from "@/store/history"; const formSchema = z.object({ topic: z.string().min(2), }); const TOPIC_FIELD_ID = "research-topic-field"; const RESOURCE_TRIGGER_ID = "knowledge-resource-trigger"; function Topic() { const { t } = useTranslation(); const fileInputRef = useRef(null); const taskStore = useTaskStore(); const { askQuestions } = useDeepResearch(); const { hasApiKey } = useAiProvider(); const { getKnowledgeFromFile } = useKnowledge(); const { formattedTime, start: accurateTimerStart, stop: accurateTimerStop, } = useAccurateTimer(); const [isThinking, setIsThinking] = useState(false); const [openCrawler, setOpenCrawler] = useState(false); const form = useForm>({ resolver: zodResolver(formSchema), defaultValues: { topic: taskStore.question, }, }); const topicPresets: Array<{ id: string; label: string; value: string }> = [ { id: "market", label: t("research.topic.presets.marketLabel"), value: t("research.topic.presets.marketPrompt"), }, { id: "technical", label: t("research.topic.presets.technicalLabel"), value: t("research.topic.presets.technicalPrompt"), }, { id: "comparison", label: t("research.topic.presets.comparisonLabel"), value: t("research.topic.presets.comparisonPrompt"), }, ]; const handleTopicSubmitShortcut = useSubmitShortcut(() => { void form.handleSubmit(handleSubmit)(); }); function applyTopicPreset(value: string): void { form.setValue("topic", value, { shouldDirty: true, shouldTouch: true, }); } function handleCheck(): boolean { const { mode } = useSettingStore.getState(); if ((mode === "local" && hasApiKey()) || mode === "proxy") { return true; } else { const { setOpenSetting } = useGlobalStore.getState(); setOpenSetting(true); return false; } } async function handleSubmit(values: z.infer) { if (handleCheck()) { const { id, setQuestion } = useTaskStore.getState(); try { console.log(`[Topic handleSubmit] Start Thinking requested. Topic: "${values.topic.substring(0, 50)}..."`); setIsThinking(true); accurateTimerStart(); if (id !== "") { createNewResearch(); form.setValue("topic", values.topic); } setQuestion(values.topic); console.log(`[Topic handleSubmit] Calling askQuestions()`); await askQuestions(); console.log(`[Topic handleSubmit] askQuestions() completed`); } catch (err) { console.error("[Topic handleSubmit] Caught error:", err); const { mode } = useSettingStore.getState(); let message = t("research.common.thinkingError"); const isFetchError = err instanceof TypeError && err.message === "Failed to fetch" || String(err).includes("Failed to fetch"); if (isFetchError) { if (mode === "local") { message = "Network Error: Failed to fetch. This is likely a CORS issue because the API provider blocks direct browser requests. Please open Settings and switch API Request Mode to 'Server Proxy'."; } else if (mode === "proxy") { message = "Failed to connect to server. Please check your network or refresh the page."; } } else if (err instanceof Error) { message += `: ${err.message}`; } toast.error(message, { duration: 8000 }); } finally { setIsThinking(false); accurateTimerStop(); } } } function createNewResearch() { const { id, backup, reset } = useTaskStore.getState(); const { update } = useHistoryStore.getState(); if (id) update(id, backup()); reset(); form.reset(); } function openKnowledgeList() { const { setOpenKnowledge } = useGlobalStore.getState(); setOpenKnowledge(true); } async function handleFileUpload(files: FileList | null) { if (files) { for await (const file of files) { await getKnowledgeFromFile(file); } // Clear the input file to avoid processing the previous file multiple times if (fileInputRef.current) { fileInputRef.current.value = ""; } } } useEffect(() => { form.setValue("topic", taskStore.question); }, [taskStore.question, form]); return (

{t("research.topic.title")}

(