// 一键采纳改法:把审稿提议的补丁(original→replacement)应用到终稿。 // 纯逻辑(可单测,不依赖 DOM)。粒度=最小句段;找不到原文则不动正文(作者手改)。 export type FixOutcome = | { status: "applied"; text: string } // 命中原文并替换:text=改后终稿 | { status: "not-found"; text: string } // 有补丁但终稿里找不到原文(作者已改稿/漂移):text 原样 | { status: "no-patch"; text: string }; // 该冲突无可自动应用的补丁:text 原样 // 已确认可应用的补丁(original/replacement 均为 string)。 export interface ApplicablePatch { original: string; replacement: string; } // 松散补丁入参:original/replacement 可能缺失/为 null(审稿可局部修复时才带上)。 interface LoosePatch { original: string | null | undefined; replacement: string | null | undefined; } // 是否带可应用补丁:original 非空 + replacement 为字符串(容许空串=删除)。 // 类型谓词:命中即把 original/replacement 收窄为 string,调用方无需强转。 export function hasApplicableFix(patch: LoosePatch): patch is ApplicablePatch { return ( typeof patch.original === "string" && patch.original.length > 0 && typeof patch.replacement === "string" ); } // 把首个匹配的 original 替换为 replacement(不可变:返回新串,不改入参)。 // 无补丁 → no-patch;有补丁但未命中 → not-found(终稿不变);命中 → applied。 export function applyConflictFix( text: string, original: string | null | undefined, replacement: string | null | undefined, ): FixOutcome { const patch: LoosePatch = { original, replacement }; if (!hasApplicableFix(patch)) { return { status: "no-patch", text }; } // 谓词已把 patch 收窄为 ApplicablePatch,original/replacement 皆为 string。 const idx = text.indexOf(patch.original); if (idx === -1) { return { status: "not-found", text }; } return { status: "applied", text: text.slice(0, idx) + patch.replacement + text.slice(idx + patch.original.length), }; }