Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273<script lang="ts"> import ArrowRight from "$icon/arrow-right"; import ChevronsLeftRightEllipsis from "$icon/chevrons-left-right-ellipsis"; import Diff from "$icon/diff"; import FoldVertical from "$icon/fold-vertical"; import LoaderCircle from "$icon/loader-circle"; import MessageSquareMore from "$icon/message-square-more"; import PanelLeftClose from "$icon/panel-left-close"; import PanelLeftOpen from "$icon/panel-left-open"; import SquareSplitHorizontal from "$icon/square-split-horizontal"; import SquareSplitVertical from "$icon/square-split-vertical"; import UnfoldVertical from "$icon/unfold-vertical"; import { resizable } from "$lib/actions/resizable"; import DiffStatPill from "$lib/components/repo/DiffStatPill.svelte"; import Button from "$lib/components/ui/Button.svelte"; import ButtonGroup, { segmentProps } from "$lib/components/ui/ButtonGroup.svelte"; import PullDiffFiles from "./PullDiffFiles.svelte"; import PullFileTree from "./PullFileTree.svelte"; import type { ResolvedPathname } from "$app/types"; import type { DiffStyle } from "$lib/components/repo/pierre"; import type { PullDiff } from "$lib/api/pullDiff";
interface Props { // absent while loading, on an error, or in interdiff mode diff?: PullDiff; diffError?: string; diffStyle?: DiffStyle; isInterdiff: boolean; // `v1`, or the two ends of an interdiff version: number; version1?: number; version2?: number; // a diff shows base → head, an interdiff shows which change it is filtered to baseLabel?: string; headLabel?: string; changeLabel?: string; diffHref: ResolvedPathname; interdiffHref: ResolvedPathname; // version 0 has nothing before it to compare against canInterdiff: boolean; // the rail is a sibling of this panel, so the page owns its open state discussionOpen?: boolean; onToggleDiscussion?: () => void; }
let { diff, diffError, diffStyle = "unified", isInterdiff, version, version1, version2, baseLabel, headLabel, changeLabel, diffHref, interdiffHref, canInterdiff, discussionOpen = true, onToggleDiscussion }: Props = $props();
let filesOpen = $state(true); let allOpen = $state(true); let filesPanel = $state<HTMLElement>();
// `inline-block box-content min-h-[1lh]` keeps a chip from collapsing when its // text is empty const chip = "inline-block box-content min-h-[1lh] border border-border-default px-1 py-0.5 typography-monospace-small text-foreground-default";</script>
<!-- skipped from the appview: the per-file `Reviewed` state, the active-file scroll highlight, and the line-quote button (pierre renders into a shadow dom with no per-line anchors to hang it off)--><div class="group/diff-layout min-h-[50vh]"> <header class="sticky top-0 z-20 flex flex-col bg-background-canvas px-1 md:flex-row md:justify-between" > <div class="flex h-12 items-center gap-2"> <div class="hidden md:block"> <Button type="button" size="small" variant="ghost" icon={filesOpen ? PanelLeftClose : PanelLeftOpen} title="Toggle filetree panel" onclick={() => (filesOpen = !filesOpen)} /> </div>
{#if diff} <DiffStatPill stat={diff.stat} /> {:else} <div class="flex items-center typography-monospace-regular font-mono"> <span class="rounded-l-sm bg-background-success-subtle p-1 text-foreground-success select-none" > +<LoaderCircle class="inline-flex size-3 animate-spin" aria-hidden="true" /> </span> <span class="rounded-r-sm bg-background-danger-subtle p-1 text-foreground-danger select-none" > -<LoaderCircle class="inline-flex size-3 animate-spin" aria-hidden="true" /> </span> </div> {/if}
<span class="typography-paragraph-small"> {isInterdiff ? "Interdiff" : "Diff"} <span class="ml-1 typography-paragraph-mini after:ml-1 after:text-foreground-disabled after:content-['|']" > {#if isInterdiff} <code class={chip}>v{version1}</code> <ArrowRight class="inline-flex size-3" aria-hidden="true" /> <code class={chip}>v{version2}</code> {:else} <code class={chip}>v{version}</code> {/if} </span> <a href="#commits" class="inline-flex min-h-8 items-center justify-center gap-1.5 rounded-sm px-2 py-1 typography-paragraph-mini text-foreground-default no-underline hover:bg-background-inset hover:no-underline" > {#if baseLabel !== undefined && headLabel !== undefined} <code class={chip}>{baseLabel}</code> <ArrowRight class="inline-flex size-3" aria-hidden="true" /> <code class={chip}>{headLabel}</code> {:else if changeLabel} <code class={chip}>{changeLabel}</code> {:else} <span class="px-1">All changes</span> {/if} </a> </span> </div>
<div class="flex h-12 items-center gap-2"> <Button type="button" size="small" icon={allOpen ? FoldVertical : UnfoldVertical} title="Expand/Collapse diffs" class="font-normal normal-case" onclick={() => (allOpen = !allOpen)} > <span class="hidden md:inline">{allOpen ? "Collapse all" : "Expand all"}</span> </Button>
<div class="grow md:hidden"></div>
<ButtonGroup> <Button href={"?diff=unified" as "/"} data-sveltekit-replacestate data-sveltekit-noscroll size="small" icon={SquareSplitVertical} title="Unified diff" {...segmentProps(diffStyle !== "split")} > Unified </Button> <Button href={"?diff=split" as "/"} data-sveltekit-replacestate data-sveltekit-noscroll size="small" icon={SquareSplitHorizontal} title="Split diff" {...segmentProps(diffStyle === "split")} > Split </Button> </ButtonGroup>
<ButtonGroup> <Button size="small" icon={Diff} href={diffHref} {...segmentProps(!isInterdiff)}>Diff</Button> {#if canInterdiff} <Button size="small" href={interdiffHref} {...segmentProps(isInterdiff)}> <ChevronsLeftRightEllipsis class="size-4 shrink-0 rotate-90" aria-hidden="true" /> Interdiff </Button> {:else} <Button type="button" size="small" {...segmentProps(isInterdiff)} disabled> <ChevronsLeftRightEllipsis class="size-4 shrink-0 rotate-90" aria-hidden="true" /> Interdiff </Button> {/if} </ButtonGroup>
{#if !discussionOpen} <Button type="button" size="small" variant="ghost" icon={MessageSquareMore} title="Toggle discussion panel" class="hidden lg:inline-flex" onclick={onToggleDiscussion} /> {/if} </div> </header>
<div class="flex"> <aside bind:this={filesPanel} class="w-1/5 max-w-[40%] {filesOpen ? 'hidden md:block' : 'hidden'}" > <div class="sticky top-12 rounded-sm border border-border-default bg-background-default shadow-xs" > <div class="max-h-[calc(100vh-3rem-0.5rem)] overflow-y-auto pb-2"> <section class="w-full px-6 py-2 typography-paragraph-small"> <div class="mb-8"> {#if diff} <PullFileTree files={diff.files} /> {:else} <span class="text-foreground-subtle"> <LoaderCircle class="inline-flex size-4 animate-spin" aria-hidden="true" /> loading... </span> {/if} </div> </section> </div> </div> </aside>
{#if filesOpen} <div class="group sticky top-12 hidden max-h-screen w-4 shrink-0 cursor-col-resize flex-col items-center justify-center md:flex" role="separator" aria-orientation="vertical" aria-label="Resize file tree" use:resizable={{ target: () => filesPanel, direction: "before" }} > <div class="h-16 w-1 rounded-full bg-border-strong transition-all group-hover:h-24 group-hover:bg-foreground-subtle group-[.resizing]:h-24" ></div> </div> {/if}
<div class="min-w-0 flex-1"> {#if diffError} <div class="flex min-h-32 w-full items-center justify-center rounded-sm border border-border-danger bg-background-danger-subtle p-4 typography-paragraph-regular text-foreground-danger" > <span>{diffError}</span> </div> {:else if diff && diff.files.length > 0} <PullDiffFiles files={diff.files} prerendered={diff.prerendered} {diffStyle} {allOpen} /> {:else if diff} <div class="flex min-h-32 w-full items-center justify-center rounded-sm border border-border-default bg-background-info-subtle p-4 typography-paragraph-regular text-foreground-info" > <span>No change between two revisions.</span> </div> {:else} <span class="text-foreground-subtle"> <LoaderCircle class="inline-flex size-4 animate-spin" aria-hidden="true" /> loading... </span> {/if} </div> </div></div>