This repository has no description
1<script lang="ts">
2 import ArrowRight from "$icon/arrow-right";
3 import ChevronDown from "$icon/chevron-down";
4 import ChevronRight from "$icon/chevron-right";
5 import Eye from "$icon/eye";
6 import type { DiffFile, DiffStat } from "$lib/api/diff";
7 import type { FileDiffMetadata } from "@pierre/diffs";
8 import Button from "$lib/components/ui/Button.svelte";
9 import type { Snippet } from "svelte";
10 import DiffStatPill from "./DiffStatPill.svelte";
11 import PierreDiff from "./PierreDiff.svelte";
12 import type { DiffStyle } from "./pierre";
13
14 interface Props {
15 file: DiffFile;
16 // resolved display path (new name, falling back to old)
17 name: string;
18 stat: DiffStat;
19 // pierre's structured diff, absent for binaries
20 fileDiff?: FileDiffMetadata;
21 // server-prerendered shadow dom for fileDiff
22 prerenderedHTML?: string;
23 // side-by-side or stacked
24 diffStyle?: DiffStyle;
25 // blob route for the "view file" link, hidden when absent
26 blobUrl?: string;
27 open?: boolean;
28 // review-button slot on the file header (pull pages)
29 headerActions?: Snippet<[DiffFile]>;
30 }
31
32 let {
33 file,
34 name,
35 stat,
36 fileDiff,
37 prerenderedHTML,
38 diffStyle = "unified",
39 blobUrl,
40 open = $bindable(true),
41 headerActions
42 }: Props = $props();
43</script>
44
45<!-- the summary sticks below the topbar and needs a solid bg
46 or the diff shows through. z-[5] keeps it under the z-10 topbar it
47 shares its sticky band with -->
48<details
49 bind:open
50 id="file-{name}"
51 class="group mx-auto w-full rounded border border-border-default bg-background-default"
52>
53 <summary
54 class="sticky top-[calc(3rem+env(safe-area-inset-top))] z-5 cursor-pointer list-none rounded bg-background-default group-open:rounded-b-none group-open:border-b group-open:border-border-default"
55 >
56 <div class="flex cursor-pointer justify-between">
57 <div class="flex items-center gap-2 overflow-x-auto p-2">
58 {#if open}
59 <ChevronDown class="size-4" />
60 {:else}
61 <ChevronRight class="size-4" />
62 {/if}
63 <DiffStatPill {stat} />
64
65 <div class="flex items-center gap-2 overflow-x-auto">
66 {#if file.name.old && file.name.new && file.name.old !== file.name.new}
67 {file.name.old}
68 <ArrowRight class="size-4" />
69 {file.name.new}
70 {:else}
71 {name}
72 {/if}
73 </div>
74 </div>
75 <div class="flex items-center pr-1">
76 {#if blobUrl}
77 <Button
78 href={blobUrl as "/"}
79 variant="ghost"
80 size="sm"
81 icon={Eye}
82 class="hidden md:inline-flex"
83 onclick={(event) => event.stopPropagation()}
84 >
85 View file
86 </Button>
87 {/if}
88 {@render headerActions?.(file)}
89 </div>
90 </div>
91 </summary>
92
93 {#if file.is_binary}
94 <p class="p-4 text-center text-foreground-placeholder">
95 This is a binary file and will not be displayed.
96 </p>
97 {:else}
98 <PierreDiff files={fileDiff ? [fileDiff] : []} {diffStyle} prerenderedHTML={[prerenderedHTML]} />
99 {/if}
100</details>