"use client"; import Link from "next/link"; import { RotateCcw, X } from "lucide-react"; import { StatusNote } from "@/components/ui/StatusNote"; import { buttonClass } from "@/lib/ui/variants"; import { isPinned, kindLabel, reasonLabel, RECENT_N_MAX, RECENT_N_MIN, type InjectionEntity, type InjectionEntityRef, } from "@/lib/workbench/injection"; import { useInjection } from "@/lib/workbench/useInjection"; interface ChapterAssistantProps { projectId: string; chapterNo: number; } // 右栏「本章助手」(UX §6.3)。 // 「本章注入(透明)」读 B0 端点,列出确定性选中的设定/角色 + 入选理由徽标, // 并让作者 📌置顶 / ✕排除 / 调近 N 章(可控版)——「看到的=写章用的」信任牌(不变量 #6)。 // 桌面 aside 包裹;移动端经 Workbench 抽屉复用 AssistantContent。 export function ChapterAssistant({ projectId, chapterNo }: ChapterAssistantProps) { return ( ); } export function AssistantContent({ projectId, chapterNo }: ChapterAssistantProps) { const injection = useInjection(projectId, chapterNo); const data = injection.data; const selected = data?.selected ?? []; const excluded = data?.excluded ?? []; return (

本章助手

{injection.saving ? ( 保存中… ) : null}

本章注入(透明)

{data ? ( ) : null}
{injection.loading ? (

加载注入信息…

) : !data ? ( // 首次加载失败:data 仍为空 → 满屏错误框 + 重试。

请检查后端连接后重试。

) : ( <> {injection.saveError ? ( // 保存失败:悬浮提示,不替换列表;可关闭 + 重试上一次覆盖。
) : null} {selected.length === 0 ? (

本章暂无注入实体(先在大纲点名设定/角色,或从下方排除项恢复)。

) : ( )} )} {excluded.length > 0 ? (

已排除(不注入)

    {excluded.map((ref) => ( ))}
) : null}

四审

一致性 / 伏笔 / 文风 / 节奏,在审稿页逐条裁决。

去审稿 →
); } interface EntityRowProps { entity: InjectionEntity; pinned: boolean; disabled: boolean; onTogglePin: (ref: InjectionEntityRef) => void; onExclude: (ref: InjectionEntityRef) => void; } function EntityRow({ entity, pinned, disabled, onTogglePin, onExclude }: EntityRowProps) { const ref: InjectionEntityRef = { kind: entity.kind, name: entity.name }; return (
  • {kindLabel(entity.kind)} {entity.name}
    {(entity.reasons ?? []).map((reason) => ( {reasonLabel(reason)} ))}
  • ); } interface ExcludedRowProps { refItem: InjectionEntityRef; disabled: boolean; onRestore: (ref: InjectionEntityRef) => void; } function ExcludedRow({ refItem, disabled, onRestore }: ExcludedRowProps) { return (
  • {kindLabel(refItem.kind)} {refItem.name}
  • ); } interface RecentStepperProps { value: number; disabled: boolean; onChange: (n: number) => void; } // 「关联近 N 章」步进器:覆盖近况摘要回看章数(钳在 [MIN, MAX])。 function RecentStepper({ value, disabled, onChange }: RecentStepperProps) { return (
    {value}
    ); }