feat: Add Github Integration for project data

- Added ribbons
- The icons now show github stars and forks on hover
This commit is contained in:
2026-02-23 11:38:27 +05:30
parent 246e1c7688
commit 804fde0051
4 changed files with 143 additions and 13 deletions

View File

@@ -1,42 +1,116 @@
import { ExternalLink, Layout } from 'lucide-react';
import { useEffect, useState } from 'react';
import { ChevronRight, ExternalLink, GitFork, Layout, Star } from 'lucide-react';
import { PROJECTS } from '../data/contentData';
export function ProjectsSection() {
const [repoStats, setRepoStats] = useState<Record<string, { stars: number; forks: number }>>({});
useEffect(() => {
let isMounted = true;
const fetchStats = async () => {
const entries = PROJECTS.filter(project => project.github).map(async project => {
try {
const url = new URL(project.github!);
const [owner, repo] = url.pathname.replace(/^\//, '').split('/');
if (!owner || !repo) {
return null;
}
const response = await fetch(`https://api.github.com/repos/${owner}/${repo}`);
if (!response.ok) {
return null;
}
const data = await response.json();
return {
id: project.id,
stars: Number(data.stargazers_count ?? 0),
forks: Number(data.forks_count ?? 0),
};
} catch {
return null;
}
});
const results = await Promise.all(entries);
if (!isMounted) return;
const nextStats = results.reduce<Record<string, { stars: number; forks: number }>>((acc, item) => {
if (item) {
acc[item.id] = { stars: item.stars, forks: item.forks };
}
return acc;
}, {});
if (Object.keys(nextStats).length > 0) {
setRepoStats(nextStats);
}
};
fetchStats();
return () => {
isMounted = false;
};
}, []);
return (
<section className="md:col-span-2 space-y-6">
<h2 className="font-sketch text-2xl text-[var(--text-main)] border-b-2 border-dashed border-[var(--border-main)] pb-2 flex items-center gap-2">
<h2 className="font-sketch text-2xl text-(--text-main) border-b-2 border-dashed border-(--border-main) pb-2 flex items-center gap-2">
<Layout className="w-5 h-5 accent-text" /> recent_projects/
</h2>
<div className="flex flex-col gap-6">
{PROJECTS.map((project, idx) => (
<article
key={project.id}
className={`group relative p-6 bg-[var(--bg-surface)] sketch-border-subtle cursor-pointer overflow-hidden ${idx % 2 === 0 ? 'transform rotate-[0.5deg]' : 'transform -rotate-[0.5deg]'}`}
className={`group relative p-6 bg-(--bg-surface) sketch-border-subtle cursor-pointer overflow-visible ${idx % 2 === 0 ? 'transform rotate-[0.5deg]' : 'transform -rotate-[0.5deg]'}`}
>
<div className="absolute top-0 left-0 w-full h-1 bg-[var(--border-main)] group-hover:bg-[var(--color-accent)] transition-colors duration-300 opacity-50" />
{project.ribbon && (
<div className="absolute -top-2 -right-10 rotate-12">
<div className="sketch-border-subtle bg-(--color-accent) text-(--bg-base) px-6 py-1 text-xs font-sketch uppercase tracking-wide shadow-lg">
{project.ribbon}
</div>
</div>
)}
<div className="absolute top-0 left-0 w-full h-1 bg-(--border-main) group-hover:bg-(--color-accent) transition-colors duration-300 opacity-50" />
<div className="flex justify-between items-start mb-4">
<div className="flex items-center gap-4">
<div className="p-3 sketch-border-subtle bg-[var(--bg-panel)] text-[var(--text-muted)] group-hover:text-[var(--color-accent)] group-hover:scale-110 transition-all transform -rotate-3">
{project.icon}
<div className="group/icon relative flex items-center gap-2 py-2.5 ps-2.5 pe-1.5 pr-6 sketch-border-subtle bg-(--bg-panel) text-(--text-muted) hover:text-(--color-accent) hover:scale-110 transition-all transform -rotate-3">
<span className="flex items-center justify-center">
{project.icon}
</span>
<ChevronRight className="absolute right-1.5 top-1/2 -translate-y-1/2 w-4 h-4 text-(--color-accent) opacity-90 animate-pulse transition-all drop-shadow-[0_0_16px_var(--color-accent)] group-hover/icon:opacity-0 group-hover/icon:scale-0 group-focus-within/icon:opacity-0 group-focus-within/icon:scale-0" />
{project.github && (
<div className="flex items-center gap-3 text-xs text-(--text-muted) overflow-hidden max-w-0 opacity-0 group-hover/icon:max-w-35 group-hover/icon:opacity-100 transition-all duration-300">
<span className="flex items-center gap-1">
<Star className="w-3 h-3 text-(--color-accent)" />
{repoStats[project.id]?.stars ?? project.stars ?? '—'}
</span>
<span className="flex items-center gap-1">
<GitFork className="w-3 h-3 text-(--color-accent)" />
{repoStats[project.id]?.forks ?? project.forks ?? '—'}
</span>
</div>
)}
</div>
<div>
<h3 className="text-[var(--text-main)] font-bold text-lg flex items-center gap-2 font-mono">
<h3 className="text-(--text-main) font-bold text-lg flex items-center gap-2 font-mono">
{project.title}
<ExternalLink className="w-4 h-4 opacity-0 group-hover:opacity-100 transition-opacity accent-text" />
</h3>
<span className="font-sketch text-sm text-[var(--text-muted)] accent-text">{project.category}</span>
<span className="font-sketch text-sm text-(--text-muted) accent-text">{project.category}</span>
</div>
</div>
</div>
<p className="text-sm text-[var(--text-muted)] mb-5 leading-relaxed font-mono">
<p className="text-sm text-(--text-muted) mb-5 leading-relaxed font-mono">
{project.desc}
</p>
<div className="flex flex-wrap gap-2">
{project.tech.map((t) => (
<span key={t} className="text-xs px-2 py-1 bg-[var(--bg-panel)] sketch-border-subtle text-[var(--text-muted)] font-mono">
<span key={t} className="text-xs px-2 py-1 bg-(--bg-panel) sketch-border-subtle text-(--text-muted) font-mono">
{t}
</span>
))}

View File

@@ -1,8 +1,9 @@
import { Cpu, Gamepad, Layout } from 'lucide-react';
import { Chromium, Cpu, Gamepad, Layout } from 'lucide-react';
export const PROJECTS = [
{
id: 'Krohnkite',
github: 'https://github.com/sortedcord/krohnkite-i3',
title: 'Krohnkite i3',
category: 'Window Management',
tech: ['Typescript', 'Kwin API', 'QML'],
@@ -11,14 +12,25 @@ export const PROJECTS = [
},
{
id: 'leenim',
github: 'https://github.com/sortedcord/leenim',
title: 'Leenim',
category: 'Desktop Application Development',
tech: ['React', 'Rust', 'Tauri', 'Python'],
desc: 'A Cross platform Video Editor style application for creating mathematical illustrations with Manim.',
icon: <Cpu className="w-5 h-5" />,
},
{
id: 'gippity-pruner',
github: 'https://github.com/sortedcord/gippity-pruner',
title: 'Gippity Pruner',
category: 'Browser Extension for Chromium & Gecko',
tech: ['JavaScript'],
desc: 'Improve ChatGPT performance in long conversations, without losing your context!',
icon: <Chromium className='w-5 h-5' />,
},
{
id: 'omnia',
github: 'https://github.com/sortedcord/omnia',
title: 'Omnia',
category: 'AI-Native Computing Systems',
tech: ['Typescript'],

View File

@@ -1,11 +1,15 @@
import type { ReactElement } from 'react';
import { Cpu, Gamepad, Layout } from 'lucide-react';
import { Cpu, Gamepad, Layout, Chromium } from 'lucide-react';
export type Project = {
id: string;
github?: string;
stars?: number;
forks?: number;
title: string;
category: string;
tech: string[];
ribbon: string | null;
desc: string;
icon: ReactElement;
};
@@ -18,26 +22,42 @@ export type LabNote = {
export const PROJECTS: Project[] = [
{
id: 'Krohnkite',
github: 'https://github.com/sortedcord/krohnkite-i3',
title: 'Krohnkite i3',
category: 'Window Management',
tech: ['Typescript', 'Kwin API', 'QML'],
ribbon: null,
desc: 'A modified stripped down version of Krohnkite that implements i3-style tiling in KDE Plasma (Wayland).',
icon: <Layout className="w-5 h-5" />,
},
{
id: 'leenim',
github: 'https://github.com/sortedcord/leenim',
title: 'Leenim',
category: 'Desktop Application Development',
tech: ['React', 'Rust', 'Tauri', 'Python'],
desc: 'A Cross platform Video Editor style application for creating mathematical illustrations with Manim.',
ribbon: null,
icon: <Cpu className="w-5 h-5" />,
},
{
id: 'gippity-pruner',
github: 'https://github.com/sortedcord/gippity-pruner',
title: 'Gippity Pruner',
category: 'Browser Extension',
tech: ['JavaScript'],
desc: 'Cross browser extension that introduces lazy loading in ChatGPTs UI, improving performance for long coversations without context or memory loss.',
ribbon: "+1k Users!",
icon: <Chromium className='w-5 h-5' />,
},
{
id: 'omnia',
github: 'https://github.com/sortedcord/omnia',
title: 'Omnia',
category: 'AI-Native Computing Systems',
tech: ['Typescript'],
desc: 'A game engine specification that defines an AI driven, intent-based architecture for building determinist-first interactive worlds.',
ribbon: null,
icon: <Gamepad className="w-5 h-5" />,
},
];

View File

@@ -114,6 +114,30 @@
}
}
@keyframes project-shake {
0%,
100% {
transform: rotate(-3deg) translateY(0);
}
20% {
transform: rotate(1deg) translateY(-1px);
}
40% {
transform: rotate(-1deg) translateY(1px);
}
60% {
transform: rotate(1.5deg) translateY(-2px);
}
80% {
transform: rotate(-1.5deg) translateY(1px);
}
}
@media (prefers-reduced-motion: reduce) {
.dot-grid::before {
animation: none;