import type { ReleaseAssetRow, ReleaseRow, RepositoryRow, } from "../../db/index.ts"; import type { SessionUser } from "../../middleware/session.ts"; import { Layout } from "../layout.tsx"; import { RepoHeader } from "../repos/RepoHeader.tsx"; import { RepoNav } from "../repos/RepoNav.tsx"; function formatBytes(bytes: number): string { if (bytes < 1024) return `${bytes} B`; if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; return `${(bytes / (1024 * 1024)).toFixed(1)} MB`; } interface SourceArchive { format: string; filename: string; size: number; } interface ReleaseDetailProps { user: SessionUser | null; repo: RepositoryRow; release: ReleaseRow; notesHtml: string; assets: ReleaseAssetRow[]; sourceArchives: SourceArchive[]; sourceArchivesPending?: boolean; } export function ReleaseDetail({ user, repo, release, notesHtml, assets, sourceArchives, sourceArchivesPending, }: ReleaseDetailProps) { const hasDownloads = sourceArchives.length > 0 || assets.length > 0 || sourceArchivesPending; return (

{release.name || release.tag_name}

{release.tag_name}
{notesHtml && (
{notesHtml}
)} {hasDownloads && (

Downloads

    {assets.map((asset) => (
  • {asset.filename} {formatBytes(asset.size)}
  • ))} {sourceArchives.map((archive) => (
  • {archive.filename} Source code ({archive.format}) {formatBytes(archive.size)}
  • ))} {sourceArchivesPending && ( [ ["zip", ".zip"], ["tar.gz", ".tar.gz"], ["tar.zst", ".tar.zst"], ] as const ).map(([format, ext]) => (
  • {`${repo.name}-${release.commit_hash.slice(0, 8)}${ext}`} Source code ({format}) — generating…
  • ))}
)}
← All releases {user?.isAdmin && (
Delete release
Permanently delete this release and all its files?
)}
); }