Something went wrong. Try again.
Monorepo for Tangled
Something went wrong. Try again.
Svelte
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229<script lang="ts"> import { resolve } from "$app/paths"; import Copy from "$icon/copy"; import FolderCode from "$icon/folder-code"; import CommitBodyToggle from "$lib/components/repo/CommitBodyToggle.svelte"; import CommitSignOff from "$lib/components/repo/CommitSignOff.svelte"; import Button, { type ButtonVariants } from "$lib/components/ui/Button.svelte"; import CopyButton from "$lib/components/ui/CopyButton.svelte"; import DropdownItem from "$lib/components/ui/DropdownItem.svelte"; import MoreMenu from "$lib/components/ui/MoreMenu.svelte"; import Pending from "$lib/components/ui/Pending.svelte"; import Tag from "$lib/components/ui/Tag.svelte"; import TimeAgo from "$lib/components/ui/TimeAgo.svelte"; import User from "$lib/components/ui/User.svelte"; import { hasBodyBeyondSignOffs, signOffsFrom } from "$lib/api/repo"; import type { CommitStatuses } from "$lib/api/commitStatuses"; import PipelineWorkflows from "$lib/components/repo/pipelines/PipelineWorkflows.svelte"; import type { CommitSummary } from "$lib/components/repo/types"; import { tv } from "tailwind-variants";
const subjectLayout = tv({ slots: { wrapper: "min-w-0", link: "text-foreground-default no-underline hover:underline", signOff: "", bodyToggle: "" }, variants: { wrap: { true: { wrapper: "space-x-xs", link: "wrap-break-word", signOff: "align-middle", bodyToggle: "inline-flex align-middle" }, false: { wrapper: "flex items-center gap-xs", link: "truncate", signOff: "", bodyToggle: "" } } } });
const row = tv({ base: "px-md md:col-span-full md:grid md:grid-cols-subgrid md:grid-rows-[minmax(3rem,auto)] md:items-center" });
interface Props { ownerHandle: string; repoName: string; commit: CommitSummary; tagsByCommit?: Record<string, string[]> | Promise<Record<string, string[]>>; pipelineStatuses?: Promise<CommitStatuses>; class?: string; }
let { ownerHandle, repoName, commit, tagsByCommit, pipelineStatuses, class: className }: Props = $props();
const base = $derived(`/${ownerHandle}/${repoName}`); const commitHref = $derived(resolve(`${base}/commit/${commit.hash}` as "/")); const treeHref = $derived(resolve(`${base}/tree/${commit.hash}` as "/"));
let expanded = $state(false);
const signOffs = $derived(signOffsFrom(commit.body)); const hasBody = $derived(hasBodyBeyondSignOffs(commit.body));</script>
{#snippet author()} <span class="flex min-w-0 items-center gap-2xs"> {#if commit.authorHandle} <User did={commit.authorDid} handle={commit.authorHandle} class="min-w-0" link /> {:else if commit.authorName && commit.authorEmail && !commit.authorDid} <a href="mailto:{commit.authorEmail}" class="inline-flex min-w-0 items-center text-inherit hover:underline" > <User handle={commit.authorName} class="min-w-0" /> </a> {:else} <User did={commit.authorDid} handle={commit.authorName} class="min-w-0" /> {/if} </span>{/snippet}
{#snippet sha()} <a href={commitHref} title={commit.changeId ? `jj change id: ${commit.changeId}` : undefined} class="rounded-sm bg-background-inset px-2xs typography-monospace-small text-foreground-default no-underline hover:underline" > {commit.shortHash} </a>{/snippet}
{#snippet actions()} <span class="flex items-center gap-xs"> <div class="flex flex-row gap-none"> <CopyButton value={commit.hash} label="Copy SHA" title="Copy SHA" class="fill-foreground-muted stroke-foreground-muted text-foreground-muted" /> <Button variant="ghost" size="small" href={treeHref} title="Browse repository at this commit" aria-label="Browse repository at this commit" icon={FolderCode} class="fill-foreground-muted stroke-foreground-muted text-foreground-muted" /> </div> </span>{/snippet}
{#snippet subject(wrap = false)} {@const s = subjectLayout({ wrap })} <span class={s.wrapper()}> <a href={commitHref} class={s.link()} title={commit.subject}> {commit.subject} </a> {#if signOffs.length > 0} <CommitSignOff {signOffs} class={s.signOff()} /> {/if} {#if hasBody} <CommitBodyToggle {expanded} onclick={() => (expanded = !expanded)} class={s.bodyToggle()} /> {/if} {#if tagsByCommit} <Pending> {#each (await tagsByCommit)[commit.hash] ?? [] as name (name)} <Tag color="gray" class="shrink-0 typography-monospace-regular font-normal" >{name}</Tag > {/each} </Pending> {/if} {#if wrap} {@render pipeline("min-h-0 inline-flex align-middle", "small")} {/if} </span>{/snippet}
{#snippet pipeline(extra = "", size: ButtonVariants["size"] = "regular")} {#if pipelineStatuses} <span class="flex min-h-9 items-center {extra}"> <Pending> {@const status = (await pipelineStatuses)[commit.hash]} {#if status} <PipelineWorkflows {ownerHandle} {repoName} pipeline={status} {size} /> {/if} </Pending> </span> {/if}{/snippet}
{#snippet mobileMenu()} <MoreMenu label="More actions for commit {commit.shortHash}" size="small"> <DropdownItem icon={Copy} onclick={() => navigator.clipboard.writeText(commit.hash)}> Copy SHA </DropdownItem> <DropdownItem icon={FolderCode} href={treeHref}>Browse repository</DropdownItem> </MoreMenu>{/snippet}
{#snippet body(extra = "")} {#if hasBody && expanded} <p class="pb-xs typography-paragraph-small whitespace-pre-wrap text-foreground-muted {extra}" > {commit.body} </p> {/if}{/snippet}
{#snippet when()} <TimeAgo value={commit.when} class="shrink-0 whitespace-nowrap text-foreground-muted" />{/snippet}
<!-- only the columns come from CommitRows; the rows are the row's own, so the cells keep a comfortable height and an expanded body drops underneath them.--><div class={row({ class: className })}> <div class="hidden md:contents"> {@render author()} <span class="flex flex-row items-center gap-xs"> {@render actions()} {@render sha()} </span> {@render subject()} <span class="flex flex-row items-center gap-xs justify-self-end"> {@render pipeline()} {@render when()} </span> </div>
<div class="flex flex-col gap-2xs py-md text-foreground-muted md:hidden"> <div class="flex min-w-0 items-start"> {@render subject(true)} <span class="ml-auto shrink-0">{@render mobileMenu()}</span> </div> {@render body()} <div class="flex min-w-0 items-center gap-2xs"> {@render author()} <span class="shrink-0">committed</span> {@render when()} </div> </div>
<div class="hidden md:contents"> {@render body("md:col-start-3")} </div></div>