import { useState, useEffect } from 'react' import { useSearchParams } from 'react-router-dom' import { gql, useQuery } from '@apollo/client' import { AlertCircle, GitCommit } from 'lucide-react' import { CodeBreadcrumb } from '@/components/code/CodeBreadcrumb' import { RefSelector } from '@/components/code/RefSelector' import { FileTree } from '@/components/code/FileTree' import { FileViewer } from '@/components/code/FileViewer' import { CommitList } from '@/components/code/CommitList' import { Skeleton } from '@/components/ui/skeleton' import { Button } from '@/components/ui/button' import { getRefs, getTree, getBlob } from '@/lib/gitApi' import type { GitRef, GitTreeEntry, GitBlob } from '@/lib/gitApi' import { useRepo } from '@/lib/repo' const REPO_NAME_QUERY = gql` query RepoName($ref: String) { repository(ref: $ref) { name } } ` type ViewMode = 'tree' | 'blob' | 'commits' // Code browser page (/:repo). Switches between tree view, file viewer, and // commit history via the ?type= search param. Ref is selected via ?ref=. export function CodePage() { const repo = useRepo() const [searchParams, setSearchParams] = useSearchParams() const [refs, setRefs] = useState([]) const [refsLoading, setRefsLoading] = useState(true) const [error, setError] = useState(null) const [entries, setEntries] = useState([]) const [blob, setBlob] = useState(null) const [contentLoading, setContentLoading] = useState(false) const currentRef = searchParams.get('ref') ?? '' const currentPath = searchParams.get('path') ?? '' const viewMode: ViewMode = (searchParams.get('type') as ViewMode) ?? 'tree' // Load refs once on mount useEffect(() => { getRefs() .then((data) => { setRefs(data) // If no ref in URL yet, use the default branch if (!searchParams.get('ref')) { const defaultRef = data.find((r) => r.isDefault) ?? data[0] if (defaultRef) { setSearchParams( (prev) => { prev.set('ref', defaultRef.shortName); return prev }, { replace: true }, ) } } }) .catch((e: Error) => setError(e.message)) .finally(() => setRefsLoading(false)) }, []) // eslint-disable-line react-hooks/exhaustive-deps // Load tree or blob when ref/path/mode changes useEffect(() => { if (!currentRef) return setContentLoading(true) setEntries([]) setBlob(null) const load = viewMode === 'blob' ? getBlob(currentRef, currentPath).then((b) => setBlob(b)) : getTree(currentRef, currentPath).then((e) => setEntries(e)) load .catch((e: Error) => setError(e.message)) .finally(() => setContentLoading(false)) }, [currentRef, currentPath, viewMode]) function navigate(path: string, type: ViewMode = 'tree') { setSearchParams((prev) => { prev.set('path', path) prev.set('type', type) return prev }) } function handleEntryClick(entry: GitTreeEntry) { const newPath = currentPath ? `${currentPath}/${entry.name}` : entry.name navigate(newPath, entry.type === 'blob' ? 'blob' : 'tree') } function handleNavigateUp() { const parts = currentPath.split('/').filter(Boolean) parts.pop() navigate(parts.join('/'), 'tree') } function handleRefSelect(ref: GitRef) { setSearchParams((prev) => { prev.set('ref', ref.shortName) prev.set('path', '') prev.set('type', 'tree') return prev }) } const { data: repoData } = useQuery(REPO_NAME_QUERY, { variables: { ref: repo } }) const repoName = repoData?.repository?.name ?? repo ?? 'git-bug' if (error) { return (

Code browser unavailable

{error}

Make sure the git-bug server is running and the repository supports code browsing.

) } return (
{/* Top bar: breadcrumb + ref selector */}
{refsLoading ? ( ) : ( navigate(p, 'tree')} /> )}
{!refsLoading && ( )} {refsLoading ? ( ) : ( )}
{/* Content */} {viewMode === 'commits' ? ( ) : viewMode === 'tree' || !blob ? ( ) : ( )}
) }