<script module lang="ts">
import { tv, type VariantProps } from "tailwind-variants";
export const commitHeader = tv({
slots: {
root: "flex flex-col gap-md text-foreground-default",
header: "flex flex-col gap-xs",
headerSubject: "min-w-0 typography-paragraph-large wrap-break-word",
headerBody: "typography-monospace-regular wrap-anywhere whitespace-pre-wrap",
commitDetails: "flex flex-col gap-xs typography-paragraph-regular",
commitDetailsRow: "inline-flex min-h-7 flex-wrap items-center",
commitDetailsTitle: "w-32 text-foreground-muted select-none"
}
});
export type CommitHeaderVariants = VariantProps<typeof commitHeader>;
</script>
<script lang="ts">
import { resolve } from "$app/paths";
import type { CommitDetail } from "#lib/api/repo.ts";
import Pending from "#lib/components/ui/Pending.svelte";
import CommitAuthor from "#lib/components/repo/CommitAuthor.svelte";
import TimeAgo from "#lib/components/ui/TimeAgo.svelte";
import { formatDateTime } from "#lib/format.ts";
import type { CommitStatuses } from "#lib/api/commitStatuses.ts";
import PipelineWorkflows from "#lib/components/repo/pipelines/PipelineWorkflows.svelte";
import Separator from "#lib/components/ui/Separator.svelte";
import Button from "#lib/components/ui/Button.svelte";
import CopyButton from "#lib/components/ui/CopyButton.svelte";
import CommitShaButton from "#lib/components/repo/CommitShaButton.svelte";
import { verifiedSignature } from "#lib/components/repo/commitSignature.ts";
import Tooltip from "#lib/components/ui/Tooltip.svelte";
interface Props {
ownerHandle: string;
repoName: string;
commit: CommitDetail;
// empty for a root commit
parent?: string;
changeId?: string;
pipelineStatuses?: Promise<CommitStatuses>;
refNames?: string[] | Promise<string[]>;
}
let {
ownerHandle,
repoName,
commit,
parent = "",
changeId = "",
pipelineStatuses,
refNames
}: Props = $props();
const base = $derived(`/${ownerHandle}/${repoName}`);
const showCommitter = $derived(
commit.committerEmail !== "" && commit.committerEmail !== commit.authorEmail
);
const parentShort = $derived(parent.slice(0, 8));
const changeIdShort = $derived(changeId.slice(0, 8));
const absoluteTime = $derived(formatDateTime(commit.committerWhen));
const styles = $derived(commitHeader());
</script>
<section class={styles.root()}>
<div class={styles.header()}>
<span class="flex min-h-9 flex-row items-center justify-between">
<p class={styles.headerSubject()}>
{commit.subject}
{#if refNames}
<Pending>
{#each await refNames as name (name)}
<span
class="ml-2xs min-w-0 rounded-md bg-background-inset px-2xs typography-monospace-regular font-normal wrap-anywhere text-foreground-default"
>
{name}
</span>
{/each}
</Pending>
{/if}
</p>
{#if pipelineStatuses}
<Pending>
{@const pipeline = (await pipelineStatuses)[commit.hash]}
{#if pipeline}
<PipelineWorkflows
{ownerHandle}
{repoName}
{pipeline}
variant="symbol"
summary="long"
/>
{/if}
</Pending>
{/if}
</span>
{#if commit.body}
<p class={styles.headerBody()}>
{commit.body}
</p>
{/if}
</div>
<Separator />
<div class={styles.commitDetails()}>
{#snippet attribution(
label: string,
name: string,
email: string,
did?: string,
handle?: string
)}
<span class={styles.commitDetailsRow()}>
<span class={styles.commitDetailsTitle()}>{label}</span>
<CommitAuthor {name} {email} {did} {handle} />
</span>
{/snippet}
{@render attribution(
"Author",
commit.authorName,
commit.authorEmail,
commit.authorDid,
commit.authorHandle
)}
{#each commit.coAuthors as coAuthor (coAuthor.email)}
{@render attribution("Co-author", coAuthor.name, coAuthor.email)}
{/each}
{#if showCommitter}
{@render attribution("Committer", commit.committerName, commit.committerEmail)}
{/if}
{#if commit.committerWhen}
<span class={styles.commitDetailsRow()}>
<span class={styles.commitDetailsTitle()}>Date</span>
<span class="flex flex-row gap-2xs">
<TimeAgo value={commit.committerWhen} variant="full" />
{#if absoluteTime}
<span class="hidden text-foreground-muted sm:inline">({absoluteTime})</span>
{/if}
</span>
</span>
{/if}
{#snippet copyId(label: string, tooltip: string, value: string, short: string)}
<span class={styles.commitDetailsRow()}>
<span class={styles.commitDetailsTitle()}>{label}</span>
<Tooltip content={tooltip}>
<CopyButton
{value}
feedback="icon"
aria-label="{tooltip} {short}"
class="-ml-2 typography-monospace-regular"
>
<span class="sm:hidden">{short}</span>
<span class="hidden sm:inline">{value}</span>
</CopyButton>
</Tooltip>
</span>
{/snippet}
{#if commit.hash}
{@const signature = verifiedSignature(commit)}
{#if signature}
<span class={styles.commitDetailsRow()}>
<span class={styles.commitDetailsTitle()}>Commit</span>
<CommitShaButton
hash={commit.hash}
shortHash={commit.shortHash}
{changeId}
{signature}
full
/>
</span>
{:else}
{@render copyId("Commit", "Copy SHA", commit.hash, commit.shortHash)}
{/if}
{/if}
{#if parent}
<span class={styles.commitDetailsRow()}>
<span class={styles.commitDetailsTitle()}>Parent</span>
<Button
variant="ghost"
size="small"
href={resolve(`${base}/commit/${parent}` as "/")}
class="-ml-2 typography-monospace-regular"
>
<span class="sm:hidden">{parentShort}</span>
<span class="hidden sm:inline">{parent}</span>
</Button>
</span>
{/if}
{#if changeId}
{@render copyId("Change ID", "Copy change ID", changeId, changeIdShort)}
{/if}
</div>
</section>