Files
writer-work-flow/apps/web/lib/workbench/useContinue.ts
Yaojia Wang 59f2f60a7e refactor(frontend): 四生成 hook 结束后额外返回产物(AC-3 留痕前置)
useRefine.refine/recommunicate→RefineVersion|null、useContinue.generate→string|null、
useChapterRewrite.send→RewriteVersion|null、useGenerator.generate→GeneratorArtifact|null。
向后兼容(既有调用忽略返回值不受影响);各 hook 成功用例补返回值断言。
面板据此 if(artifact) 才 append,空候选/错误路径不留痕。
2026-07-09 17:15:49 +02:00

75 lines
2.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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 指定承接哪一章。返回本条候选文本,空/失败返回 null。
generate: (projectId: string, chapterNo: number) => Promise<string | null>;
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<ContinueStatus>("idle");
const [candidates, setCandidates] = useState<string[]>([]);
const toast = useToast();
const generate = useCallback<UseContinue["generate"]>(
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 null;
}
const text = extractText(data.preview).trim();
if (text.length === 0) {
setStatus("ready");
toast("本次未生成有效续写,请再试一次。", "info");
return null;
}
setCandidates((prev) => [...prev, text]);
setStatus("ready");
return text;
} catch {
setStatus("error");
toast("续写请求异常,请检查网络。", "error");
return null;
}
},
[toast],
);
const reset = useCallback((): void => {
setStatus("idle");
setCandidates([]);
}, []);
return { status, candidates, generate, reset };
}