// snapshot views: a commit's tree, one of its blobs, or its files of one // language. only the tree is rendered at the tip: the static blob page is // richer than what the client can make import type { Commit, TreeEntry } from "./git/types.ts"; import { objectAt } from "./git/walk.ts"; import { detectLanguage, languageGroup } from "./languages.ts"; import { canonical, encodePath, isPageView, type Site, type View } from "./route.ts"; import { commitTime, humanSize, isImage, looksBinary, looksGenerated, plural, utf8 } from "./util.ts"; const TREE = 0o040000; const SYMLINK = 0o120000; const GITLINK = 0o160000; const kindOf = (entry: TreeEntry) => entry.mode & 0o170000; export async function kindAtTip(site: Site, path: string[]): Promise<"tree" | "blob" | null> { if (!site.page.tip) return null; if (path.length === 0) return "tree"; const commit = await site.repo.commit(site.page.tip); const object = await objectAt(site.repo, commit.tree, path); if (!object) return null; const kind = object.mode & 0o170000; return kind === TREE ? "tree" : kind === GITLINK ? null : "blob"; } export async function href(site: Site, view: View): Promise { const needsTip = view.kind === "tree" || view.kind === "blob" || view.kind === "history"; return canonical(site.page, view, needsTip ? await kindAtTip(site, view.path) : null); } async function entriesAt(site: Site, commit: Commit, path: string[]): Promise { const object = await objectAt(site.repo, commit.tree, path); if (!object || (object.mode & 0o170000) !== TREE) throw new Error(`${path.join("/")} is not a tree`); return site.repo.tree(object.oid); } /** `a/b/c` when `a` holds only `b`, which holds only `c`: a chain of lone * directories reads better as one link than as three clicks */ async function collapseLoneDirs(site: Site, entry: TreeEntry): Promise { const names = [entry.name]; let oid = entry.oid; for (;;) { const [only, ...rest] = await site.repo.tree(oid); if (rest.length > 0 || !only || kindOf(only) !== TREE) return names; names.push(only.name); oid = only.oid; } } /** the same shape as the static topbar. the ref switcher is baked into the * static page and only applies at the tip, so it is borrowed from there; * elsewhere the commit panel above says which commit this is */ export async function topbar(site: Site, view: View, stats: string[], actions: Node[] = []): Promise { const { name } = site; const { kind, oid, path } = view; const crumbs = (