feat(frontend): 五类交互接入 AI 对话留痕 + Workbench 抽屉入口(AC-3)
- RefinePanel/ChapterRewritePanel:结束后 append [author(意见/答复), ai(产出)];
clarify buffer-until-concluded——澄清 Q&A(原意见→反问→答复)缓冲,随最终产出批
一起落库同 thread;纯放弃不记。refine ai 行 meta 存 version_no+原选段(每版重锚)。
- ContinuePanel:ai-only(续写无作者输入),每候选一条 ai 气泡 meta{candidate_index}。
- GeneratorRunner:成功后 append [author(字段摘要, meta{tool_key,input_fields}),
ai(渲染预览, meta{tool_key,output_kind})];空产物不记。lib/toolbox/aiSummary.ts 纯助手+单测。
- InlineToolbox→GeneratorRunner 透传 append+本章号;ToolboxPage 用 silent 直投项目级(chapter_no=null)。
- Workbench:挂 useAiConversation 单实例 + AiConversationDrawer + 底栏「AI 对话」入口(flag 灰度),
三个 DRAFT-only 再接受回调(替换整章/插入正文/回填选段),成功关抽屉+toast、漂移不盲替换。
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { useCallback, useMemo, useState } from "react";
|
||||
import { useCallback, useMemo, useRef, useState } from "react";
|
||||
import { ArrowLeft, Database, Sparkles } from "lucide-react";
|
||||
|
||||
import { useToast } from "@/components/Toast";
|
||||
@@ -23,8 +23,10 @@ import {
|
||||
type PreviewItem,
|
||||
} from "@/lib/toolbox/toolbox";
|
||||
import { ingestTable, isSingleObjectIngest } from "@/lib/toolbox/ingest";
|
||||
import { fieldSummary, renderPreviewText } from "@/lib/toolbox/aiSummary";
|
||||
import { buildManuscriptExcerpt } from "@/lib/workbench/manuscriptExcerpt";
|
||||
import { useGenerator } from "@/lib/toolbox/useGenerator";
|
||||
import type { AiTurnInput } from "@/lib/workbench/useAiConversation";
|
||||
import { TemplateFiller } from "./TemplateFiller";
|
||||
|
||||
interface GeneratorRunnerProps {
|
||||
@@ -36,6 +38,10 @@ interface GeneratorRunnerProps {
|
||||
// 可选:作者【已写正文】。传入且当前工具有 brief 字段时,表单里出现「用当前正文」按钮,
|
||||
// 点击把正文摘录填进 brief,让内容感知型生成器(如书名)据正文反推。工具箱页不传 → 不显示。
|
||||
manuscriptText?: string;
|
||||
// 可选留痕:生成成功后 append 一批 [author(字段摘要), ai(预览文本)]。
|
||||
// 内联工具箱传 conversation.appendMessages + 当前章号;工具箱页传 silent 直投 + chapterNo=null。
|
||||
appendMessages?: (turn: AiTurnInput) => void;
|
||||
logChapterNo?: number | null;
|
||||
}
|
||||
|
||||
// 声明驱动的通用生成器(核心):按 descriptor.input_fields 渲染表单 → 调通用 generate →
|
||||
@@ -47,6 +53,8 @@ export function GeneratorRunner({
|
||||
onClose,
|
||||
onInsertText,
|
||||
manuscriptText,
|
||||
appendMessages,
|
||||
logChapterNo,
|
||||
}: GeneratorRunnerProps) {
|
||||
const gen = useGenerator();
|
||||
const toast = useToast();
|
||||
@@ -55,6 +63,8 @@ export function GeneratorRunner({
|
||||
);
|
||||
// 入库勾选:预览行下标集合(与 gen.rawPreview 行对齐)。
|
||||
const [selected, setSelected] = useState<Set<number>>(new Set());
|
||||
// 本 runner 一次会话 = 一个 thread;多次生成归此组。
|
||||
const threadId = useRef(crypto.randomUUID()).current;
|
||||
|
||||
const setField = useCallback((name: string, value: string): void => {
|
||||
setValues((prev) => ({ ...prev, [name]: value }));
|
||||
@@ -99,8 +109,36 @@ export function GeneratorRunner({
|
||||
return;
|
||||
}
|
||||
setSelected(new Set());
|
||||
await gen.generate(projectId, tool.key, buildGenerateRequest(values));
|
||||
}, [gen, projectId, tool, values, toast]);
|
||||
const artifact = await gen.generate(
|
||||
projectId,
|
||||
tool.key,
|
||||
buildGenerateRequest(values),
|
||||
);
|
||||
// 留痕(成功且有可读预览时):author(字段摘要) + ai(渲染预览);空产物/错误路径不记。
|
||||
if (artifact && appendMessages) {
|
||||
const aiText = renderPreviewText(artifact.preview);
|
||||
if (aiText.length > 0) {
|
||||
appendMessages({
|
||||
threadId,
|
||||
kind: "generator",
|
||||
toolKey: tool.key,
|
||||
chapterNo: logChapterNo ?? null,
|
||||
messages: [
|
||||
{
|
||||
role: "author",
|
||||
content: fieldSummary(tool.input_fields ?? [], values) || tool.title,
|
||||
meta: { tool_key: tool.key, input_fields: values },
|
||||
},
|
||||
{
|
||||
role: "ai",
|
||||
content: aiText,
|
||||
meta: { tool_key: tool.key, output_kind: artifact.outputKind },
|
||||
},
|
||||
],
|
||||
});
|
||||
}
|
||||
}
|
||||
}, [gen, projectId, tool, values, toast, appendMessages, logChapterNo, threadId]);
|
||||
|
||||
const rows = useMemo(
|
||||
() => previewRows(gen.rawPreview),
|
||||
|
||||
Reference in New Issue
Block a user