<script lang="ts">
import Code from "$icon/code";
import FolderTree from "$icon/folder-tree";
import GitBranch from "$icon/git-branch";
import Logs from "$icon/logs";
import Tags from "$icon/tags";
import TriangleAlert from "$icon/triangle-alert";
import BranchList from "#lib/components/repo/BranchList.svelte";
import CommitList from "#lib/components/repo/CommitList.svelte";
import EmptyRepo from "#lib/components/repo/EmptyRepo.svelte";
import FileTree from "#lib/components/repo/FileTree.svelte";
import LanguageList from "#lib/components/repo/LanguageList.svelte";
import RepoSection from "#lib/components/repo/RepoSection.svelte";
import Readme from "#lib/components/repo/Readme.svelte";
import { repoMarkupContext } from "#lib/markup/pageContext.ts";
import RepoIndexPanelSkeleton from "#lib/components/repo/RepoIndexPanelSkeleton.svelte";
import RepoReadmeSkeleton from "#lib/components/repo/RepoReadmeSkeleton.svelte";
import TagList from "#lib/components/repo/TagList.svelte";
import RepoToolbar from "#lib/components/repo/RepoToolbar.svelte";
import Spinner from "#lib/components/ui/Spinner.svelte";
import Pending from "#lib/components/ui/Pending.svelte";
import Card from "#lib/components/ui/Card.svelte";
import type { Settled } from "#lib/api/load.ts";
import { hasGit } from "#lib/api/gitclient.ts";
import { withImportAvailability } from "#lib/api/repoIndex.ts";
import type { loadRepoIndex } from "#lib/server/repoIndex.ts";
import type { ImportState } from "#lib/api/repo.ts";
import type { CommitStatuses } from "#lib/api/commitStatuses.ts";
import { NO_ACCESS, type RepoAccess } from "#lib/api/access.ts";
import type { RepoInfo } from "#lib/components/repo/types.ts";
interface Props {
repo: RepoInfo;
data: Settled<Awaited<ReturnType<typeof loadRepoIndex>>>;
bobbinUrl: string;
pipelineStatuses?: Promise<CommitStatuses | undefined>;
importStatus?: ImportState | null;
access?: RepoAccess | Promise<RepoAccess>;
}
let {
importStatus = null,
access = NO_ACCESS,
pipelineStatuses,
bobbinUrl,
data,
repo
}: Props = $props();
const base = $derived(`/${repo.ownerHandle}/${repo.name}`);
</script>
<Pending>
{#snippet skeleton()}
<RepoIndexPanelSkeleton />
{/snippet}
{@const content = await data.content}
{@const availability = content && withImportAvailability(content.availability, importStatus)}
{#if !hasGit(repo) || !content || availability !== "ok"}
<Card class="relative mx-auto w-full">
{#if !hasGit(repo) || !content}
<p class="flex h-96 items-center justify-center text-center text-foreground-subtle">
This repository doesn't support git. You can still create issues.
</p>
{:else if availability === "not-synced"}
<p class="flex h-96 items-center justify-center text-center text-foreground-subtle">
This repository hasn't been mirrored yet. Its content will appear here once it
syncs.
</p>
{:else if availability === "error"}
<div
class="flex h-96 items-center justify-center text-center text-foreground-danger"
>
<span class="flex items-center gap-xs">
<TriangleAlert class="size-5 shrink-0" aria-hidden="true" />
Something went wrong loading this repository. Try again in a moment.
</span>
</div>
{:else if availability === "importing"}
<div class="flex h-96 items-center justify-center text-center">
<span class="flex items-center gap-2">
<Spinner class="size-5 shrink-0" />
This repository is still importing from {repo.importSource ?? "its source"}.
Its content will appear here as it lands.
</span>
</div>
{:else if availability === "import-failed"}
<div
class="flex h-96 items-center justify-center text-center text-foreground-danger"
>
<div>
<span class="flex items-center justify-center gap-2">
<TriangleAlert class="size-5 shrink-0" aria-hidden="true" />
The import from {repo.importSource ?? "its source"} failed.
</span>
<p class="mt-2 text-foreground-muted">
Ask your knot operator for support.
</p>
</div>
</div>
{:else if availability === "empty"}
<EmptyRepo {repo} canPush={(await access).canPush} />
{/if}
</Card>
{:else}
<Card padding="none" class="mx-auto w-full">
<div class="p-sm">
<RepoToolbar
{repo}
ref={content.ref}
refs={content.refs}
totalCommits={content.totalCommits}
totalBranches={content.totalBranches}
totalTags={content.totalTags}
{bobbinUrl}
/>
</div>
<div
class="grid grid-cols-1 border-t border-border-default md:grid-cols-[minmax(0,3fr)_minmax(0,2fr)]"
>
<RepoSection
class="pb-2 md:border-r md:border-border-default"
title="Files"
icon={FolderTree}
headerClass="max-md:hidden"
bodyClass="-mx-1.5"
clamp
>
<FileTree
ownerHandle={repo.ownerHandle}
repoName={repo.name}
ref={content.ref}
entries={content.files}
clamp
/>
</RepoSection>
<div class="hidden flex-col divide-y divide-border-default md:flex">
{#if content.commits.length > 0}
<RepoSection
title="Commits"
icon={Logs}
href={`${base}/commits/${encodeURIComponent(content.ref)}`}
bodyClass="-mx-1.5"
count={content.totalCommits}
>
<CommitList
ownerHandle={repo.ownerHandle}
repoName={repo.name}
commits={content.commits}
tagsByCommit={content.tagsByCommit}
{pipelineStatuses}
/>
</RepoSection>
{/if}
{#if content.branches.length > 0}
<RepoSection
title="Branches"
icon={GitBranch}
href={`${base}/branches`}
bodyClass="-mx-1.5"
count={content.totalBranches}
>
<BranchList
ownerHandle={repo.ownerHandle}
repoName={repo.name}
branches={content.branches}
currentRef={content.ref}
/>
</RepoSection>
{/if}
{#if content.tags.length > 0}
<RepoSection
title="Tags"
icon={Tags}
href={`${base}/tags`}
bodyClass="-mx-1.5"
count={content.totalTags}
>
<TagList
ownerHandle={repo.ownerHandle}
repoName={repo.name}
tags={content.tags}
/>
</RepoSection>
{/if}
<Pending>
{@const languages = await data.languages}
{#if languages.length > 0}
<RepoSection title="Languages" icon={Code} href={`${base}/languages`}>
<LanguageList {languages} />
</RepoSection>
{/if}
</Pending>
</div>
</div>
</Card>
{/if}
</Pending>
<Pending>
{#snippet skeleton()}
<RepoReadmeSkeleton />
{/snippet}
{@const content = await data.content}
{#if content?.readme}
<Readme
filename={content.readme.filename}
contents={content.readme.contents}
markup={repoMarkupContext(`${repo.ownerHandle}/${repo.name}`, content.ref)}
/>
{/if}
</Pending>