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 / BlobHeader.svelte
3.7 kB 123 lines
1<script lang="ts"> 2 import Check from "$icon/check"; 3 import Copy from "$icon/copy"; 4 import Download from "$icon/download"; 5 import { hasRenderedView, hasTextView, type BlobKind, type BlobViewMode } from "$lib/api/blob"; 6 import { createCopyFeedback } from "$lib/copy.svelte"; 7 import Button from "$lib/components/ui/Button.svelte"; 8 import ButtonGroup from "$lib/components/ui/ButtonGroup.svelte"; 9 import Checkbox from "$lib/components/ui/Checkbox.svelte"; 10 import { LANGUAGE_COLORS, LANGUAGE_COLOR_FALLBACK } from "./language-colors"; 11 import RefChip from "./RefChip.svelte"; 12 import RepoCrumbs from "./RepoCrumbs.svelte"; 13 import { baseName, rawBlobHref } from "./urls"; 14 15 interface Props { 16 ownerHandle: string; 17 repoName: string; 18 ref: string; 19 path: string; 20 kind: BlobKind; 21 // enry language name. the blob xrpc does not expose one yet 22 language?: string | null; 23 sizeLabel?: string | null; 24 lines?: number | null; 25 view?: BlobViewMode; 26 wrap?: boolean; 27 showWrap?: boolean; 28 copyText?: string | null; 29 } 30 31 let { 32 ownerHandle, 33 repoName, 34 ref, 35 path, 36 kind, 37 language = null, 38 sizeLabel = null, 39 lines = null, 40 view = $bindable("code"), 41 wrap = $bindable(false), 42 showWrap = false, 43 copyText = null 44 }: Props = $props(); 45 46 const rawHref = $derived(rawBlobHref(ownerHandle, repoName, ref, path)); 47 const fileName = $derived(baseName(path)); 48 49 const showLines = $derived(lines !== null && view === "code"); 50 const showWrapToggle = $derived(showWrap && hasTextView(kind) && view === "code"); 51 const languageColor = $derived( 52 language ? (LANGUAGE_COLORS[language] ?? LANGUAGE_COLOR_FALLBACK) : null 53 ); 54 55 const copyFeedback = createCopyFeedback(); 56 const copyContents = () => { 57 if (copyText !== null) void copyFeedback.copy(copyText); 58 }; 59</script> 60 61<div class="flex flex-wrap items-center justify-between gap-2 typography-paragraph-regular mb-2"> 62 <div class="flex min-w-0 items-center gap-3"> 63 <RepoCrumbs {ownerHandle} {repoName} {ref} {path} /> 64 65 <div class="flex items-center gap-2 text-foreground-muted"> 66 {#if sizeLabel} 67 <span>{sizeLabel}</span> 68 {/if} 69 {#if sizeLabel && showLines} 70 <span class="select-none" aria-hidden="true">&middot;</span> 71 {/if} 72 {#if showLines} 73 <span>{lines} {lines === 1 ? "line" : "lines"}</span> 74 {/if} 75 </div> 76 </div> 77 78 <div class="flex flex-wrap items-center gap-2 whitespace-nowrap max-md:order-3 max-md:w-full"> 79 {#if showWrapToggle} 80 <Checkbox bind:checked={wrap} class="px-2 text-foreground-muted">Wrap</Checkbox> 81 {/if} 82 <RefChip {ownerHandle} {repoName} {ref} /> 83 {#if language && languageColor} 84 <div 85 class="flex w-fit items-center gap-2 rounded border border-border-default bg-background-canvas px-2 py-1 whitespace-nowrap" 86 > 87 <div 88 class="size-2 rounded-full" 89 style:background="radial-gradient(circle at 35% 35%, color-mix(in srgb, {languageColor} 70%, 90 white), {languageColor} 30%, color-mix(in srgb, {languageColor} 85%, black))" 91 ></div> 92 {language} 93 </div> 94 {/if} 95 {#if kind !== "submodule"} 96 <ButtonGroup> 97 {#if hasRenderedView(kind)} 98 <Button size="sm" onclick={() => (view = view === "code" ? "rendered" : "code")}> 99 View {view === "rendered" ? "code" : "rendered"} 100 </Button> 101 {/if} 102 <Button size="sm" href={rawHref}>View raw</Button> 103 {#if copyText !== null} 104 <Button 105 size="sm" 106 onclick={copyContents} 107 title="Copy contents" 108 aria-label="Copy contents" 109 icon={copyFeedback.copied !== null ? Check : Copy} 110 /> 111 {/if} 112 <Button 113 size="sm" 114 href={rawHref} 115 download={fileName} 116 title="Download" 117 aria-label="Download" 118 icon={Download} 119 /> 120 </ButtonGroup> 121 {/if} 122 </div> 123</div>