// R1 · 冲突卡内联上下文片段(UX §6.4)。 // `where` 是 LLM 给的文字定位(如「第 3 段,主角忽然示弱」),M2 无精确字符 offset。 // 这里按段级回放:从 where 解析 1-based 段号 → 取终稿对应段 → 截断成预览。 // 纯逻辑,与 ReviewReport.segmentText 同口径(空行切段),便于 node 环境单测。 // 预览窗口最大字符数(命中段超长时截断,避免撑爆卡片)。 const PREVIEW_MAX = 80; // 从 where 文案解析 1-based 段号(「第 3 段」→ 3;范围「第 4–6 段」取首个)。无则 null。 export function parseParagraphNo(where: string): number | null { const match = where.match(/第\s*(\d+)/); if (!match) return null; const n = Number(match[1]); return Number.isInteger(n) && n > 0 ? n : null; } // 取终稿第 N 段(1-based)正文预览;无段号/越界/空段 → null(卡片回退到「跳转」按钮)。 export function conflictSnippet(finalText: string, where: string): string | null { const no = parseParagraphNo(where); if (no === null) return null; const paragraphs = finalText.split(/\n{2,}/); const paragraph = paragraphs[no - 1]?.trim(); if (!paragraph) return null; return paragraph.length <= PREVIEW_MAX ? paragraph : `${paragraph.slice(0, PREVIEW_MAX)}…`; }