"use client"; import { useCallback, useMemo, useState } from "react"; import { api } from "@/lib/api/client"; import { useToast } from "@/components/Toast"; // 编辑器内「润色 / 再沟通」核心(复用同步 /refine,只读、不写库——不变量 #3)。 // 润色:对选中段回炉一次。再沟通:以上一版产出为输入 + 作者新意见再回炉,形成版本栈, // 作者可回看每一版、择一 accept 回填正文(accept/回填由组件负责,此处只管生成与版本历史)。 export interface RefineVersion { // 本次回炉的输入段:首轮 = 选中原文;再沟通 = 上一版产出。 segment: string; // 本次改写指令(再沟通时为作者的新意见);无则空串。 instruction: string; refined: string; } export type RefineStatus = "idle" | "refining" | "ready" | "error"; export interface UseRefine { status: RefineStatus; versions: RefineVersion[]; // 版本栈顶(最新产出)。 latest: RefineVersion | null; // 最初选中的原文(accept 回填的对比基线)。 original: string | null; // 回炉并返回本版产出(供调用方在结束后留痕);失败返回 null。 refine: ( projectId: string, chapterNo: number, segment: string, instruction?: string, ) => Promise; // 以上一版产出为输入 + 作者新意见再回炉一次。无历史版本时为空操作(返回 null)。 recommunicate: ( projectId: string, chapterNo: number, instruction: string, ) => Promise; reset: () => void; } export function useRefine(): UseRefine { const [status, setStatus] = useState("idle"); const [versions, setVersions] = useState([]); const toast = useToast(); const runRefine = useCallback( async ( projectId: string, chapterNo: number, segment: string, instruction: string, ): Promise => { setStatus("refining"); try { const trimmed = instruction.trim(); const { data, error } = await api.POST( "/projects/{project_id}/chapters/{chapter_no}/refine", { params: { path: { project_id: projectId, chapter_no: chapterNo } }, body: trimmed ? { segment, instruction: trimmed } : { segment }, }, ); if (error || !data) { setStatus("error"); toast("回炉失败,请重试。", "error"); return null; } const version: RefineVersion = { segment, instruction: trimmed, refined: data.refined, }; setVersions((prev) => [...prev, version]); setStatus("ready"); return version; } catch { setStatus("error"); toast("回炉请求异常,请检查网络。", "error"); return null; } }, [toast], ); const refine = useCallback( (projectId, chapterNo, segment, instruction) => runRefine(projectId, chapterNo, segment, instruction ?? ""), [runRefine], ); const recommunicate = useCallback( (projectId, chapterNo, instruction) => { const last = versions[versions.length - 1]; // 无历史版本 → 无从「再沟通」;空操作,不打后端。 if (!last) return Promise.resolve(null); return runRefine(projectId, chapterNo, last.refined, instruction); }, [runRefine, versions], ); const reset = useCallback((): void => { setStatus("idle"); setVersions([]); }, []); const latest = versions.at(-1) ?? null; const original = versions[0]?.segment ?? null; return useMemo( () => ({ status, versions, latest, original, refine, recommunicate, reset }), [status, versions, latest, original, refine, recommunicate, reset], ); }