fix(app): stabilize diff reviews and workspace tabs

Reuse measured diff geometry when review controls open, keeping the interaction responsive on large changes. Preserve complete tab-track snapshots while exact content widths settle so new tabs and retained workspaces do not flash or truncate.
This commit is contained in:
Mohamed Boudra
2026-08-17 21:14:38 +02:00
parent c7b103a2cd
commit 8f7b3e8d75
10 changed files with 433 additions and 96 deletions
@@ -272,6 +272,54 @@ describe("diff document model", () => {
expect(reviewRow?.kind === "line" ? reviewRow.reviewHeight : 0).toBe(148);
});
it("reflows review geometry without remeasuring unchanged text", () => {
const files = [file("a.ts"), file("b.ts")];
let measurementCount = 0;
const countingMeasurer: TextMeasurer = {
measure(text) {
measurementCount += 1;
return Array.from(text).length * 10;
},
};
const base = buildDiffDocumentModel(input({ files, measureText: countingMeasurer }));
const initialMeasurementCount = measurementCount;
const reviewCell = base.rows
.filter((row) => row.kind === "line")
.flatMap((row) => row.cells)
.find((cell) => cell?.reviewTarget);
if (!reviewCell?.reviewTarget) throw new Error("Expected a reviewable cell");
const baseReviewRow = rowForCell(base, reviewCell);
const baseSecondFileTop = base.files[1]!.top;
const withEditor = buildDiffDocumentModel(
input({
files,
measureText: countingMeasurer,
reviewActions: reviewActionsWithEditor(reviewCell.reviewTarget),
reuseFrom: [base],
}),
);
const editorRow = rowForReviewTarget(withEditor, reviewCell.reviewTarget.key);
expect(measurementCount).toBe(initialMeasurementCount);
expect(editorRow.reviewHeight).toBe(148);
expect(editorRow.cells).toBe(baseReviewRow.cells);
expect(withEditor.files[1]!.top).toBe(baseSecondFileTop + 148);
const withoutEditor = buildDiffDocumentModel(
input({
files,
measureText: countingMeasurer,
reviewActions: reviewActionsWithEditor(null),
reuseFrom: [base, withEditor],
}),
);
expect(measurementCount).toBe(initialMeasurementCount);
expect(withoutEditor.height).toBe(base.height);
expect(withoutEditor.files[1]!.top).toBe(baseSecondFileTop);
});
it("preserves a logical source anchor through width and font metric relayout", () => {
const narrow = buildDiffDocumentModel(
input({ files: [fileWithChangedLine("abcdefghij")], viewportWidth: 90 }),
@@ -492,3 +540,26 @@ function rowForCell(
if (!row || row.kind !== "line") throw new Error("Expected the cell row");
return row;
}
function rowForReviewTarget(model: ReturnType<typeof buildDiffDocumentModel>, key: string) {
const row = model.rows.find(
(candidate) =>
candidate.kind === "line" && candidate.cells.some((cell) => cell?.reviewTarget?.key === key),
);
if (!row || row.kind !== "line") throw new Error("Expected the review row");
return row;
}
function reviewActionsWithEditor(
target: NonNullable<ReturnType<typeof addedCell>["reviewTarget"]> | null,
): NonNullable<BuildDiffDocumentModelInput["reviewActions"]> {
return {
commentsByTarget: new Map(),
editor: target ? { target, body: "", commentId: null } : null,
onStartComment() {},
onCancelEditor() {},
onSaveEditor() {},
onEditComment() {},
onDeleteComment() {},
};
}