"use client"; import { useCallback, useState } from "react"; import { api } from "@/lib/api/client"; import { useToast } from "@/components/Toast"; import { generationErrorMessage } from "@/lib/generation/cards"; // 编辑器「续写」:复用 continue 生成器(with_prior_chapter,自动读该章已写正文承接), // 每次生成一条候选并累加成多候选,作者点选某条插入正文(HITL,不静默写入)。 export type ContinueStatus = "idle" | "generating" | "ready" | "error"; export interface UseContinue { status: ContinueStatus; candidates: string[]; // 生成一条续写候选(累加,不覆盖);chapterNo 指定承接哪一章。 generate: (projectId: string, chapterNo: number) => Promise; reset: () => void; } function extractText(preview: unknown): string { if (preview && typeof preview === "object" && "text" in preview) { const text = (preview as { text: unknown }).text; return typeof text === "string" ? text : ""; } return ""; } export function useContinue(): UseContinue { const [status, setStatus] = useState("idle"); const [candidates, setCandidates] = useState([]); const toast = useToast(); const generate = useCallback( async (projectId, chapterNo) => { setStatus("generating"); try { const { data, error } = await api.POST( "/projects/{project_id}/skills/{tool_key}/generate", { params: { path: { project_id: projectId, tool_key: "continue" } }, body: { brief: "", chapter_no: chapterNo }, }, ); if (error || !data) { setStatus("error"); toast(generationErrorMessage(error), "error"); return; } const text = extractText(data.preview).trim(); if (text.length === 0) { setStatus("ready"); toast("本次未生成有效续写,请再试一次。", "info"); return; } setCandidates((prev) => [...prev, text]); setStatus("ready"); } catch { setStatus("error"); toast("续写请求异常,请检查网络。", "error"); } }, [toast], ); const reset = useCallback((): void => { setStatus("idle"); setCandidates([]); }, []); return { status, candidates, generate, reset }; }