- Q6 bug: useAutosave 加 initialSavedText,进页种入已存草稿时初始即「已保存」,修「加载已存草稿却显示尚未保存」的误导。 - A: 新 ThinkingIndicator(三点波动,motion-safe 尊重 reduced-motion)接入审稿进行中/验收中/回炉中三处静态文案,长等待看得见在工作。
103 lines
3.3 KiB
TypeScript
103 lines
3.3 KiB
TypeScript
"use client";
|
||
|
||
import { useRefine } from "@/lib/style/useRefine";
|
||
import { useEffect } from "react";
|
||
|
||
import { ThinkingIndicator } from "@/components/ThinkingIndicator";
|
||
|
||
interface RefineViewProps {
|
||
projectId: string;
|
||
chapterNo: number;
|
||
// 选中漂移段的原文(从终稿按段切出;空则提示先编辑终稿)。
|
||
segment: string;
|
||
// 采纳:把 refined 合入终稿(经审稿页 finalText → 既有 draft 自动保存路径)。
|
||
onAdopt: (original: string, refined: string) => void;
|
||
onClose: () => void;
|
||
}
|
||
|
||
// 回炉 diff(UX §8.3):POST .../refine → 展示原段/重写段对比 → 采纳合入正文。
|
||
// 不另写库(不变量#3);采纳经既有 draft 自动保存合入。
|
||
export function RefineView({
|
||
projectId,
|
||
chapterNo,
|
||
segment,
|
||
onAdopt,
|
||
onClose,
|
||
}: RefineViewProps) {
|
||
const refiner = useRefine();
|
||
|
||
// 进入即触发回炉(段非空)。
|
||
useEffect(() => {
|
||
if (segment.trim().length > 0) {
|
||
void refiner.refine(projectId, chapterNo, segment);
|
||
}
|
||
// 仅在段变化时触发。
|
||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||
}, [segment, projectId, chapterNo]);
|
||
|
||
return (
|
||
<div
|
||
className="rounded border border-cinnabar bg-panel p-3 text-xs"
|
||
role="dialog"
|
||
aria-label="回炉对比"
|
||
>
|
||
<div className="mb-2 flex items-center justify-between">
|
||
<h3 className="font-semibold text-ink">回炉对比</h3>
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="text-ink-soft hover:text-cinnabar"
|
||
aria-label="关闭回炉对比"
|
||
>
|
||
✕
|
||
</button>
|
||
</div>
|
||
|
||
{segment.trim().length === 0 ? (
|
||
<p className="text-overdue">该段在终稿中为空,请先在终稿中保留该段正文。</p>
|
||
) : refiner.status === "refining" ? (
|
||
<p className="text-info">
|
||
<ThinkingIndicator label="回炉中" />
|
||
</p>
|
||
) : refiner.status === "error" ? (
|
||
<p className="text-conflict">回炉失败,请稍后重试。</p>
|
||
) : refiner.result ? (
|
||
<div className="space-y-2">
|
||
<div>
|
||
<p className="mb-1 text-ink-soft">原段</p>
|
||
<p className="whitespace-pre-wrap rounded border border-line bg-bg p-2 text-ink-soft line-through">
|
||
{refiner.result.original}
|
||
</p>
|
||
</div>
|
||
<div>
|
||
<p className="mb-1 text-cinnabar">重写段</p>
|
||
<p className="whitespace-pre-wrap rounded border border-cinnabar bg-bg p-2 text-ink">
|
||
{refiner.result.refined}
|
||
</p>
|
||
</div>
|
||
<div className="flex gap-2">
|
||
<button
|
||
type="button"
|
||
onClick={() => {
|
||
if (refiner.result) {
|
||
onAdopt(refiner.result.original, refiner.result.refined);
|
||
}
|
||
}}
|
||
className="rounded bg-cinnabar px-3 py-1.5 text-panel hover:opacity-90"
|
||
>
|
||
采纳合入正文
|
||
</button>
|
||
<button
|
||
type="button"
|
||
onClick={onClose}
|
||
className="rounded border border-line px-3 py-1.5 text-ink hover:border-cinnabar"
|
||
>
|
||
放弃
|
||
</button>
|
||
</div>
|
||
</div>
|
||
) : null}
|
||
</div>
|
||
);
|
||
}
|