'use client' import * as React from 'react' import Link from 'next/link' import { useParams } from 'next/navigation' import { ArrowLeft, ExternalLink, GitFork, Star, Code, AlertCircle } from 'lucide-react' import { Button } from '@/components/ui/button' import { Badge } from '@/components/ui/badge' import { Skeleton } from '@/components/ui/skeleton' import { PushToListButton } from '@/components/personal-list/push-to-list-button' import { marked } from 'marked' import { markedHighlight } from 'marked-highlight' import hljs from 'highlight.js' interface Repository { id: number name: string url: string description: string | null stars: number | null forks: number | null language: string | null topics: string | null } interface Readme { content: string } interface RepositoryDetailResponse { id: number name: string url: string description: string | null stars: number | null forks: number | null language: string | null topics: string | null readme: Readme | null } // Configure marked with syntax highlighting and options marked.use({ breaks: true, gfm: true, }) marked.use( markedHighlight({ langPrefix: 'hljs language-', highlight(code, lang) { const language = hljs.getLanguage(lang) ? lang : 'plaintext' return hljs.highlight(code, { language }).value } }) ) export default function RepositoryDetailPage() { const params = useParams() const repositoryId = params.id as string const [data, setData] = React.useState(null) const [loading, setLoading] = React.useState(true) const [error, setError] = React.useState(null) React.useEffect(() => { setLoading(true) setError(null) fetch(`/api/repositories/${repositoryId}`) .then(async (res) => { if (!res.ok) { throw new Error('Failed to fetch repository') } return res.json() }) .then((data) => { setData(data) setLoading(false) }) .catch((err) => { console.error('Failed to fetch repository:', err) setError(err.message) setLoading(false) }) }, [repositoryId]) // Determine if content is already HTML or needs markdown parsing // Must be called before any conditional returns const readmeHtml = React.useMemo(() => { if (!data?.readme?.content) return null const content = data.readme.content // Ensure content is a string before processing if (typeof content !== 'string' || !content.trim()) return null // Check if content is already HTML (starts with < tag) const isHtml = content.trim().startsWith('<') if (isHtml) { // Content is already HTML, use as-is return content } else { // Content is markdown, parse it return marked.parse(content) as string } }, [data?.readme?.content]) const topics = data?.topics ? data.topics.split(',') : [] if (loading) { return (
) } if (error || !data) { return (

Repository Not Found

The repository you're looking for doesn't exist or couldn't be loaded.

) } return (
{/* Header */}

{data.name}

{data.description && (

{data.description}

)}
{data.language && ( {data.language} )}
{data.stars !== null && (
{data.stars.toLocaleString()} stars
)} {data.forks !== null && (
{data.forks.toLocaleString()} forks
)}
{topics.length > 0 && (
{topics.slice(0, 10).map((topic) => ( {topic.trim()} ))} {topics.length > 10 && ( +{topics.length - 10} more )}
)}
{/* README Content */}
{readmeHtml ? (
) : (

No README Available

This repository doesn't have a README file or it couldn't be loaded.

)}
) }