diff --git a/packages/git-ui/src/pages/packages.jsx b/packages/git-ui/src/pages/packages.jsx index 6a7c805..97f47f5 100644 --- a/packages/git-ui/src/pages/packages.jsx +++ b/packages/git-ui/src/pages/packages.jsx @@ -3,15 +3,33 @@ import { PackageIcon } from 'lucide-react'; import { Skeleton } from '#/components/atoms/skeleton.jsx'; import { EmptyState } from '#/components/molecules/empty-state.jsx'; import { PackageCard } from '#/components/molecules/package-card.jsx'; +import { TabBar } from '#/components/molecules/tab-bar.jsx'; import { repoRecord } from '#/lib/git.js'; import { loadPackages } from '#/lib/packages.js'; +/** What each tab holds, and what an empty one says. */ +const TABS = [ + { key: '', label: 'All', noun: 'package', plural: 'packages' }, + { key: 'npm', label: 'npm', noun: 'npm package', plural: 'npm packages' }, + { + key: 'oci', + label: 'images', + noun: 'container image', + plural: 'container images', + }, +]; + /** * Everything this repository publishes to the account's own registries, with * the command that installs each one. The account holds the registries, so a * reader installs from the same host that serves this page. + * + * A repository can name many packages, so the page filters by registry. The + * registries there are (npm and oci) stay fixed tabs rather than being read + * from the data, so a tab is a stable address and the "nothing here" line + * tells the two registries apart. */ -export function PackagesPage({ repo }) { +export function PackagesPage({ repo, registry }) { const record = repoRecord(repo); const { data } = useQuery({ queryKey: ['packages', repo], @@ -22,8 +40,29 @@ export function PackagesPage({ repo }) { return

No such repository.

; } + const active = TABS.find((entry) => entry.key === registry) ?? TABS[0]; + const at = `/${encodeURIComponent(repo)}/packages`; + const of = (entry) => + (data ?? []).filter( + (pkg) => entry.key === '' || pkg.registry === entry.key, + ); + const shown = data ? of(active) : []; + return ( - <> +
+
+ ({ + key: entry.key, + label: entry.label, + href: entry.key === '' ? at : `${at}/${entry.key}`, + count: data ? of(entry).length : undefined, + }))} + /> +
+ {!data ? (
@@ -36,13 +75,21 @@ export function PackagesPage({ repo }) { description="This repository names no published package." />
+ ) : shown.length === 0 ? ( +
+ } + title={`No ${active.plural}`} + description={`This repository names no ${active.noun}.`} + /> +
) : ( )} - +
); } diff --git a/packages/git-ui/src/router.jsx b/packages/git-ui/src/router.jsx index 68c17e0..cca872a 100644 --- a/packages/git-ui/src/router.jsx +++ b/packages/git-ui/src/router.jsx @@ -515,7 +515,21 @@ const packagesRoute = createRoute({ component: function Packages() { const { repo } = packagesRoute.useParams(); if (!repoRecord(repo)) return ; - return ; + return ; + }, +}); + +const packagesTabRoute = createRoute({ + getParentRoute: () => repoRoute, + path: '/packages/$registry', + loader: ({ context, params }) => + prefetch(context, ['packages', params.repo], () => + loadPackages(params.repo), + ), + component: function PackagesTab() { + const { repo, registry } = packagesTabRoute.useParams(); + if (!repoRecord(repo)) return ; + return ; }, }); @@ -543,6 +557,7 @@ const routeTree = rootRoute.addChildren([ issuesTabRoute, issueRoute, packagesRoute, + packagesTabRoute, ]), ]);