import { useEffect, useRef, useState } from 'react' import { useParams, useSearchParams, Link } from 'react-router-dom' import ReactMarkdown from 'react-markdown' import remarkGfm from 'remark-gfm' import { useRepo, useRepoTree, useRepoBlob, useRepoBranches } from '../api/queries/repos' import { useEnvironments } from '../api/queries/environments' import { TreeBrowser } from '../components/repos/TreeBrowser' import { RepoListSkeleton } from '../ui/Skeleton' import { RepoAvatar } from '../ui/RepoAvatar' import { useRecentRepos } from '../hooks/useRecentRepos' export default function RepoPage() { const { owner = '', repo: repoName = '' } = useParams<{ owner: string; repo: string }>() const [searchParams, setSearchParams] = useSearchParams() const [showBranches, setShowBranches] = useState(false) const [showClone, setShowClone] = useState(false) const branchRef = useRef(null) const cloneRef = useRef(null) const path = searchParams.get('path') ?? '' const ref = searchParams.get('ref') ?? '' const { data: repo, isLoading, isError } = useRepo(owner, repoName) const { data: branches } = useRepoBranches(owner, repoName) const { data: environments } = useEnvironments(owner, repoName) const { track } = useRecentRepos() useEffect(() => { if (owner && repoName) track(owner, repoName) }, [owner, repoName]) // Close dropdowns on outside click useEffect(() => { function handle(e: MouseEvent) { if (branchRef.current && !branchRef.current.contains(e.target as Node)) setShowBranches(false) if (cloneRef.current && !cloneRef.current.contains(e.target as Node)) setShowClone(false) } document.addEventListener('mousedown', handle) return () => document.removeEventListener('mousedown', handle) }, []) if (isLoading) return
if (isError || !repo) return
Repository not found.
const branch = ref || repo.defaultBranch const cloneUrl = `${window.location.origin}/${owner}/${repoName}.git` function switchBranch(b: string) { setSearchParams({ ref: b, ...(path ? { path } : {}) }) setShowBranches(false) } return (
{/* Header row */}
Repositories / {repo.name} {repo.isPrivate && ( Private )}
{repo.description && (

{repo.description}

)} {/* Deployment status badges */} {environments && environments.length > 0 && (
{environments.map(env => { const status = env.latestDeployment?.status const dot: Record = { success: 'bg-[var(--c-success)]', in_progress: 'bg-[var(--c-brand)] animate-pulse', failure: 'bg-[var(--c-danger)]', pending: 'bg-[var(--c-subtle)]', cancelled: 'bg-[var(--c-subtle)]', } return ( {env.name} {env.latestDeployment?.sha && ( {env.latestDeployment.sha.slice(0, 7)} )} ) })}
)}
Pull requests {/* Clone dropdown */}
{showClone && (

Clone over HTTP

{cloneUrl}
)}
{repo.isEmpty ? ( ) : ( <> {/* Branch selector */}
{showBranches && (

Switch branch

    {branches?.map(b => (
  • ))} {!branches?.length && (
  • No branches found
  • )}
)}
{/* Nav links */} Commits Branches Issues Security Settings
{/* README preview — only at repo root */} {!path && } )}
) } function ReadmePreview({ owner, repo, ref }: { owner: string; repo: string; ref: string }) { const { data: entries } = useRepoTree(owner, repo, ref, '') const readmeEntry = entries?.find(e => e.name.toLowerCase() === 'readme.md') const { data: blob } = useRepoBlob(owner, repo, ref, readmeEntry?.name ?? '') if (!readmeEntry || !blob) return null return (
{readmeEntry.name}
{blob.content}
) } function GettingStarted({ repoName, branch, cloneUrl }: { repoName: string; branch: string; cloneUrl: string }) { return (

Getting started

Push your first commit to get started.

Clone over HTTP

…or push an existing repository

…or create a new repository on the command line

> README.md\ngit init\ngit add README.md\ngit commit -m "first commit"\ngit branch -M ${branch}\ngit remote add origin ${cloneUrl}\ngit push -u origin ${branch}`} multiline />
) } function CopyBlock({ value, multiline }: { value: string; multiline?: boolean }) { const copy = () => navigator.clipboard.writeText(value).catch(() => {}) return (
        {value}
      
) }