F12: inline editable proposed-change diff in the Markdown editor + Accept/Reject control parity (#64) #66
@@ -828,6 +828,17 @@ export function renderReview(
|
|||||||
return curOff + delta;
|
return curOff + delta;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// F12/#64 (INV-49/50): blocks are split from `landedText`, so `blk.start` is a
|
||||||
|
// landedText offset — but `authorSpans` arrive in `currentText` coordinates. A
|
||||||
|
// colored block sitting AFTER a length-changing pending proposal would otherwise
|
||||||
|
// be mis-colored (the offsets diverge by the revert delta). Map the spans into
|
||||||
|
// landedText coordinates once so authorship coloring stays aligned.
|
||||||
|
const landedSpans: AuthorSpan[] = authorSpans.map((s) => ({
|
||||||
|
...s,
|
||||||
|
start: toLanded(s.start),
|
||||||
|
end: toLanded(s.end),
|
||||||
|
}));
|
||||||
|
|
||||||
// Associate each resolved proposal with the landedText block index whose range
|
// Associate each resolved proposal with the landedText block index whose range
|
||||||
// it anchors into: the largest block with start <= anchorStart (the containing
|
// it anchors into: the largest block with start <= anchorStart (the containing
|
||||||
// block, or the nearest preceding block when the anchor sits in a gap). A
|
// block, or the nearest preceding block when the anchor sits in a gap). A
|
||||||
@@ -857,7 +868,7 @@ export function renderReview(
|
|||||||
const blockIndex = op.kind === "removed" ? -1 : ci;
|
const blockIndex = op.kind === "removed" ? -1 : ci;
|
||||||
const blk = op.kind === "removed" ? undefined : ranges[ci++];
|
const blk = op.kind === "removed" ? undefined : ranges[ci++];
|
||||||
const colored = (raw: string): string =>
|
const colored = (raw: string): string =>
|
||||||
blk && !clean ? colorByAuthor(raw, blk.start, authorSpans, render) : render(raw);
|
blk && !clean ? colorByAuthor(raw, blk.start, landedSpans, render) : render(raw);
|
||||||
bodyParts.push(renderReviewOp(op, render, colored, srcAttr(blk)));
|
bodyParts.push(renderReviewOp(op, render, colored, srcAttr(blk)));
|
||||||
const here = blockIndex >= 0 ? byBlock.get(blockIndex) : undefined;
|
const here = blockIndex >= 0 ? byBlock.get(blockIndex) : undefined;
|
||||||
if (here) for (const p of here) bodyParts.push(proposalBlockHtml(p, render));
|
if (here) for (const p of here) bodyParts.push(proposalBlockHtml(p, render));
|
||||||
|
|||||||
@@ -392,6 +392,26 @@ describe("renderReview", () => {
|
|||||||
// the applied paragraph is NOT also emitted as a word-merged changed block
|
// the applied paragraph is NOT also emitted as a word-merged changed block
|
||||||
expect(html).not.toContain("<del>brown</del>"); // no double-render of the change as a baseline diff
|
expect(html).not.toContain("<del>brown</del>"); // no double-render of the change as a baseline diff
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test("renderReview keeps author coloring aligned on a block AFTER a length-changing pending proposal (INV-49/50)", () => {
|
||||||
|
// block 1 has a pending proposal whose applied text is MUCH LONGER than its
|
||||||
|
// original; block 2 carries a Claude authorship span (in currentText coords).
|
||||||
|
const baseline = "one short.\n\ntwo stable here.\n";
|
||||||
|
const current = "one MUCH LONGER REPLACED TEXT.\n\ntwo stable here.\n";
|
||||||
|
const proposals: ProposalView[] = [{
|
||||||
|
id: "pr_1",
|
||||||
|
anchorStart: current.indexOf("one MUCH LONGER REPLACED TEXT."),
|
||||||
|
anchorEnd: current.indexOf("one MUCH LONGER REPLACED TEXT.") + "one MUCH LONGER REPLACED TEXT.".length,
|
||||||
|
replaced: "one short.",
|
||||||
|
replacement: "one MUCH LONGER REPLACED TEXT.",
|
||||||
|
original: "one short.",
|
||||||
|
}];
|
||||||
|
const sStart = current.indexOf("stable");
|
||||||
|
const authorSpans = [{ start: sStart, end: sStart + "stable".length, author: "claude" as const }];
|
||||||
|
const html = renderReview(baseline, current, authorSpans, proposals);
|
||||||
|
// the Claude coloring wraps "stable" exactly — not shifted by the proposal's length delta.
|
||||||
|
expect(html).toMatch(/<span class="cw-by-claude">stable<\/span>/);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
import { renderTrackChanges as rtc2 } from "../src/trackChangesModel";
|
import { renderTrackChanges as rtc2 } from "../src/trackChangesModel";
|
||||||
|
|||||||
Reference in New Issue
Block a user