diff --git a/api/master.js b/api/master.js index d440768..4628877 100644 --- a/api/master.js +++ b/api/master.js @@ -18,7 +18,7 @@ const { fetchMergedPullRequests, fetchUserTotalStars, fetchRecentPRLinesChanged, - fetchOpenPullRequests // Added this + fetchOpenPullRequests } = require("../src/github"); const { getTheme, applyColorOverrides } = require("../src/themes"); const { generateMasterCardSVG } = require("../src/svg-master"); @@ -40,7 +40,7 @@ module.exports = async (req, res) => { } try { - const cacheKey = `master_full_v3:${username.toLowerCase()}`; + const cacheKey = `master_ultimate_v1:${username.toLowerCase()}`; if (refresh === "true") { clearCache(cacheKey); @@ -85,6 +85,13 @@ module.exports = async (req, res) => { else tempStreak = 0; } + // Commit Ranking logic (by day of week) + const weekMap = { 0:0, 1:0, 2:0, 3:0, 4:0, 5:0, 6:0 }; + days.forEach(d => { + const dayOfWeek = new Date(d.date).getDay(); + weekMap[dayOfWeek] += d.count; + }); + const repoMap = {}; (prs || []).slice(0, 50).forEach(pr => { if (pr.repository_url) { @@ -98,6 +105,7 @@ module.exports = async (req, res) => { data = { username: profile?.login || username, + name: profile?.name || profile?.login || username, totalPRs: prs?.length || 0, openPRs: openPRCount || 0, mergedPRs: mergedPRCount || 0, @@ -113,18 +121,19 @@ module.exports = async (req, res) => { openIssues: openIssues || 0, closedIssues: closedIssues || 0, totalStars: totalStars || 0, - linesChanged: linesChanged || 0 + linesChanged: linesChanged || 0, + weekMap }; setCache(cacheKey, data, CACHE_TTL); } catch (fetchErr) { - console.error("Mastercard Optimized Fetch error:", fetchErr.message); + console.error("Mastercard Ultimate Fetch error:", fetchErr.message); data = { - username: username, totalPRs: 0, openPRs: 0, mergedPRs: 0, + username: username, name: username, totalPRs: 0, openPRs: 0, mergedPRs: 0, repoCount: 0, languages: [], contributions: 0, repoList: [], contributionDays: [], currentStreak: 0, longestStreak: 0, totalCommits: 0, totalIssues: 0, openIssues: 0, closedIssues: 0, - totalStars: 0, linesChanged: 0 + totalStars: 0, linesChanged: 0, weekMap: {} }; } } @@ -146,8 +155,8 @@ module.exports = async (req, res) => { }; function errorSVG(msg) { - return ` - - ${msg} + return ` + + ${msg} `; } diff --git a/src/svg-master.js b/src/svg-master.js index 6dd07f4..e4da7f2 100644 --- a/src/svg-master.js +++ b/src/svg-master.js @@ -1,6 +1,6 @@ /** - * Master Card SVG generation - Complete Profile Dashboard (820px). - * A unique, high-impact card for GitHub README. + * Master Card SVG generation - Ultimate Profile Dashboard (830px). + * Designed to cover the full width of a GitHub README with a premium look. */ function getContributionColorByLevel(level) { @@ -14,16 +14,16 @@ function escapeXml(s) { function generateMasterCardSVG(options) { const { - username, totalPRs = 0, openPRs = 0, mergedPRs = 0, repoCount = 0, + username, name, totalPRs = 0, openPRs = 0, mergedPRs = 0, repoCount = 0, languages = [], contributions = 0, totalCommits = 0, repoList = [], contributionDays = [], currentStreak = 0, longestStreak = 0, totalIssues = 0, openIssues = 0, closedIssues = 0, totalStars = 0, - linesChanged = 0, colors, hideBorder, cardWidth = 820 + linesChanged = 0, weekMap = {}, colors, hideBorder, cardWidth = 830 } = options; - const pad = 28; + const pad = 30; const innerW = cardWidth - pad * 2; - const hba = hideBorder ? `rx="14"` : `rx="14" stroke="#30363d" stroke-width="1.5"`; + const hba = hideBorder ? `rx="16"` : `rx="16" stroke="#30363d" stroke-width="2"`; const accentColor = (colors && colors.accent_color) || "58a6ff"; const titleColor = (colors && colors.title_color) || "58a6ff"; @@ -32,143 +32,146 @@ function generateMasterCardSVG(options) { let y = 0; let content = ""; - // --- HEADER SECTION --- + // --- TOP HEADER --- content += ` - - ${escapeXml(username)}'s Profile - - Master Dashboard + + ${escapeXml(name || username)} + @${escapeXml(username.toLowerCase())} • Full Profile Dashboard + `; - y = 110; + y = 125; - // --- HERO METRICS (5 Key stats) --- - const statW = Math.floor(innerW / 5); + // --- HERO STATS ROW (6 metrics) --- + const statW = Math.floor(innerW / 6); const heroStats = [ { label: "Commits", value: totalCommits.toLocaleString(), color: accentColor }, { label: "Stars", value: totalStars.toLocaleString(), color: "eab308" }, - { label: "Contributions", value: contributions.toLocaleString(), color: "39d353" }, - { label: "Total PRs", value: totalPRs, color: "8b5cf6" }, - { label: "Total Issues", value: totalIssues, color: "f85149" }, + { label: "PRs", value: totalPRs, color: "8b5cf6" }, + { label: "Issues", value: totalIssues, color: "f85149" }, + { label: "Contribs", value: contributions.toLocaleString(), color: "39d353" }, + { label: "Lines +/-", value: linesChanged.toLocaleString(), color: "fb923c" }, ]; heroStats.forEach((s, i) => { content += ` - ${s.value} - ${s.label} + ${s.value} + ${s.label} `; }); y += 85; - content += ``; - y += 35; + content += ``; + y += 40; - // --- INSIGHTS GRID (Middle row) --- - const leftColW = Math.floor(innerW * 0.55); - const rightColW = innerW - leftColW - 30; + // --- MIDDLE GRID: Languages (Left) & Commit Ranking (Right) --- + const midGridW = (innerW - 40) / 2; - // Languages (Left) + // Top Languages content += ` - Top Languages - `; + Language Mastery + `; const { getLanguageColor } = require("./languages"); - const topLangs = languages.slice(0, 6); + const topLangs = languages.slice(0, 7); topLangs.forEach((lang, i) => { const ly = i * 28; - const barMax = leftColW - 130; + const barMax = midGridW - 140; const barW = (lang.percentage / 100) * barMax; content += ` - ${escapeXml(lang.name)} - - - ${lang.percentage.toFixed(1)}% + ${escapeXml(lang.name)} + + + ${lang.percentage.toFixed(1)}% `; }); content += ``; - // Streaks & Activity (Right) - content += ` - Performance - - - Current Streak - ${currentStreak} Days - - - - Longest Streak - ${longestStreak} Days - + // Commits Ranking / Streaks + const daysOfWeek = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; + const maxWeekly = Math.max(...Object.values(weekMap), 1); + content += ` + Weekly Activity + `; - - Lines Changed (recent) - ${linesChanged.toLocaleString()} - - - `; + daysOfWeek.forEach((day, i) => { + const ly = i * 28; + const count = weekMap[i] || 0; + const barW = (count / maxWeekly) * (midGridW - 80); + content += ` + + ${day} + + + ${count.toLocaleString()} + `; + }); + content += ``; - y += 205; - content += ``; - y += 35; + y += 240; + content += ``; + y += 40; - // --- REPOSITORIES & STATUS (Middle-Bottom row) --- - const breakW = (innerW - 30) / 2; + // --- THIRD ROW: PR & ISSUE BREAKDOWN + STREAKS --- content += ` - Projects Activity - `; - repoList.slice(0, 3).forEach((repo, i) => { - content += `• ${escapeXml(repo.name.length > 25 ? repo.name.substring(0, 22) + '...' : repo.name)} (${repo.count} PRs)`; + Projects & PRs + `; + repoList.slice(0, 4).forEach((repo, i) => { + const ly = i * 24; + content += `• ${escapeXml(repo.name.length > 32 ? repo.name.substring(0, 29) + '...' : repo.name)} (${repo.count} PRs)`; }); content += ` - - Detailed Status - - Merged PRs: ${mergedPRs} - Open PRs: ${openPRs} - Closed Issues: ${closedIssues} + + Performance & Streaks + + + 🔥 Current Streak: ${currentStreak} Days + + + 🏆 Best Streak: ${longestStreak} Days + `; - y += 90; + y += 130; - content += ``; - y += 35; + content += ``; + y += 40; - // --- CONTRIBUTION HEATMAP (Bottom) --- + // --- FOOTER: CONTRIBUTION HEATMAP (Full width) --- content += ` - Contribution Activity (Last Year) - `; + Contribution Activity (Last 12 Months) + `; const sortedDays = [...contributionDays].sort((a, b) => String(a.date).localeCompare(String(b.date))); const recent = sortedDays.slice(-371); - const cell = 11, gap = 3.2, step = cell + gap; + const cell = 11.5, gap = 3.5, step = cell + gap; recent.forEach((d, i) => { const col = Math.floor(i / 7); const row = i % 7; - content += ``; + content += ``; }); content += ``; - y += 130; + y += 145; - const cardHeight = y + 30; + const cardHeight = y + 40; return ` - - - + + + - - + + ${content} - gitlyy full dashboard + gitlyy ultimate dashboard • generated with real data `; }