mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-24 10:12:44 +00:00
* perf(app): keep large diffs responsive Render file headers on the canvas and bound expensive text shaping to the active viewport while preserving sticky, hover, and horizontal-scroll behavior. Let native scroll cancellation arbitrate header presses so finger scrolling cannot expand files. * test(app): assert canvas diff header accessibility * test(app): assert canvas diff header semantics
110 lines
3.1 KiB
TypeScript
110 lines
3.1 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
diffHeaderViewportKey,
|
|
diffInteractionWindowTop,
|
|
resolveVisibleFileSections,
|
|
} from "./header-layout";
|
|
import type { DiffFileSection } from "./types";
|
|
|
|
describe("diff header viewport", () => {
|
|
it("keeps a many-file document bounded by the viewport", () => {
|
|
const files = Array.from({ length: 2_000 }, (_, index) => collapsedFile(index));
|
|
|
|
const window = resolveVisibleFileSections({
|
|
files,
|
|
scrollTop: 30_000,
|
|
viewportHeight: 600,
|
|
overscan: 600,
|
|
});
|
|
|
|
expect(window.files.length).toBeLessThanOrEqual(61);
|
|
expect(window.sticky?.file.fileIndex).toBe(1_000);
|
|
expect(window.sticky?.y).toBe(0);
|
|
});
|
|
|
|
it("pins an expanded file whose original header is outside overscan", () => {
|
|
const files = [expandedFile(0, 4_000), collapsedFile(1, 4_030)];
|
|
|
|
const window = resolveVisibleFileSections({
|
|
files,
|
|
scrollTop: 2_000,
|
|
viewportHeight: 600,
|
|
overscan: 600,
|
|
});
|
|
|
|
expect(window.files.map((file) => file.path)).toEqual(["file-0.ts"]);
|
|
expect(window.sticky).toEqual({ file: files[0], y: 0 });
|
|
});
|
|
|
|
it("pushes the pinned header out one pixel at a time", () => {
|
|
const files = [expandedFile(0, 100), collapsedFile(1, 130)];
|
|
|
|
expect(
|
|
resolveVisibleFileSections({ files, scrollTop: 129, viewportHeight: 100, overscan: 0 }).sticky
|
|
?.y,
|
|
).toBe(-29);
|
|
expect(
|
|
resolveVisibleFileSections({ files, scrollTop: 130, viewportHeight: 100, overscan: 0 }).sticky
|
|
?.y,
|
|
).toBe(0);
|
|
});
|
|
|
|
it("reuses a fixed sticky header until its visible layout changes", () => {
|
|
const files = [section(0, 0, 4_000, false), section(1, 4_000, 4_030, true)];
|
|
const keyAt = (scrollTop: number) =>
|
|
diffHeaderViewportKey({ files, scrollTop, viewportHeight: 600, activePath: null });
|
|
|
|
expect(keyAt(100)).toBe(keyAt(2_000));
|
|
expect(keyAt(3_985)).not.toBe(keyAt(2_000));
|
|
});
|
|
});
|
|
|
|
describe("diff interaction window", () => {
|
|
it("retains one shell window across two viewport heights", () => {
|
|
expect(diffInteractionWindowTop(0, 500)).toBe(0);
|
|
expect(diffInteractionWindowTop(999, 500)).toBe(0);
|
|
expect(diffInteractionWindowTop(1_000, 500)).toBe(1_000);
|
|
});
|
|
});
|
|
|
|
function collapsedFile(fileIndex: number, top = fileIndex * 30): DiffFileSection {
|
|
return section(fileIndex, top, top + 30, true);
|
|
}
|
|
|
|
function expandedFile(fileIndex: number, bodyHeight: number): DiffFileSection {
|
|
const top = fileIndex === 0 ? 0 : bodyHeight - 30;
|
|
return section(fileIndex, top, top + 30 + bodyHeight, false);
|
|
}
|
|
|
|
function section(
|
|
fileIndex: number,
|
|
top: number,
|
|
bottom: number,
|
|
isCollapsed: boolean,
|
|
): DiffFileSection {
|
|
const path = `file-${fileIndex}.ts`;
|
|
return {
|
|
file: {
|
|
path,
|
|
oldPath: undefined,
|
|
additions: 1,
|
|
deletions: 1,
|
|
isNew: false,
|
|
isDeleted: false,
|
|
hunks: [],
|
|
},
|
|
fileIndex,
|
|
path,
|
|
top,
|
|
headerHeight: 30,
|
|
bodyTop: top + 30,
|
|
bodyHeight: bottom - top - 30,
|
|
bottom,
|
|
gutterWidth: 30,
|
|
contentWidth: 300,
|
|
rowStart: 0,
|
|
rowEnd: 0,
|
|
isCollapsed,
|
|
};
|
|
}
|