import { listBranches, listTree, readBlob } from "@/lib/git";
import { cloneUrls, type Repo } from "@/lib/repo";
import { encodePath } from "@/lib/format";
import { BranchSelect } from "./branch-select";
import { Breadcrumbs } from "./breadcrumbs";
import { CloneButton } from "./clone-button";
import { DocTabs } from "./doc-tabs";
import { FileTable } from "./file-table";
import { BranchIcon } from "./icons";
import { Markdown } from "./markdown";
import { AddFileMenu } from "./repo-add-file-menu";
import { ButtonLink } from "./ui";
const DOCS = [
{ label: "README", pattern: /^readme(\.(md|markdown|txt))?$/i },
{ label: "Code of conduct", pattern: /^code_of_conduct(\.(md|markdown|txt))?$/i },
{ label: "Contributing", pattern: /^contributing(\.(md|markdown|txt))?$/i },
{ label: "License", pattern: /^(licen[cs]e|copying)(\.(md|markdown|txt))?$/i },
];
/** The directory's README, plus (at the root) the other community files, as tabs. */
async function Docs({ repo, gitRef, path }: { repo: Repo; gitRef: string; path: string }) {
const entries = await listTree(repo.dir, gitRef, path);
const docs = (path ? DOCS.slice(0, 1) : DOCS).flatMap((doc) => {
const entry = entries.find((e) => e.type === "blob" && doc.pattern.test(e.name));
return entry ? [{ ...doc, entry }] : [];
});
const tabs = (
await Promise.all(
docs.map(async ({ label, entry }) => {
const blob = await readBlob(repo.dir, gitRef, entry.path);
if (blob.binary || blob.tooLarge) return null;
const text = blob.content.toString("utf8");
return {
label,
href: `${repo.base}/blob/${encodePath(gitRef)}/${encodePath(entry.path)}`,
content: /\.(md|markdown)$/i.test(entry.name) ? (
<Markdown context={{ base: repo.base, gitRef, dir: path }}>{text}</Markdown>
) : (
<pre className="font-mono text-[12px] leading-[20px] whitespace-pre-wrap text-ink">{text}</pre>
),
};
}),
)
).filter((t) => t !== null);
return tabs.length ? <DocTabs tabs={tabs} /> : null;
}
export async function TreeView({ repo, gitRef, path }: { repo: Repo; gitRef: string; path: string }) {
const [branches, urls] = await Promise.all([listBranches(repo.dir), cloneUrls(repo)]);
const onBranch = branches.some((b) => b.name === gitRef);
return (
<div>
<div className="mb-3 flex flex-wrap items-center gap-2">
<BranchSelect base={repo.base} kind="tree" current={gitRef} branches={branches.map((b) => b.name)} path={path} />
{path && <Breadcrumbs base={repo.base} name={repo.name} gitRef={gitRef} path={path} />}
<div className="ml-auto flex items-center gap-1.5">
{!path && (
<ButtonLink href={`${repo.base}/branches`} variant="invisible" icon={<BranchIcon width={14} height={14} />}>
Branches <span className="type-readout text-ink3">{branches.length}</span>
</ButtonLink>
)}
{repo.canWrite && onBranch && <AddFileMenu base={repo.base} branch={gitRef} path={path} />}
{!path && <CloneButton urls={urls} />}
</div>
</div>
<FileTable repo={repo} gitRef={gitRef} path={path} />
<Docs repo={repo} gitRef={gitRef} path={path} />
</div>
);
}