This repository has no description
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">·</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>