- 冲突「采纳改法」:有补丁瞬时改入终稿并选中;无补丁但可定位则采纳时调 AI(refiner)重写该段套入;定位不到提示手改 - 正文改为行内高亮叠层编辑器(与页面同底色、随内容增高、无滚动条),点冲突卡/锚点整页滚到并选中+闪烁 - 仅对可在正文定位的冲突显示「跳转」/锚点;locate 支持从 where 引号抽原文,杜绝定位失败刷屏 - 伏笔建议卡:新埋一键登记(预填表单)/ 疑似回收一键标记 CLOSED(复用现有端点) - 审稿页草稿自动保存(防抖 PUT /draft),与「验收本章」解耦,刷新不丢 - 写作路由带章号 ?chapter=N + 验收成功面板/大纲页「写下一章」入口;写作目录从大纲列出全部章节 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
55 lines
1.9 KiB
TypeScript
55 lines
1.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
||
|
||
import { extractDraftQuotes, locateInDraft } from "./locate";
|
||
|
||
const TEXT = "第一段没问题。\n\n他乘坐迈巴赫扬长而去,留下一地尘土。\n\n结尾。";
|
||
|
||
describe("locateInDraft", () => {
|
||
it("locates the exact original fragment", () => {
|
||
const loc = locateInDraft(TEXT, { original: "迈巴赫", where: "第2段" });
|
||
expect(loc).not.toBeNull();
|
||
expect(TEXT.slice(loc!.start, loc!.end)).toBe("迈巴赫");
|
||
});
|
||
|
||
it("falls back to the paragraph snippet when no original", () => {
|
||
const loc = locateInDraft(TEXT, { original: null, where: "第2段" });
|
||
expect(loc).not.toBeNull();
|
||
expect(TEXT.slice(loc!.start, loc!.end)).toBe(
|
||
"他乘坐迈巴赫扬长而去,留下一地尘土。",
|
||
);
|
||
});
|
||
|
||
it("returns null when original is absent from text and no paragraph hint", () => {
|
||
expect(
|
||
locateInDraft(TEXT, { original: "保时捷", where: "无段号" }),
|
||
).toBeNull();
|
||
});
|
||
|
||
it("returns null when the snippet paragraph is out of range", () => {
|
||
expect(locateInDraft(TEXT, { original: null, where: "第9段" })).toBeNull();
|
||
});
|
||
|
||
it("locates via quoted draft text inside where (no original, no 段号)", () => {
|
||
// 真实形态:where = 草稿中写"<草稿原文>",引号内即正文精确子串。
|
||
const loc = locateInDraft(TEXT, {
|
||
original: null,
|
||
where: '草稿中写"迈巴赫扬长而去"',
|
||
});
|
||
expect(loc).not.toBeNull();
|
||
expect(TEXT.slice(loc!.start, loc!.end)).toBe("迈巴赫扬长而去");
|
||
});
|
||
});
|
||
|
||
describe("extractDraftQuotes", () => {
|
||
it("extracts quoted spans (multiple quote styles) longest-first, deduped", () => {
|
||
expect(extractDraftQuotes('草稿中写"长一些的原文",又如「短的」')).toEqual([
|
||
"长一些的原文",
|
||
"短的",
|
||
]);
|
||
});
|
||
|
||
it("returns empty when there are no quotes", () => {
|
||
expect(extractDraftQuotes("第3段,主角忽然示弱")).toEqual([]);
|
||
});
|
||
});
|