feat(frontend): 写作工作台补齐三项——内容感知书名 / 上下文速查抽屉 / genre采集
写作工作台重构剩余 3 项(多 agent 并行构建各自新文件,主线统一集成): - WFW-5 内容感知书名(#6):buildManuscriptExcerpt 取正文首尾摘录(硬上界控 token); GeneratorRunner 加 manuscriptText,有 brief 字段时出现「用当前正文」按钮把摘录填入 brief,让 book-title 等据正文反推。纯前端零后端;HITL——只填表单,生成仍需作者触发。 - WFW-6 上下文速查抽屉(#7):ContextDrawer 视口无关右侧 slide-over,设定库/大纲完整 只读速查 + 伏笔/规则/文风摘要跳链;底栏「速查」按钮触发。加法式、CONTEXT_DRAWER_ENABLED 一键回滚、旧侧栏/全宽页路由全保留。 - WFW-7 genre 采集 + 无大纲降级:项目无题材时首次写章前弹 GenrePicker 采集(临时并入 本次 directive 不落库);chapters 为空时编辑器显式提示「尚无大纲,将按设定自由生成」。 各项 TDD:manuscriptExcerpt/useGenreGate/contextDrawer/useContextDrawer 共 32 新单测。 门禁全绿:vitest 629 / tsc / lint / build / coverage 95.43%。
This commit is contained in:
31
apps/web/lib/workbench/manuscriptExcerpt.ts
Normal file
31
apps/web/lib/workbench/manuscriptExcerpt.ts
Normal file
@@ -0,0 +1,31 @@
|
||||
// 从作者【已写正文】抽一段代表性摘录,喂给内容感知型生成器(如书名反推)的 brief 字段。
|
||||
// 纯函数、无副作用;硬上界 maxChars 控 token,保证首尾都进入摘录。
|
||||
|
||||
/** 默认摘录上界(字符数),约束喂给 LLM 的 token 体量。 */
|
||||
export const DEFAULT_EXCERPT_MAX_CHARS = 1800;
|
||||
|
||||
/** 首尾之间插入的省略标记,提示中间正文被截去。 */
|
||||
const ELLIPSIS_MARKER = "\n\n……(中略)……\n\n";
|
||||
|
||||
/**
|
||||
* 取正文的代表性摘录:
|
||||
* - 空/纯空白 → 返回 ""。
|
||||
* - 长度 ≤ maxChars → 原样返回(trim 后)。
|
||||
* - 超界 → 取「开头一段 + 结尾一段」拼接,中间插省略标记,保证首尾都在。
|
||||
* 正文预算按 maxChars 均分给首尾两段;返回长度 ≤ maxChars + 标记长度。
|
||||
*/
|
||||
export function buildManuscriptExcerpt(
|
||||
fullText: string,
|
||||
maxChars: number = DEFAULT_EXCERPT_MAX_CHARS,
|
||||
): string {
|
||||
const trimmed = fullText.trim();
|
||||
if (trimmed.length === 0) return "";
|
||||
if (trimmed.length <= maxChars) return trimmed;
|
||||
|
||||
// 正文预算(不含标记)均分给首尾,向下取整避免超界。
|
||||
const headBudget = Math.floor(maxChars / 2);
|
||||
const tailBudget = maxChars - headBudget;
|
||||
const head = trimmed.slice(0, headBudget).trimEnd();
|
||||
const tail = trimmed.slice(trimmed.length - tailBudget).trimStart();
|
||||
return `${head}${ELLIPSIS_MARKER}${tail}`;
|
||||
}
|
||||
Reference in New Issue
Block a user