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