Something went wrong. Try again.
Monorepo for Tangled tangled.org
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228<script lang="ts"> import CodeXml from "$icon/code-xml"; import Download from "$icon/download"; import ExternalLink from "$icon/external-link"; import Eye from "$icon/eye"; import History from "$icon/history"; import type { ResolvedPathname } from "$app/types"; import { hasRenderedView, hasTextView, type BlobKind, type BlobViewMode } from "#lib/api/blob.ts"; import Button from "#lib/components/ui/Button.svelte"; import ButtonGroup from "#lib/components/ui/ButtonGroup.svelte"; import Segment from "#lib/components/ui/Segment.svelte"; import SegmentedControl from "#lib/components/ui/SegmentedControl.svelte"; import Checkbox from "#lib/components/ui/Checkbox.svelte"; import ColorBall from "#lib/components/ui/ColorBall.svelte"; import CopyButton from "#lib/components/ui/CopyButton.svelte"; import Separator from "#lib/components/ui/Separator.svelte"; import Toggle from "#lib/components/ui/Toggle.svelte"; import { LANGUAGE_COLORS, LANGUAGE_COLOR_FALLBACK } from "#lib/components/repo/languageColors.ts"; import RefChip from "#lib/components/repo/RefChip.svelte"; import RepoCrumbs from "#lib/components/repo/RepoCrumbs.svelte"; import { baseName } from "#lib/components/repo/urls.ts"; import { plural } from "#lib/format.ts";
interface Props { path: string; repo?: { ownerHandle: string; repoName: string; ref: string } | null; rawHref: ResolvedPathname; historyHref?: ResolvedPathname | null; kind: BlobKind; // enry language name. the blob xrpc does not expose one yet language?: string | null; sizeLabel?: string | null; lines?: number | null; timeLabel?: string | null; view?: BlobViewMode; wrap?: boolean; showWrap?: boolean; showBlame?: boolean; blame?: boolean; blameLoading?: boolean; blameError?: string | null; copyText?: string | null; }
let { path, repo = null, rawHref, historyHref = null, kind, language = null, sizeLabel = null, lines = null, timeLabel = null, view = $bindable("code"), wrap = $bindable(false), showWrap = false, showBlame = false, blame = $bindable(false), blameLoading = false, blameError = null, copyText = null }: Props = $props();
const fileName = $derived(baseName(path));
const showLines = $derived(lines !== null && view === "code"); const showWrapToggle = $derived(showWrap && hasTextView(kind) && view === "code"); const showBlameToggle = $derived(showBlame && hasTextView(kind) && view === "code"); const languageColor = $derived( language ? (LANGUAGE_COLORS[language] ?? LANGUAGE_COLOR_FALLBACK) : null );</script>
<div class="mb-xs flex flex-col gap-sm typography-paragraph-regular md:mb-md md:grid md:grid-cols-[minmax(0,1fr)_minmax(0,auto)] md:items-center md:gap-md"> <div class="flex min-w-0 flex-wrap items-center justify-between gap-x-sm gap-y-xs md:flex-nowrap md:justify-start" > {#if repo} <div class="flex min-w-0 items-center gap-xs"> <RepoCrumbs ownerHandle={repo.ownerHandle} repoName={repo.repoName} ref={repo.ref} {path} /> </div> {/if}
<div class="flex shrink-0 items-center gap-xs typography-paragraph-small whitespace-nowrap text-foreground-muted" > {#if sizeLabel} <span>{sizeLabel}</span> {/if} {#if sizeLabel && showLines} <Separator variant="dot" /> {/if} {#if showLines && lines !== null} <span>{lines} {plural(lines, "line", "lines")}</span> {/if} {#if timeLabel} {#if sizeLabel || showLines} <Separator variant="dot" /> {/if} <span>{timeLabel}</span> {/if} </div> </div>
<div class="flex min-w-0 flex-col gap-md whitespace-nowrap md:flex-row md:items-center md:gap-xs" > {#if repo || (language && languageColor)} <div class="flex min-w-0 items-center gap-md max-md:justify-between md:flex-row-reverse md:px-xs" > {#if language && languageColor} <div class="flex w-fit shrink-0 items-center gap-xs px-2xs typography-paragraph-small md:px-none" > <ColorBall color={languageColor} /> {language} </div> {/if} {#if repo} <RefChip ownerHandle={repo.ownerHandle} repoName={repo.repoName} ref={repo.ref} class="typography-paragraph-small max-md:ms-auto" /> {/if} </div> {/if}
{#if kind !== "submodule"} <div class="flex items-center gap-xs max-md:justify-between"> {#if showWrapToggle || showBlameToggle || hasRenderedView(kind)} <div class="flex items-center gap-xs"> {#if showBlameToggle} <Checkbox bind:checked={blame} aria-busy={blameLoading} title={blameError ?? undefined} class="px-xs text-foreground-muted" > {blameLoading ? "Blame…" : "Blame"} </Checkbox> {/if} {#if showWrapToggle} <Toggle bind:checked={wrap} size="small" class="px-xs text-foreground-muted" > Wrap </Toggle> {/if} {#if hasRenderedView(kind)} <SegmentedControl bind:value={view} label="View"> <Segment value="code" size="small" icon={CodeXml} title="View source" /> <Segment value="rendered" size="small" icon={Eye} title="View rendered" /> </SegmentedControl> {/if} </div> {/if} <ButtonGroup class="max-md:ms-auto"> {#if copyText !== null} <CopyButton value={copyText} variant="default" label="Copy contents" title="Copy contents" /> {/if} <Button size="small" href={rawHref} download={fileName} title="Download" aria-label="Download" icon={Download} /> <Button size="small" href={rawHref} target="_blank" title="Open raw file" aria-label="Open raw file" icon={ExternalLink} iconSide="right" /> {#if historyHref} <Button size="small" href={historyHref} title="File history" aria-label="File history" icon={History} /> {/if} </ButtonGroup> </div> {/if} </div></div>