"use client";
import { Minus, Pin, PinOff, Plus, RotateCcw, Undo2, 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 { 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 (
) : null
}
/>
) : null
}
/>
{injection.loading ? (
加载参考信息…
) : !data ? (
// 首次加载失败:data 仍为空 → 满屏错误框 + 重试。
请检查后端连接后重试。
) : (
<>
{injection.saveError ? (
// 保存失败:悬浮提示,不替换列表;可关闭 + 重试上一次覆盖。
) : null}
{selected.length === 0 ? (
这一章 AI 还没有要参考的设定/角色。可在大纲里点名,或从下方「已排除」里恢复。
) : (
{selected.map((entity) => (
))}
)}
>
)}
{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}
章
);
}