Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 3 additions & 2 deletions app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,11 @@ export default function Home() {
return (
<div className="flex flex-col bg-background font-mono selection:bg-neon selection:text-black">
<Hero />
<Projects />
<div className="h-16" />
<About />
<Activity />
<Projects />
<AutomationPlayground />
<Activity />
<Contact />
</div>
);
Expand Down
12 changes: 6 additions & 6 deletions components/sections/about.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ export function About() {
];

return (
<section id="about" className="py-32 relative border-t border-white/5">
<div className="container px-4 mx-auto max-w-4xl">
<div className="flex flex-col md:flex-row gap-16 items-start">
<section id="about" className="py-20 relative border-t border-white/5">
<div className="container px-4 mx-auto max-w-6xl">
<div className="flex flex-col md:flex-row gap-20 items-start">

<div className="flex-1">
<h2 className="text-4xl font-bold tracking-tighter mb-8 flex items-center gap-3">
<h2 className="text-4xl font-bold tracking-tighter mb-12 flex items-center gap-3">
<span className="text-neon">/</span> About Me
</h2>

<div className="prose prose-invert prose-lg text-gray-400">
<div className="prose prose-invert prose-lg text-gray-400 space-y-6">
<p>
I’m a self-taught software engineer who learns by breaking things. My journey didn't start with "Hello World"—it started with the <span className="text-white font-bold">PlayStation 3</span> and <span className="text-white font-bold">Call of Duty: World at War</span>.
</p>
Expand All @@ -42,7 +42,7 @@ export function About() {
<Activity className="w-5 h-5 text-gray-500" />
Milestones
</h3>
<div className="space-y-6 border-l border-white/10 pl-6 relative">
<div className="space-y-8 border-l border-white/10 pl-6 relative">
{milestones.map((m, i) => (
<div key={i} className="relative">
<div className="absolute -left-[29px] top-1.5 w-3 h-3 bg-surface border border-white/20 rounded-full" />
Expand Down
216 changes: 178 additions & 38 deletions components/sections/activity.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,79 @@ export function Activity() {
const [commits, setCommits] = useState<any[]>([]);
const [calendarData, setCalendarData] = useState<any[]>([]);

// Quick Stats state
const [repoCount, setRepoCount] = useState<number>(0);
const [languages, setLanguages] = useState<{ name: string; percentage: number; color: string }[]>([]);
const [currentStreak, setCurrentStreak] = useState<number>(0);
const [yearlyCommits, setYearlyCommits] = useState<number>(0);
const [statsLoaded, setStatsLoaded] = useState<boolean>(false);

// Language colors mapping
const languageColors: Record<string, string> = {
TypeScript: '#3178c6',
JavaScript: '#f1e05a',
Python: '#3572A5',
Rust: '#dea584',
'C#': '#178600',
Java: '#b07219',
Go: '#00ADD8',
Ruby: '#701516',
PHP: '#4F5D95',
CSS: '#563d7c',
HTML: '#e34c26',
};

// Calculate language breakdown from repos
const calculateLanguages = (repos: any[]) => {
const langCount: Record<string, number> = {};
repos.forEach(repo => {
if (repo.language) {
langCount[repo.language] = (langCount[repo.language] || 0) + 1;
}
});

const total = Object.values(langCount).reduce((a, b) => a + b, 0);
if (total === 0) return [];

return Object.entries(langCount)
.map(([lang, count]) => ({
name: lang,
percentage: Math.round((count / total) * 100),
color: languageColors[lang] || '#6e7681'
}))
.sort((a, b) => b.percentage - a.percentage)
.slice(0, 5);
};

// Calculate current streak from contribution data
const calculateStreak = (contributions: any[]) => {
if (!contributions || contributions.length === 0) return 0;

let streak = 0;
const today = new Date();
today.setHours(0, 0, 0, 0);

// Sort by date descending
const sorted = [...contributions].sort((a, b) =>
new Date(b.date).getTime() - new Date(a.date).getTime()
);

for (const day of sorted) {
const dayDate = new Date(day.date);
dayDate.setHours(0, 0, 0, 0);

const daysDiff = Math.floor((today.getTime() - dayDate.getTime()) / (1000 * 60 * 60 * 24));

if (daysDiff === streak && day.count > 0) {
streak++;
} else if (daysDiff > streak) {
break;
}
}

return streak;
};

useEffect(() => {
// Fetch recent events
fetch("https://api.github.com/users/luinbytes/events/public")
Expand Down Expand Up @@ -47,19 +120,50 @@ export function Activity() {
level: day.level
}));
setCalendarData(formatted);

// Calculate streak and yearly commits from contribution data
const streak = calculateStreak(formatted);
setCurrentStreak(streak);

const currentYear = new Date().getFullYear();
const thisYearContributions = formatted
.filter((day: any) => new Date(day.date).getFullYear() === currentYear)
.reduce((sum: number, day: any) => sum + day.count, 0);
setYearlyCommits(thisYearContributions);
}
})
.catch(err => console.error("Failed to fetch calendar", err));

// Fetch GitHub repos for language stats
fetch("https://api.github.com/users/luinbytes/repos?per_page=100")
.then(res => res.json())
.then(repos => {
if (Array.isArray(repos)) {
// Filter out forks
const ownRepos = repos.filter((repo: any) => !repo.fork);
setRepoCount(ownRepos.length);

// Calculate language breakdown
const langs = calculateLanguages(ownRepos);
setLanguages(langs);

setStatsLoaded(true);
}
})
.catch(err => {
console.error("Failed to fetch repos", err);
setStatsLoaded(true);
});

}, []);

return (
<section id="activity" className="py-32 relative">
<div className="container px-4 mx-auto grid lg:grid-cols-2 gap-16">
<section id="activity" className="py-24 relative border-t border-white/5">
<div className="container px-4 mx-auto max-w-6xl grid lg:grid-cols-2 gap-20">

{/* Left Column: Contributions & Graph */}
<div>
<h2 className="text-3xl font-bold tracking-tighter mb-8 flex items-center gap-3">
<h2 className="text-3xl font-bold tracking-tighter mb-12 flex items-center gap-3">
Activity
</h2>

Expand Down Expand Up @@ -119,48 +223,84 @@ export function Activity() {
</div>
</div>

{/* Right Column: Build Log */}
{/* Right Column: Quick Dev Stats */}
<div>
<h2 className="text-3xl font-bold tracking-tighter mb-8 flex items-center gap-3">
Build Log <span className="text-sm font-normal text-gray-500 font-mono self-end mb-1">/ now</span>
<h2 className="text-3xl font-bold tracking-tighter mb-12 flex items-center gap-3">
Quick Stats <span className="text-sm font-normal text-gray-500 font-mono self-end mb-1">/ live</span>
</h2>

<div className="bg-surface border border-white/5 rounded-xl p-0 overflow-hidden">
<div className="bg-white/5 px-6 py-3 border-b border-white/5 flex items-center gap-2">
<div className="w-3 h-3 rounded-full bg-red-500/50" />
<div className="w-3 h-3 rounded-full bg-yellow-500/50" />
<div className="w-3 h-3 rounded-full bg-green-500/50" />
<span className="text-xs font-mono text-gray-500 ml-2">~/current-tasks</span>
{!statsLoaded ? (
<div className="bg-surface border border-white/10 rounded-xl p-8 h-[400px] flex items-center justify-center">
<div className="text-gray-500 text-sm animate-pulse">Loading GitHub stats...</div>
</div>
<div className="p-6 font-mono text-sm space-y-4">
<div className="flex gap-3 text-gray-300">
<span className="text-neon shrink-0">➜</span>
<span>Updating Raycast extensions</span>
</div>
<div className="flex gap-3 text-gray-300">
<span className="text-neon shrink-0">➜</span>
<span>Refactoring portfolio sites</span>
</div>
<div className="flex gap-3 text-gray-300">
<span className="text-neon shrink-0">➜</span>
<span>Playing around with Python scripts</span>
) : (
<>
{/* Stats Cards Grid */}
<div className="grid grid-cols-2 gap-4 mb-6">
<div className="bg-surface border border-white/10 rounded-xl p-6 hover:border-neon/30 transition-colors">
<div className="text-4xl font-bold text-neon mb-2 font-mono">{repoCount}</div>
<div className="text-sm text-gray-400 uppercase tracking-wide">Public Repos</div>
</div>
<div className="bg-surface border border-white/10 rounded-xl p-6 hover:border-neon/30 transition-colors">
<div className="text-4xl font-bold text-neon mb-2 font-mono">{yearlyCommits}</div>
<div className="text-sm text-gray-400 uppercase tracking-wide">This Year</div>
</div>
</div>
<div className="animate-pulse flex gap-3 text-gray-500">
<span className="shrink-0">➜</span>
<span className="w-2 h-4 bg-gray-500 block" />

{/* Language Breakdown */}
<div className="bg-surface border border-white/10 rounded-xl p-6 mb-6">
<h3 className="text-lg font-bold text-white mb-4">Top Languages</h3>
<div className="space-y-3">
{languages.map((lang, index) => (
<div key={index}>
<div className="flex justify-between items-center mb-1.5">
<div className="flex items-center gap-2">
<div
className="w-3 h-3 rounded-full"
style={{ backgroundColor: lang.color }}
/>
<span className="text-sm text-gray-300 font-medium">{lang.name}</span>
</div>
<span className="text-sm text-gray-500 font-mono">{lang.percentage}%</span>
</div>
<div className="h-2 bg-[#1a1a1a] rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-500"
style={{
width: `${lang.percentage}%`,
backgroundColor: lang.color
}}
/>
</div>
</div>
))}
</div>
</div>
</div>
</div>

<div className="mt-8 p-6 border border-white/10 rounded-xl bg-gradient-to-br from-neon/5 to-purple-500/5">
<h3 className="font-bold text-white mb-2">Check the real source.</h3>
<p className="text-gray-400 text-sm mb-4">
My activity above is pulled directly from the GitHub API.
</p>
<a href="https://github.com/luinbytes" target="_blank" className="text-neon hover:underline text-sm font-bold">
Visit GitHub Profile &rarr;
</a>
</div>
{/* Streak Display */}
{currentStreak > 0 && (
<div className="bg-gradient-to-r from-orange-500/10 to-red-500/10 border border-orange-500/20 rounded-xl p-4 mb-6">
<div className="flex items-center gap-3">
<span className="text-3xl">🔥</span>
<div>
<div className="text-xl font-bold text-white font-mono">{currentStreak} day streak</div>
<div className="text-xs text-gray-400">Keep the momentum going!</div>
</div>
</div>
</div>
)}

<div className="p-6 border border-white/10 rounded-xl bg-gradient-to-br from-neon/5 to-purple-500/5">
<h3 className="font-bold text-white mb-2">Real-time data.</h3>
<p className="text-gray-400 text-sm mb-4">
These stats are pulled live from the GitHub API.
</p>
<a href="https://github.com/luinbytes" target="_blank" className="text-neon hover:underline text-sm font-bold">
Visit GitHub Profile &rarr;
</a>
</div>
</>
)}
</div>

</div>
Expand Down
21 changes: 17 additions & 4 deletions components/sections/automation-playground.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export function AutomationPlayground() {
<section id="playground" className="py-32 relative">
<div className="absolute inset-0 bg-gradient-to-b from-black via-surface/50 to-black pointer-events-none" />

<div className="container px-4 mx-auto relative z-10">
<div className="container px-4 mx-auto max-w-7xl relative z-10">
<div className="text-center mb-16">
<h2 className="text-4xl md:text-5xl font-bold tracking-tighter mb-6 flex items-center justify-center gap-3">
<Command className="w-8 h-8 text-neon" />
Expand All @@ -21,7 +21,7 @@ export function AutomationPlayground() {
</p>
</div>

<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
<div className="grid grid-cols-1 lg:grid-cols-2 gap-10">
<DiscordUtilitiesDemo />
<ASFBotDemo />
</div>
Expand Down Expand Up @@ -161,17 +161,30 @@ function DiscordUtilitiesDemo() {
function ASFBotDemo() {
const [selectedBot, setSelectedBot] = useState(0);
const [twoFATime, setTwoFATime] = useState(30);
const [twoFACode, setTwoFACode] = useState('123 456');

const bots = [
{ id: 'bot1', name: 'MainAccount', status: 'Farming', game: 'Team Fortress 2', cards: '3/5', online: true },
{ id: 'bot2', name: 'AltAccount', status: 'Online', game: 'Idle', cards: '0/0', online: true },
{ id: 'bot3', name: 'TradingBot', status: 'Offline', game: null, cards: '0/0', online: false },
];

const generateCode = () => {
const code1 = Math.floor(Math.random() * 900 + 100);
const code2 = Math.floor(Math.random() * 900 + 100);
return `${code1} ${code2}`;
};

useEffect(() => {
// Generate initial code on client
setTwoFACode(generateCode());

const interval = setInterval(() => {
setTwoFATime(prev => {
if (prev <= 1) return 30;
if (prev <= 1) {
setTwoFACode(generateCode());
return 30;
}
return prev - 1;
});
}, 1000);
Expand Down Expand Up @@ -242,7 +255,7 @@ function ASFBotDemo() {
</div>
<div className="flex items-center gap-2">
<div className="flex-1 bg-black/50 border border-white/10 rounded px-3 py-2 font-mono text-lg text-white text-center tracking-widest">
{Math.floor(Math.random() * 900000 + 100000).toString().slice(0, 3)} {Math.floor(Math.random() * 900000 + 100000).toString().slice(0, 3)}
{twoFACode}
</div>
<button className="p-2 bg-white/5 hover:bg-white/10 border border-white/10 rounded transition-colors">
<Copy className="w-4 h-4 text-gray-400" />
Expand Down
Loading