diff --git a/app/page.tsx b/app/page.tsx index 0ab5648..2f3f9b2 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -9,10 +9,11 @@ export default function Home() { return (
- +
- + +
); diff --git a/components/sections/about.tsx b/components/sections/about.tsx index e58d1bf..4fd5814 100644 --- a/components/sections/about.tsx +++ b/components/sections/about.tsx @@ -15,16 +15,16 @@ export function About() { ]; return ( -
-
-
+
+
+
-

+

/ About Me

-
+

I’m a self-taught software engineer who learns by breaking things. My journey didn't start with "Hello World"—it started with the PlayStation 3 and Call of Duty: World at War.

@@ -42,7 +42,7 @@ export function About() { Milestones -
+
{milestones.map((m, i) => (
diff --git a/components/sections/activity.tsx b/components/sections/activity.tsx index 7f365b5..cc0b8a4 100644 --- a/components/sections/activity.tsx +++ b/components/sections/activity.tsx @@ -9,6 +9,79 @@ export function Activity() { const [commits, setCommits] = useState([]); const [calendarData, setCalendarData] = useState([]); + // Quick Stats state + const [repoCount, setRepoCount] = useState(0); + const [languages, setLanguages] = useState<{ name: string; percentage: number; color: string }[]>([]); + const [currentStreak, setCurrentStreak] = useState(0); + const [yearlyCommits, setYearlyCommits] = useState(0); + const [statsLoaded, setStatsLoaded] = useState(false); + + // Language colors mapping + const languageColors: Record = { + 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 = {}; + 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") @@ -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 ( -
-
+
+
{/* Left Column: Contributions & Graph */}
-

+

Activity

@@ -119,48 +223,84 @@ export function Activity() {
- {/* Right Column: Build Log */} + {/* Right Column: Quick Dev Stats */}
-

- Build Log / now +

+ Quick Stats / live

-
-
-
-
-
- ~/current-tasks + {!statsLoaded ? ( +
+
Loading GitHub stats...
-
-
- - Updating Raycast extensions -
-
- - Refactoring portfolio sites -
-
- - Playing around with Python scripts + ) : ( + <> + {/* Stats Cards Grid */} +
+
+
{repoCount}
+
Public Repos
+
+
+
{yearlyCommits}
+
This Year
+
-
- - + + {/* Language Breakdown */} +
+

Top Languages

+
+ {languages.map((lang, index) => ( +
+
+
+
+ {lang.name} +
+ {lang.percentage}% +
+
+
+
+
+ ))} +
-
-
-
-

Check the real source.

-

- My activity above is pulled directly from the GitHub API. -

- - Visit GitHub Profile → - -
+ {/* Streak Display */} + {currentStreak > 0 && ( +
+
+ 🔥 +
+
{currentStreak} day streak
+
Keep the momentum going!
+
+
+
+ )} + +
+

Real-time data.

+

+ These stats are pulled live from the GitHub API. +

+ + Visit GitHub Profile → + +
+ + )}
diff --git a/components/sections/automation-playground.tsx b/components/sections/automation-playground.tsx index e5caeaa..3cf42e0 100644 --- a/components/sections/automation-playground.tsx +++ b/components/sections/automation-playground.tsx @@ -9,7 +9,7 @@ export function AutomationPlayground() {
-
+

@@ -21,7 +21,7 @@ export function AutomationPlayground() {

-
+
@@ -161,6 +161,7 @@ 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 }, @@ -168,10 +169,22 @@ function ASFBotDemo() { { 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); @@ -242,7 +255,7 @@ function ASFBotDemo() {
- {Math.floor(Math.random() * 900000 + 100000).toString().slice(0, 3)} {Math.floor(Math.random() * 900000 + 100000).toString().slice(0, 3)} + {twoFACode}