'use client'; import { useEffect, useState } from 'react'; import { getRepos } from '@/lib/gitea'; import type { GiteaRepo } from '@/lib/types'; import { SITE, GITEA_URL, GITEA_USERNAME } from '@/lib/config'; const LANG_COLOR: Record = { python: '#3572A5', javascript: '#f1e05a', typescript: '#3178c6', go: '#00add8', rust: '#dea584', 'c++': '#f34b7d', c: '#555555', html: '#e34c26', css: '#563d7c', shell: '#89e051', ruby: '#701516', java: '#b07219', }; function langColor(l: string | null) { return LANG_COLOR[(l || '').toLowerCase()] || '#555555'; } function timeAgo(d: string | undefined | null) { if (!d) return ''; const t = new Date(d).getTime(); if (Number.isNaN(t)) return ''; const diff = Date.now() - t; if (diff < 0) return 'just now'; const m = Math.floor(diff / 60000); if (m < 1) return 'just now'; if (m < 60) return `${m}m ago`; const h = Math.floor(m / 60); if (h < 24) return `${h}h ago`; const days = Math.floor(h / 24); if (days < 30) return `${days}d ago`; const months = Math.floor(days / 30); return `${months}mo ago`; } export default function Projects() { const [repos, setRepos] = useState(null); const [error, setError] = useState(null); const [activeRepo, setActiveRepo] = useState(null); const [readmeHtml, setReadmeHtml] = useState(null); const [readmeLoading, setReadmeLoading] = useState(false); const [readmeError, setReadmeError] = useState(null); useEffect(() => { getRepos(SITE.repoLimit) .then(setRepos) .catch((e) => setError(e instanceof Error ? e.message : String(e))); }, []); useEffect(() => { if (!activeRepo) return; setReadmeLoading(true); setReadmeError(null); setReadmeHtml(null); fetch(`/api/gitea/readme?repo=${encodeURIComponent(activeRepo.name)}`) .then((res) => { if (!res.ok) throw new Error(`README HTTP ${res.status}`); return res.text(); }) .then((html) => setReadmeHtml(html)) .catch((e) => setReadmeError( e instanceof Error ? e.message : 'Could not load README', ), ) .finally(() => setReadmeLoading(false)); }, [activeRepo]); return (
{!repos && !error && (
{Array.from({ length: 6 }).map((_, i) => (
))}
)} {error && (

Could not load repositories

{error}

)} {repos && (
{repos.map((repo) => { const updated = repo.updated || (repo as any).updated_at; return ( ); })}
)}
{activeRepo && (

{activeRepo.name}

{activeRepo.description || 'This project does not have a description yet.'}

View code {activeRepo.website && activeRepo.website.trim().length > 0 && ( View live )} Wiki
{readmeLoading && (

Loading README…

)} {readmeError && (

Could not load README: {readmeError}

)} {readmeHtml && (
)} {!readmeLoading && !readmeError && !readmeHtml && (

No README content available for this repository.

)}
)}
); }