import { useQuery } from '@tanstack/react-query'; import { CheckIcon, ContainerIcon, CopyIcon } from 'lucide-react'; import { useState } from 'react'; import { Badge } from '#/components/atoms/badge.jsx'; import { Notice } from '#/components/atoms/notice.jsx'; import { RowIcon } from '#/components/atoms/row-icon.jsx'; import { EmptyState } from '#/components/molecules/empty-state.jsx'; import { ListRow } from '#/components/molecules/list-row.jsx'; import { TextLink } from '#/components/molecules/text-link.jsx'; import { get } from '#/lib/api.js'; import { bytes, timeAgo } from '#/lib/format.js'; import { queryKeys } from '#/lib/query.js'; /** One line summarizing an image repository's shape. */ function repoSummary(repo) { if (repo.manifests === 0) return 'nothing pushed yet'; const parts = [ `${repo.tags.length} ${repo.tags.length === 1 ? 'tag' : 'tags'}`, ]; parts.push(bytes(repo.size)); if (repo.updatedAt) parts.push(`pushed ${timeAgo(repo.updatedAt)}`); return parts.join(' ยท '); } /** A command with a copy button beside it, shown in full where room allows. */ function CopyCommand({ command, compact }) { const [copied, setCopied] = useState(false); const copy = (event) => { event.preventDefault(); event.stopPropagation(); navigator.clipboard.writeText(command).then(() => { setCopied(true); setTimeout(() => setCopied(false), 1500); }); }; return ( ); } /** Every tag of one image: name, manifest digest, and its pull command. */ function TagList({ repo }) { return (