// 本章 AI写作三槽(纯逻辑,UX §3;写作工作台重构 Phase 2)。 // 三槽 = 文风预设 + 剧情需求预设 + 自定义要求(自由文本)→ 单条 directive 串, // 直通后端 volatile(MVP 纯前端、零契约、不动 write_craft.md 字节 → 金标准零回归)。 // 组件只渲染 chips/textarea;预设清单与组装规则集中在此,确定性、不可变。 export interface Preset { id: string; label: string; text: string; } // 文风预设(书级文风倾向;点选引导本章表达)。来源:现有 + style_extract 维度离散化。 export const STYLE_PRESETS: readonly Preset[] = [ { id: "less-ai", label: "减少AI味", text: "减少 AI 腔,去掉套话与排比堆砌" }, { id: "colloquial", label: "口语化", text: "用更口语、贴近人物的表达" }, { id: "fast-pace", label: "快节奏", text: "加快节奏,少铺垫多冲突推进" }, ]; // 剧情需求预设(章级桥段诉求;作者「卡文给方向」)。来源:plan §4.2 分类法 // (tianyayu6/write-web-novels + oh-story-claudecode,均 MIT)。 export const PLOT_PRESETS: readonly Preset[] = [ { id: "plot-faceslap", label: "打脸反转", text: "本章安排一次打脸/反转,先抑后扬给足爽感" }, { id: "plot-hook-end", label: "章末钩子", text: "章末留一个强钩子/悬念,驱动追读" }, { id: "plot-climax", label: "高潮爆发", text: "本章为高潮,冲突集中爆发、情绪拉满" }, { id: "plot-turn", label: "关键转折", text: "本章制造关键转折,改变人物处境或目标" }, { id: "plot-reveal", label: "扮猪吃虎", text: "先藏拙后亮实力,制造反差与压制感" }, ]; // 全部预设(文风在前、剧情在后)——组装按此声明序,保证输出稳定可测。 const ALL_PRESETS: readonly Preset[] = [...STYLE_PRESETS, ...PLOT_PRESETS]; const PRESET_BY_ID: ReadonlyMap = new Map( ALL_PRESETS.map((preset) => [preset.id, preset]), ); const SEPARATOR = ";"; // 组合选中预设文案(按 ALL_PRESETS 原序:文风→剧情)+ 自定义自由文本为一条指令。空 → ""。 export function composeDirective( freeText: string, presetIds: readonly string[], ): string { const selected = new Set(presetIds); const presetTexts = ALL_PRESETS.filter((preset) => selected.has(preset.id), ).map((preset) => preset.text); const trimmedFree = freeText.trim(); const parts = trimmedFree ? [...presetTexts, trimmedFree] : presetTexts; return parts.join(SEPARATOR); } // 仅暴露给测试/组件的辅助:id 是否为已知预设。 export function isKnownPreset(id: string): boolean { return PRESET_BY_ID.has(id); }