Phase 1(写作工作台重构):编辑器选中一段即可「润色选段」,复用同步 /refine 回炉 (只读、不写库,守不变量 #3);不满意可「再沟通」——以上一版产出为输入 + 作者新意见 再改一版,形成版本栈(useRefine,已单测)。接受回填按内容重锚落回草稿,原文已变则 提示重选、绝不盲替换(applyRefinement,已单测)。Editor 上报选区,结果以内联卡片展示。
38 lines
1.4 KiB
TypeScript
38 lines
1.4 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
|
|
import { applyRefinement } from "./refineApply";
|
|
|
|
describe("applyRefinement", () => {
|
|
const full = "第一段。原文段落。第三段。";
|
|
const original = "原文段落。";
|
|
const refined = "润色后的段落。";
|
|
// "第一段。" = 4 字,原文段落在 [4, 4+len)
|
|
const range = { start: 4, end: 4 + original.length };
|
|
|
|
it("选区未漂移:按 range 原地替换", () => {
|
|
expect(applyRefinement(full, original, refined, range)).toBe(
|
|
"第一段。润色后的段落。第三段。",
|
|
);
|
|
});
|
|
|
|
it("选区漂移但原文唯一:按内容重锚替换", () => {
|
|
// range 指向错位(如流式追加导致偏移),但原文仍能唯一定位。
|
|
const drifted = { start: 0, end: 3 };
|
|
expect(applyRefinement(full, original, refined, drifted)).toBe(
|
|
"第一段。润色后的段落。第三段。",
|
|
);
|
|
});
|
|
|
|
it("原文已不存在(正文被大改):返回 null 让调用方提示重选", () => {
|
|
expect(applyRefinement("完全不同的正文", original, refined, range)).toBeNull();
|
|
});
|
|
|
|
it("只替换首个匹配(重锚回退时保守取第一处)", () => {
|
|
const twice = "原文段落。中间。原文段落。";
|
|
const drifted = { start: 99, end: 100 };
|
|
expect(applyRefinement(twice, original, refined, drifted)).toBe(
|
|
"润色后的段落。中间。原文段落。",
|
|
);
|
|
});
|
|
});
|