Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/desktop/bundle-budget.json
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
"renderer/pcm-tee-worklet.js": 413,
"renderer/avatar-session.js": 149485,
"renderer/diagram.js": 195519,
"renderer/diff-view.js": 142443,
"renderer/diff-view.js": 178690,
"renderer/factory-world.js": 161750,
"renderer/factory-world-dev.js": 1745,
"renderer/factory-world-station.js": 31732,
Expand Down
88 changes: 83 additions & 5 deletions apps/desktop/src/renderer/diff-view/contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import type {
} from "@luke/settings/diff-view";

/**
* contract.ts -- what the Files changed view and the diff bundle say to each other: the one mount call, and the handle the view redraws the diff through.
* contract.ts -- what the Files changed view and the diff bundle say to each other: a mount for one patch and one for a list of files, and the handles the view redraws them through.
*
* A changed file's patch is drawn by Pierre's diffs (`@pierre/diffs`), with
* Shiki colouring it, which is too large for the bundle every window parses,
Expand Down Expand Up @@ -45,6 +45,24 @@ export interface DiffLook {
readonly ligatures: boolean;
}

/** Whether two looks draw a diff alike: every setting the same, each appearance's theme among them. */
export function sameLook(a: DiffLook, b: DiffLook): boolean {
return (
a.layout === b.layout &&
a.indicators === b.indicators &&
a.inlineDiff === b.inlineDiff &&
a.backgrounds === b.backgrounds &&
a.lineNumbers === b.lineNumbers &&
a.wrap === b.wrap &&
a.themes.dark === b.themes.dark &&
a.themes.light === b.themes.light &&
a.font === b.font &&
a.fontSize === b.fontSize &&
a.lineHeight === b.lineHeight &&
a.ligatures === b.ligatures
);
}

/** Which side of a diff a selected line is counted on: the old side's, a removed line's, or the new side's. */
export const DIFF_SELECTION_SIDE = {
DELETIONS: "deletions",
Expand All @@ -66,17 +84,71 @@ export interface DiffAnnotation {
readonly line: number;
}

/** Whether two runs of notes stand under the same lines by the same keys. */
export function sameNotes(a: readonly DiffAnnotation[], b: readonly DiffAnnotation[]): boolean {
return (
a.length === b.length &&
a.every((note, at) => {
const other = b[at];
return (
other !== undefined &&
note.key === other.key &&
note.side === other.side &&
note.line === other.line
);
})
);
}

/** What the view hands the bundle: one file's patch, headed as git writes one, how to draw it, the lines lit in it, if any, and whether it is an excerpt. */
export interface DiffViewProps {
readonly patch: string;
readonly look: DiffLook;
readonly selected?: DiffSelection | undefined;
/** Whether the patch is an excerpt, a few lines of a file as a review thread's, drawn with nothing said of the lines around them. */
readonly excerpt?: boolean | undefined;
/** The notes drawn under lines, each by its key, whose element `renderAnnotation` hands over; none for none. */
readonly annotations?: readonly DiffAnnotation[] | undefined;
/** The element a note's key is drawn in, kept by the view and filled by it, which the bundle stands under its line. */
readonly renderAnnotation?: ((key: string) => HTMLElement) | undefined;
}

/**
* The line a note stands on to stand above a file's first hunk rather than
* under a line: Pierre's own place for a note on the whole file, which is
* drawn under the file's header whether or not the file has any hunks.
*/
export const FILE_NOTE_LINE = 0;

/** One file of a list: its path, which keys it; its patch, headed as git writes one, with no hunks where it has none to draw; whether only its header is drawn; its notes; and whether those notes stand in the place of its lines. */
export interface DiffListFile {
readonly path: string;
readonly patch: string;
readonly collapsed: boolean;
readonly annotations: readonly DiffAnnotation[];
/**
* Whether the file draws no lines of its own, only what stands in their
* place: its notes on the whole file (`FILE_NOTE_LINE`), drawn across the
* whole of its width, which say why it has no lines or show what it is.
*/
readonly lead: boolean;
}

/**
* What the view hands the bundle for a list of files: the files in order,
* how to draw them, the element each file's header is drawn in and each
* note, both kept by the view and filled by it, and the door told which file
* stands at the top of the list as it scrolls.
*/
export interface DiffListProps {
readonly files: readonly DiffListFile[];
readonly look: DiffLook;
readonly renderHeader: (path: string) => HTMLElement;
readonly renderAnnotation: (path: string, key: string) => HTMLElement;
readonly onTopFile: (path: string) => void;
}

/** The list as mounted: drawn again for new props, scrolled to a file's header, and taken down. */
export interface DiffListHandle {
readonly update: (props: DiffListProps) => void;
readonly reveal: (path: string) => void;
readonly unmount: () => void;
}

/** The diff as mounted: drawn again for new props, and taken down. */
Expand All @@ -92,4 +164,10 @@ export interface DiffViewModule {
* find reads (`../find-matches.ts`'s `FIND_SHADOW_ATTRIBUTE`).
*/
readonly mount: (host: HTMLElement, props: DiffViewProps) => DiffViewHandle;
/**
* Draws the files under `host`, which is the list's scroller, each in a
* shadow root of its own the tab's find reads, only those near the view
* drawn at all.
*/
readonly mountList: (host: HTMLElement, props: DiffListProps) => DiffListHandle;
}
Loading
Loading