Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
Svelte
at icy/tmrrpn
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250<script lang="ts"> import { resolve } from "$app/paths"; import Copy from "$icon/copy"; import CopyCheck from "$icon/copy-check"; import Ellipsis from "$icon/ellipsis"; import FolderCode from "$icon/folder-code"; import Avatar from "$lib/components/ui/Avatar.svelte"; import Pagination from "$lib/components/ui/Pagination.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 { createCopyFeedback } from "$lib/copy.svelte"; import type { CommitStatuses } from "$lib/api/commitStatuses"; import PipelineWorkflows from "./pipelines/PipelineWorkflows.svelte"; import type { CommitSummary } from "./types";
interface Props { ownerHandle: string; repoName: string; ref: string; commits: CommitSummary[]; tagsByCommit?: Record<string, string[]> | Promise<Record<string, string[]>>; pipelineStatuses?: Promise<CommitStatuses>; page: number; pageCount: number; onPageChange?: (page: number) => void; }
let { ownerHandle, repoName, ref, commits, tagsByCommit, pipelineStatuses, page, pageCount, onPageChange }: Props = $props();
const base = $derived(`/${ownerHandle}/${repoName}`);
let expanded = $state<Record<string, boolean>>({}); const copyFeedback = createCopyFeedback();</script>
{#snippet authorCell(commit: CommitSummary)} <span class="flex items-center gap-1"> <!-- no did/handle on a commit summary, always the placeholder --> <Avatar size="large" /> {#if commit.authorEmail} <a href="mailto:{commit.authorEmail}" class="no-underline hover:underline"> {commit.authorName} </a> {:else} <span>{commit.authorName}</span> {/if} </span>{/snippet}
{#snippet shaChip(commit: CommitSummary, mobile: boolean)} <a href={resolve(`${base}/commit/${commit.hash}` as "/")} title={commit.changeId ? `jj change id: ${commit.changeId}` : undefined} class="flex items-center gap-2 rounded-sm bg-background-inset px-2 {mobile ? 'py-1' : 'py-0.5'} no-underline hover:underline" > {commit.shortHash} </a>{/snippet}
{#snippet treeLink(commit: CommitSummary)} <a href={resolve(`${base}/tree/${commit.hash}` as "/")} class="rounded-sm p-1 hover:bg-background-inset" title="Browse repository at this commit" aria-label="Browse repository at this commit" > <FolderCode class="size-4" aria-hidden="true" /> </a>{/snippet}
{#snippet copyButton(commit: CommitSummary)} <button type="button" class="cursor-pointer rounded-sm p-1 hover:bg-background-inset" title="Copy SHA" aria-label="Copy SHA" onclick={() => void copyFeedback.copy(commit.hash)} > {#if copyFeedback.copied === commit.hash} <CopyCheck class="size-4" aria-hidden="true" /> {:else} <Copy class="size-4" aria-hidden="true" /> {/if} </button>{/snippet}
{#snippet pipelineCell(commit: CommitSummary)} {#if pipelineStatuses} <span class="flex min-h-9 items-center"> <Pending> {@const pipeline = (await pipelineStatuses)[commit.hash]} {#if pipeline} <PipelineWorkflows {ownerHandle} {repoName} {pipeline} /> {/if} </Pending> </span> {/if}{/snippet}
{#snippet messageCell(commit: CommitSummary)} <div> <a href={resolve(`${base}/commit/${commit.hash}` as "/")} class="text-foreground-default no-underline hover:underline" > {commit.subject} </a> {#if commit.body} <button type="button" class="cursor-pointer rounded-sm bg-background-inset px-1 py-0.5 hover:bg-background-muted" aria-expanded={expanded[commit.hash] === true} aria-label="Toggle commit body" onclick={() => (expanded[commit.hash] = !expanded[commit.hash])} > <Ellipsis class="size-3" aria-hidden="true" /> </button> {/if} {#if tagsByCommit} <Pending> {#each (await tagsByCommit)[commit.hash] ?? [] as name (name)} <Tag color="gray" class="ml-2 font-mono">{name}</Tag> {/each} </Pending> {/if} </div> {#if commit.body && expanded[commit.hash]} <p class="mt-1 typography-paragraph-regular whitespace-pre-wrap text-foreground-muted"> {commit.body} </p> {/if}{/snippet}
<section id="commit-table" class="overflow-x-auto rounded-sm border border-border-default bg-background-default px-6 py-4"> <h2 class="mb-4 typography-paragraph-regular font-bold">Commits</h2>
{#if commits.length === 0} <p class="py-6 text-center text-foreground-subtle">No commits at {ref}.</p> {:else} <div class="hidden divide-y divide-border-default md:flex md:flex-col"> <div class="grid grid-cols-16 gap-4"> <div class="col-span-3 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" > Author </div> <div class="col-span-3 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" > Commit </div> <div class="col-span-7 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" > Message </div> <div class="col-span-1 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" > <span>Pipeline</span> </div> <div class="col-span-2 justify-self-end py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" > Date </div> </div> {#each commits as commit (commit.hash)} <div class="grid grid-cols-16 gap-4 py-3"> <div class="col-span-3 align-top"> {@render authorCell(commit)} </div> <div class="col-span-3 flex items-start align-top font-mono"> {@render shaChip(commit, false)} <!-- TODO: unverified rows indent the actions by a verified-shield width, but no shield renders yet. drop the ml-6 or render the shield once commit verification lands --> <div class="ml-6 inline-flex"> {@render copyButton(commit)} {@render treeLink(commit)} </div> </div> <div class="col-span-7 align-top"> {@render messageCell(commit)} </div> <div class="col-span-1 flex items-start align-top"> {@render pipelineCell(commit)} </div> <div class="col-span-2 justify-self-end align-top text-foreground-muted"> <TimeAgo value={commit.when} /> </div> </div> {/each} </div>
<div class="md:hidden"> {#each commits as commit, index (commit.hash)} <div class="relative mb-2 p-2 {index < commits.length - 1 ? 'border-b border-border-default' : ''}" > <div class="flex items-center justify-between"> <div class="flex-1"> {@render messageCell(commit)} </div> {@render treeLink(commit)} </div> <div class="mt-2 flex items-center gap-1 typography-paragraph-small text-foreground-muted" > <span class="font-mono"> {@render shaChip(commit, true)} </span> <span aria-hidden="true">·</span> {@render authorCell(commit)} {#if commit.when} <span aria-hidden="true">·</span> <TimeAgo value={commit.when} class="shrink-0 whitespace-nowrap" /> {/if} {@render pipelineCell(commit)} </div> </div> {/each} </div> {/if}</section>
{#if pageCount > 1} <div class="mt-4 flex justify-center"> <Pagination {page} total={pageCount} labels onchange={onPageChange} class="gap-5" /> </div>{/if}