feat(frontend): 编辑器内「润色 / 再沟通」——选段回炉 + 追加意见迭代出版本栈,接受回填
Phase 1(写作工作台重构):编辑器选中一段即可「润色选段」,复用同步 /refine 回炉 (只读、不写库,守不变量 #3);不满意可「再沟通」——以上一版产出为输入 + 作者新意见 再改一版,形成版本栈(useRefine,已单测)。接受回填按内容重锚落回草稿,原文已变则 提示重选、绝不盲替换(applyRefinement,已单测)。Editor 上报选区,结果以内联卡片展示。
This commit is contained in:
37
apps/web/lib/workbench/refineApply.test.ts
Normal file
37
apps/web/lib/workbench/refineApply.test.ts
Normal file
@@ -0,0 +1,37 @@
|
||||
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(
|
||||
"润色后的段落。中间。原文段落。",
|
||||
);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user