Skip to content

Commit 93a39c5

Browse files
committed
Merge remote-tracking branch 'upstream/master' into feat/pr-comment
# Conflicts: # bun.lock # web/package.json # web/src/lib/components/diff/ConciseDiffView.svelte # web/src/lib/diff-viewer-multi-file.svelte.ts
2 parents 8742f5c + 4d422a1 commit 93a39c5

File tree

8 files changed

+200
-202
lines changed

8 files changed

+200
-202
lines changed

bun.lock

Lines changed: 128 additions & 102 deletions
Large diffs are not rendered by default.

web-extension/package.json

Lines changed: 11 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -16,23 +16,23 @@
1616
"pack": "bun run pack:chrome && bun run pack:firefox"
1717
},
1818
"devDependencies": {
19-
"@eslint/compat": "^1.2.9",
20-
"@eslint/js": "^9.28.0",
21-
"@types/bun": "^1.2.15",
19+
"@eslint/compat": "^1.3.1",
20+
"@eslint/js": "^9.30.1",
21+
"@types/bun": "^1.2.18",
2222
"@types/webextension-polyfill": "^0.12.3",
23-
"chrome-types": "^0.1.353",
24-
"eslint": "^9.28.0",
23+
"chrome-types": "^0.1.360",
24+
"eslint": "^9.30.1",
2525
"eslint-config-prettier": "^10.1.5",
26-
"globals": "^16.2.0",
27-
"prettier": "^3.5.3",
28-
"prettier-plugin-tailwindcss": "^0.6.12",
26+
"globals": "^16.3.0",
27+
"prettier": "^3.6.2",
28+
"prettier-plugin-tailwindcss": "^0.6.14",
2929
"typescript": "^5.8.3",
30-
"typescript-eslint": "^8.33.1",
30+
"typescript-eslint": "^8.36.0",
3131
"vite": "^6.3.5"
3232
},
3333
"dependencies": {
34-
"@tailwindcss/vite": "^4.1.8",
35-
"tailwindcss": "^4.1.8",
34+
"@tailwindcss/vite": "^4.1.11",
35+
"tailwindcss": "^4.1.11",
3636
"webextension-polyfill": "^0.12.0"
3737
},
3838
"bunrc": {

web/package.json

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -16,49 +16,49 @@
1616
"lint": "prettier --check . && eslint ."
1717
},
1818
"devDependencies": {
19-
"@eslint/compat": "^1.2.9",
20-
"@eslint/js": "^9.28.0",
21-
"@iconify-json/octicon": "^1.2.6",
19+
"@eslint/compat": "^1.3.1",
20+
"@eslint/js": "^9.30.1",
21+
"@iconify-json/octicon": "^1.2.7",
2222
"@iconify/tailwind4": "^1.0.6",
2323
"@octokit/openapi-types": "^25.1.0",
2424
"@sveltejs/adapter-auto": "^6.0.1",
25-
"@sveltejs/adapter-cloudflare": "^7.0.3",
26-
"@sveltejs/kit": "^2.21.2",
27-
"@sveltejs/vite-plugin-svelte": "^5.1.0",
28-
"@tailwindcss/vite": "^4.1.8",
25+
"@sveltejs/adapter-cloudflare": "^7.0.5",
26+
"@sveltejs/kit": "^2.22.5",
27+
"@sveltejs/vite-plugin-svelte": "^6.0.0",
28+
"@tailwindcss/vite": "^4.1.11",
2929
"@types/chroma-js": "^3.1.1",
3030
"@types/dompurify": "^3.2.0",
3131
"@types/luxon": "^3.6.2",
3232
"@types/marked": "^6.0.0",
3333
"@types/wicg-file-system-access": "^2023.10.6",
34-
"eslint": "^9.28.0",
34+
"eslint": "^9.30.1",
3535
"eslint-config-prettier": "^10.1.5",
36-
"eslint-plugin-svelte": "^3.9.1",
37-
"globals": "^16.2.0",
38-
"prettier": "^3.5.3",
36+
"eslint-plugin-svelte": "^3.10.1",
37+
"globals": "^16.3.0",
38+
"prettier": "^3.6.2",
3939
"prettier-plugin-svelte": "^3.4.0",
40-
"prettier-plugin-tailwindcss": "^0.6.12",
41-
"svelte": "^5.33.14",
40+
"prettier-plugin-tailwindcss": "^0.6.14",
41+
"svelte": "^5.35.5",
4242
"svelte-adapter-bun": "^0.5.2",
43-
"svelte-check": "^4.2.1",
44-
"tailwindcss": "^4.1.8",
43+
"svelte-check": "^4.2.2",
44+
"tailwindcss": "^4.1.11",
4545
"typescript": "^5.8.3",
46-
"typescript-eslint": "^8.33.1",
46+
"typescript-eslint": "^8.36.0",
4747
"vite": "^6.3.5",
4848
"vitest": "^3.2.4"
4949
},
5050
"dependencies": {
51-
"bits-ui": "^2.4.1",
51+
"bits-ui": "^2.8.10",
5252
"chroma-js": "^3.1.2",
5353
"date-fns": "^4.1.0",
5454
"diff": "^8.0.2",
5555
"dompurify": "^3.2.6",
56-
"luxon": "^3.6.1",
56+
"luxon": "^3.7.1",
5757
"marked": "^16.0.0",
58-
"runed": "^0.28.0",
59-
"shiki": "^3.4.2",
60-
"svelte-toolbelt": "^0.9.1",
61-
"virtua": "^0.41.3"
58+
"runed": "^0.30.0",
59+
"shiki": "^3.7.0",
60+
"svelte-toolbelt": "^0.9.3",
61+
"virtua": "^0.41.5"
6262
},
6363
"bunrc": {
6464
"run": {

web/src/lib/components/diff/ConciseDiffView.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@
6565
lineWrap,
6666
});
6767
68-
const parsedPatch: Promise<StructuredPatch> = $derived.by(async () => {
68+
const parsedPatch = $derived.by(() => {
6969
if (rawPatchContent !== undefined) {
7070
return parseSinglePatch(rawPatchContent);
7171
} else if (patch !== undefined) {

web/src/lib/components/diff/concise-diff-view.svelte.ts

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -681,6 +681,19 @@ async function makeHunk(
681681
return { lines };
682682
}
683683

684+
export function patchHeaderDiffOnly(patch: StructuredPatch): boolean {
685+
if (patch.hunks.length === 0) {
686+
return false;
687+
}
688+
let onlyHeaderChanges = true;
689+
for (let j = 0; j < patch.hunks.length; j++) {
690+
if (hasNonHeaderChanges(patch.hunks[j].lines)) {
691+
onlyHeaderChanges = false;
692+
}
693+
}
694+
return onlyHeaderChanges;
695+
}
696+
684697
export function hasNonHeaderChanges(contentLines: string[]) {
685698
for (const line of contentLines) {
686699
if (lineHasNonHeaderChange(line)) {
@@ -1031,7 +1044,7 @@ export function parseSinglePatch(rawPatchContent: string): StructuredPatch {
10311044

10321045
export interface ConciseDiffViewProps<K> {
10331046
rawPatchContent?: string;
1034-
patch?: Promise<StructuredPatch>;
1047+
patch?: StructuredPatch;
10351048

10361049
syntaxHighlighting?: boolean;
10371050
syntaxHighlightingTheme?: BundledTheme;
@@ -1048,7 +1061,7 @@ export interface ConciseDiffViewProps<K> {
10481061
}
10491062

10501063
export type ConciseDiffViewStateProps<K> = ReadableBoxedValues<{
1051-
patch: Promise<StructuredPatch>;
1064+
patch: StructuredPatch;
10521065

10531066
syntaxHighlighting: boolean;
10541067
syntaxHighlightingTheme: BundledTheme | undefined;

web/src/lib/diff-viewer-multi-file.svelte.ts

Lines changed: 16 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@ import {
77
type GithubPRComment,
88
fetchGithubPRWithComments,
99
} from "./github.svelte";
10-
import { type StructuredPatch, parsePatch } from "diff";
10+
import { type StructuredPatch } from "diff";
1111
import {
1212
ConciseDiffViewCachedState,
1313
DEFAULT_THEME_DARK,
1414
DEFAULT_THEME_LIGHT,
15-
hasNonHeaderChanges,
1615
isNoNewlineAtEofLine,
1716
parseSinglePatch,
17+
patchHeaderDiffOnly,
1818
} from "$lib/components/diff/concise-diff-view.svelte";
1919
import type { BundledTheme } from "shiki";
2020
import { browser } from "$app/environment";
@@ -158,8 +158,8 @@ export type CommonFileDetails = {
158158

159159
export type TextFileDetails = CommonFileDetails & {
160160
type: "text";
161-
patchText: string;
162-
structuredPatch: Promise<StructuredPatch>;
161+
structuredPatch: StructuredPatch;
162+
patchHeaderDiffOnly: boolean;
163163
};
164164

165165
export type ImageFileDetails = CommonFileDetails & {
@@ -168,13 +168,14 @@ export type ImageFileDetails = CommonFileDetails & {
168168
};
169169

170170
export function makeTextDetails(fromFile: string, toFile: string, status: FileStatus, patchText: string): TextFileDetails {
171+
const patch = parseSinglePatch(patchText);
171172
return {
172173
type: "text",
173174
fromFile,
174175
toFile,
175176
status,
176-
patchText,
177-
structuredPatch: (async () => parseSinglePatch(patchText))(),
177+
structuredPatch: patch,
178+
patchHeaderDiffOnly: patchHeaderDiffOnly(patch),
178179
};
179180
}
180181

@@ -293,45 +294,6 @@ export function getFileStatusProps(status: FileStatus): FileStatusProps {
293294
}
294295
}
295296

296-
export function findHeaderChangeOnlyPatches(fileDetails: FileDetails[]) {
297-
const patchStrings = fileDetails.map((details) => {
298-
if (details.type === "text") {
299-
return details.patchText;
300-
}
301-
return undefined;
302-
});
303-
304-
const result: boolean[] = [];
305-
306-
for (let i = 0; i < patchStrings.length; i++) {
307-
const patchString = patchStrings[i];
308-
if (patchString === undefined || patchString.length === 0) {
309-
result.push(false);
310-
continue;
311-
}
312-
// TODO: Parsing twice is wasteful
313-
const patches = parsePatch(patchString);
314-
if (patches.length !== 1) {
315-
result.push(false);
316-
continue;
317-
}
318-
const patch = patches[0];
319-
if (patch.hunks.length === 0) {
320-
result.push(false);
321-
continue;
322-
}
323-
let onlyHeaderChanges = true;
324-
for (let j = 0; j < patch.hunks.length; j++) {
325-
if (hasNonHeaderChanges(patch.hunks[j].lines)) {
326-
onlyHeaderChanges = false;
327-
}
328-
}
329-
result.push(onlyHeaderChanges);
330-
}
331-
332-
return result;
333-
}
334-
335297
export type ViewerStatistics = {
336298
addedLines: number;
337299
removedLines: number;
@@ -400,19 +362,22 @@ export class MultiFileDiffViewerState {
400362

401363
readonly fileTreeFilterDebounced = new Debounced(() => this.fileTreeFilter, 500);
402364
readonly searchQueryDebounced = new Debounced(() => this.searchQuery, 500);
403-
readonly stats: Promise<ViewerStatistics> = $derived(this.countStats());
365+
readonly stats: ViewerStatistics = $derived(this.countStats());
404366
readonly fileTreeRoots: TreeNode<FileTreeNodeData>[] = $derived(makeFileTree(this.fileDetails));
405367
readonly filteredFileDetails: FileDetails[] = $derived(
406368
this.fileTreeFilterDebounced.current ? this.fileDetails.filter((f) => this.filterFile(f)) : this.fileDetails,
407369
);
408-
readonly patchHeaderDiffOnly: boolean[] = $derived(findHeaderChangeOnlyPatches(this.fileDetails));
409370
readonly searchResults: Promise<SearchResults> = $derived(this.findSearchResults());
410371

411372
private constructor() {
412373
// Auto-check all patch header diff only diffs
413374
$effect(() => {
414-
for (let i = 0; i < this.patchHeaderDiffOnly.length; i++) {
415-
if (this.patchHeaderDiffOnly[i] && this.checked[i] === undefined) {
375+
for (let i = 0; i < this.fileDetails.length; i++) {
376+
const details = this.fileDetails[i];
377+
if (details.type !== "text") {
378+
continue;
379+
}
380+
if (details.patchHeaderDiffOnly && this.checked[i] === undefined) {
416381
this.checked[i] = true;
417382
}
418383
}
@@ -718,7 +683,7 @@ export class MultiFileDiffViewerState {
718683
return this.diffMetadata?.type === "github" && !!this.diffMetadata.prNumber;
719684
}
720685

721-
private async countStats(): Promise<ViewerStatistics> {
686+
private countStats(): ViewerStatistics {
722687
let addedLines = 0;
723688
let removedLines = 0;
724689
const fileAddedLines: number[] = [];
@@ -729,7 +694,7 @@ export class MultiFileDiffViewerState {
729694
if (details.type !== "text") {
730695
continue;
731696
}
732-
const diff = await details.structuredPatch;
697+
const diff = details.structuredPatch;
733698

734699
for (let j = 0; j < diff.hunks.length; j++) {
735700
const hunk = diff.hunks[j];

web/src/routes/+page.svelte

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -286,11 +286,7 @@
286286
</div>
287287
<div class="flex flex-row items-center gap-2 px-3 pb-2">
288288
<SidebarToggle class="data-[side=right]:order-10" />
289-
{#await viewer.stats}
290-
<DiffStats />
291-
{:then stats}
292-
<DiffStats add={stats.addedLines} remove={stats.removedLines} />
293-
{/await}
289+
<DiffStats add={viewer.stats.addedLines} remove={viewer.stats.removedLines} />
294290
<DiffSearch />
295291
</div>
296292
<div class="flex flex-1 flex-col border-t">
@@ -328,7 +324,7 @@
328324
{/if}
329325
</div>
330326
{/if}
331-
{#if !viewer.collapsed[index] && value.type === "text" && (!viewer.patchHeaderDiffOnly[index] || !globalOptions.omitPatchHeaderOnlyHunks)}
327+
{#if !viewer.collapsed[index] && value.type === "text" && (!value.patchHeaderDiffOnly || !globalOptions.omitPatchHeaderOnlyHunks)}
332328
<div class="mb border-b">
333329
<ConciseDiffView
334330
patch={value.structuredPatch}

web/src/routes/FileHeader.svelte

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828
});
2929
}
3030
}
31+
32+
let patchHeaderDiffOnly = $derived(value.type === "text" && value.patchHeaderDiffOnly);
3133
</script>
3234

3335
{#snippet fileName()}
@@ -97,19 +99,15 @@
9799
onkeyup={(event) => event.key === "Enter" && viewer.scrollToFile(index, { autoExpand: false, smooth: true })}
98100
>
99101
{#if value.type === "text"}
100-
{#await viewer.stats}
101-
<DiffStats brief />
102-
{:then stats}
103-
<DiffStats brief add={stats.fileAddedLines[index]} remove={stats.fileRemovedLines[index]} />
104-
{/await}
102+
<DiffStats brief add={viewer.stats.fileAddedLines[index]} remove={viewer.stats.fileRemovedLines[index]} />
105103
{/if}
106104
{@render fileName()}
107105
<div class="ms-0.5 ml-auto flex items-center gap-2">
108-
{#if viewer.patchHeaderDiffOnly[index]}
106+
{#if patchHeaderDiffOnly}
109107
<span class="rounded-sm bg-neutral-3 px-1.5">Patch-header-only diff</span>
110108
{/if}
111109
{@render actionsPopover()}
112-
{#if !viewer.patchHeaderDiffOnly[index] || !globalOptions.omitPatchHeaderOnlyHunks || value.type === "image"}
110+
{#if !patchHeaderDiffOnly || !globalOptions.omitPatchHeaderOnlyHunks || value.type === "image"}
113111
{@render collapseToggle()}
114112
{/if}
115113
</div>

0 commit comments

Comments
 (0)