diff --git a/apps/web/components/workbench/ChapterRewritePanel.tsx b/apps/web/components/workbench/ChapterRewritePanel.tsx new file mode 100644 index 0000000..edc889b --- /dev/null +++ b/apps/web/components/workbench/ChapterRewritePanel.tsx @@ -0,0 +1,130 @@ +"use client"; + +import { useState } from "react"; +import { Check, RotateCcw, Square, X } from "lucide-react"; + +import { ThinkingIndicator } from "@/components/ThinkingIndicator"; +import { Button } from "@/components/ui/Button"; +import { SectionHeader } from "@/components/ui/SectionHeader"; +import { StatusNote } from "@/components/ui/StatusNote"; +import { TextArea } from "@/components/ui/TextArea"; +import { friendlyError } from "@/lib/errors/messages"; +import { useChapterRewrite } from "@/lib/workbench/useChapterRewrite"; + +interface ChapterRewritePanelProps { + projectId: string; + chapterNo: number; + // 当前整章正文(首轮重写的 prior_draft)。 + currentDraft: string; + // 接受某一版 → 替换整章正文(HITL,作者显式点选才落草稿)。 + onAccept: (text: string) => void; + onClose: () => void; +} + +// 整章「再沟通 / 重写」结果卡(内联,非模态):作者给意见 → AI 就着当前整章 + 完整记忆注入 +// 流式重写一版 → 版本栈可多轮迭代 → 接受某版才替换整章正文。生成不写库(HITL,不变量 #3)。 +export function ChapterRewritePanel({ + projectId, + chapterNo, + currentDraft, + onAccept, + onClose, +}: ChapterRewritePanelProps) { + const rewrite = useChapterRewrite(); + const [feedback, setFeedback] = useState(""); + + const hasVersions = rewrite.versions.length > 0; + const canSend = feedback.trim().length > 0 && !rewrite.isStreaming; + // 下一轮以最新版正文为基础(无版本则用当前整章正文)迭代。 + const priorDraft = rewrite.latest?.text ?? currentDraft; + const err = rewrite.state.phase === "error" ? rewrite.state.error : null; + + const onSend = (): void => { + if (!canSend) return; + const fb = feedback.trim(); + setFeedback(""); + void rewrite.send(projectId, chapterNo, fb, priorDraft); + }; + + return ( +
+
+ + +
+ +
+