This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

core / web / src / lib / components / repo / CommitLogView.svelte
6.3 kB 215 lines
1<script lang="ts"> 2 import { resolve } from "$app/paths"; 3 import Copy from "$icon/copy"; 4 import CopyCheck from "$icon/copy-check"; 5 import Ellipsis from "$icon/ellipsis"; 6 import FolderCode from "$icon/folder-code"; 7 import Avatar from "$lib/components/ui/Avatar.svelte"; 8 import Pagination from "$lib/components/ui/Pagination.svelte"; 9 import Tag from "$lib/components/ui/Tag.svelte"; 10 import TimeAgo from "$lib/components/ui/TimeAgo.svelte"; 11 import { createCopyFeedback } from "$lib/copy.svelte"; 12 import type { CommitSummary } from "./types"; 13 14 interface Props { 15 ownerHandle: string; 16 repoName: string; 17 ref: string; 18 commits: CommitSummary[]; 19 tagsByCommit?: Record<string, string[]>; 20 page: number; 21 pageCount: number; 22 onPageChange?: (page: number) => void; 23 } 24 25 let { 26 ownerHandle, 27 repoName, 28 ref, 29 commits, 30 tagsByCommit = {}, 31 page, 32 pageCount, 33 onPageChange 34 }: Props = $props(); 35 36 const base = $derived(`/${ownerHandle}/${repoName}`); 37 38 let expanded = $state<Record<string, boolean>>({}); 39 const copyFeedback = createCopyFeedback(); 40</script> 41 42{#snippet authorCell(commit: CommitSummary)} 43 <span class="flex items-center gap-1"> 44 <!-- no did/handle on a commit summary, always the placeholder --> 45 <Avatar size="size-6" /> 46 {#if commit.authorEmail} 47 <a href="mailto:{commit.authorEmail}" class="no-underline hover:underline"> 48 {commit.authorName} 49 </a> 50 {:else} 51 <span>{commit.authorName}</span> 52 {/if} 53 </span> 54{/snippet} 55 56{#snippet shaChip(commit: CommitSummary, mobile: boolean)} 57 <a 58 href={resolve(`${base}/commit/${commit.hash}` as "/")} 59 title={commit.changeId ? `jj change id: ${commit.changeId}` : undefined} 60 class="flex items-center gap-2 rounded bg-background-inset px-2 {mobile 61 ? 'py-1' 62 : 'py-0.5'} text-foreground-muted no-underline hover:underline" 63 > 64 {commit.shortHash} 65 </a> 66{/snippet} 67 68{#snippet treeLink(commit: CommitSummary)} 69 <a 70 href={resolve(`${base}/tree/${commit.hash}` as "/")} 71 class="rounded p-1 hover:bg-background-inset" 72 title="Browse repository at this commit" 73 aria-label="Browse repository at this commit" 74 > 75 <FolderCode class="size-4" aria-hidden="true" /> 76 </a> 77{/snippet} 78 79{#snippet copyButton(commit: CommitSummary)} 80 <button 81 type="button" 82 class="cursor-pointer rounded p-1 hover:bg-background-inset" 83 title="Copy SHA" 84 aria-label="Copy SHA" 85 onclick={() => void copyFeedback.copy(commit.hash)} 86 > 87 {#if copyFeedback.copied === commit.hash} 88 <CopyCheck class="size-4" aria-hidden="true" /> 89 {:else} 90 <Copy class="size-4" aria-hidden="true" /> 91 {/if} 92 </button> 93{/snippet} 94 95{#snippet messageCell(commit: CommitSummary)} 96 <div> 97 <a 98 href={resolve(`${base}/commit/${commit.hash}` as "/")} 99 class="text-foreground-default no-underline hover:underline" 100 > 101 {commit.subject} 102 </a> 103 {#if commit.body} 104 <button 105 type="button" 106 class="cursor-pointer rounded bg-background-inset px-1 py-0.5 hover:bg-background-muted" 107 aria-expanded={expanded[commit.hash] === true} 108 aria-label="Toggle commit body" 109 onclick={() => (expanded[commit.hash] = !expanded[commit.hash])} 110 > 111 <Ellipsis class="size-3" aria-hidden="true" /> 112 </button> 113 {/if} 114 {#each tagsByCommit[commit.hash] ?? [] as name (name)} 115 <Tag color="gray" class="ml-2 font-mono">{name}</Tag> 116 {/each} 117 </div> 118 {#if commit.body && expanded[commit.hash]} 119 <p class="mt-1 typography-paragraph-regular whitespace-pre-wrap text-foreground-muted"> 120 {commit.body} 121 </p> 122 {/if} 123{/snippet} 124 125<section id="commit-table" class="overflow-x-auto rounded bg-background-default px-6 py-4"> 126 <h2 class="mb-4 typography-paragraph-regular font-bold">Commits</h2> 127 128 {#if commits.length === 0} 129 <p class="py-6 text-center text-foreground-subtle">No commits at {ref}.</p> 130 {:else} 131 <div class="hidden divide-y divide-border-default md:flex md:flex-col"> 132 <div class="grid grid-cols-14 gap-4"> 133 <div 134 class="col-span-3 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" 135 > 136 Author 137 </div> 138 <div 139 class="col-span-3 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" 140 > 141 Commit 142 </div> 143 <div 144 class="col-span-6 py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" 145 > 146 Message 147 </div> 148 <div 149 class="col-span-2 justify-self-end py-2 text-left typography-paragraph-regular font-bold text-foreground-muted" 150 > 151 Date 152 </div> 153 </div> 154 {#each commits as commit (commit.hash)} 155 <div class="grid grid-cols-14 gap-4 py-3"> 156 <div class="col-span-3 align-top"> 157 {@render authorCell(commit)} 158 </div> 159 <div class="col-span-3 flex items-start align-top font-mono"> 160 {@render shaChip(commit, false)} 161 <!-- TODO: unverified rows indent the actions by a verified-shield 162 width, but no shield renders yet. drop the ml-6 or render the 163 shield once commit verification lands --> 164 <div class="ml-6 inline-flex"> 165 {@render copyButton(commit)} 166 {@render treeLink(commit)} 167 </div> 168 </div> 169 <div class="col-span-6 align-top"> 170 {@render messageCell(commit)} 171 </div> 172 <div class="col-span-2 justify-self-end align-top text-foreground-muted"> 173 <TimeAgo value={commit.when} /> 174 </div> 175 </div> 176 {/each} 177 </div> 178 179 <div class="md:hidden"> 180 {#each commits as commit, index (commit.hash)} 181 <div 182 class="relative mb-2 p-2 {index < commits.length - 1 183 ? 'border-b border-border-default' 184 : ''}" 185 > 186 <div class="flex items-center justify-between"> 187 <div class="flex-1"> 188 {@render messageCell(commit)} 189 </div> 190 {@render treeLink(commit)} 191 </div> 192 <div 193 class="mt-2 flex items-center gap-1 typography-paragraph-small text-foreground-muted" 194 > 195 <span class="font-mono"> 196 {@render shaChip(commit, true)} 197 </span> 198 <span aria-hidden="true">&middot;</span> 199 {@render authorCell(commit)} 200 {#if commit.when} 201 <span aria-hidden="true">&middot;</span> 202 <TimeAgo value={commit.when} /> 203 {/if} 204 </div> 205 </div> 206 {/each} 207 </div> 208 {/if} 209</section> 210 211{#if pageCount > 1} 212 <div class="mt-4 flex justify-center"> 213 <Pagination {page} total={pageCount} labels onchange={onPageChange} class="gap-5" /> 214 </div> 215{/if}