about.js (3891B)
1 import { getWakaTimeData, subscribeWakaTimeRefresh } from "./wakatime.js"; 2 3 let unsubscribeAboutRefresh = null; 4 5 export function initAbout() { 6 const statsContainer = document.getElementById("activity-stats"); 7 const section = document.getElementById("activity-section"); 8 if (!statsContainer || !section) return; 9 10 function hideActivity() { 11 section.style.display = "none"; 12 } 13 14 async function loadActivityData(data = null) { 15 if (!section.isConnected) { 16 if (unsubscribeAboutRefresh) { 17 unsubscribeAboutRefresh(); 18 unsubscribeAboutRefresh = null; 19 } 20 return; 21 } 22 23 try { 24 const stats = data || (await getWakaTimeData()); 25 26 if (!stats || !stats.Weekly || !Array.isArray(stats.Weekly.Days)) { 27 hideActivity(); 28 return; 29 } 30 31 const totalText = stats.Weekly.TotalText; 32 const dayBars = stats.Weekly.Days; 33 if (!totalText || dayBars.length === 0) { 34 hideActivity(); 35 return; 36 } 37 38 const days = dayBars.map((bar) => ({ 39 dayText: (bar.DayText || "").toLowerCase(), 40 timeText: bar.TimeText || "", 41 barWidthPercent: 42 bar.BarWidthPercent && typeof bar.BarWidthPercent === "string" 43 ? bar.BarWidthPercent.trim() 44 : "0%", 45 })); 46 47 renderWeeklyStats(statsContainer, totalText, days); 48 } catch { 49 hideActivity(); 50 } 51 } 52 53 loadActivityData(); 54 55 if (unsubscribeAboutRefresh) { 56 unsubscribeAboutRefresh(); 57 } 58 unsubscribeAboutRefresh = subscribeWakaTimeRefresh((data) => { 59 void loadActivityData(data); 60 }); 61 } 62 63 function renderWeeklyStats(statsContainer, totalText, days) { 64 statsContainer.innerHTML = ""; 65 66 const width = 24; 67 const pre = document.createElement("pre"); 68 pre.className = "weekly-ascii"; 69 70 const totalLabel = document.createElement("span"); 71 totalLabel.className = "weekly-total-label"; 72 totalLabel.textContent = "total: "; 73 74 const totalValue = document.createElement("span"); 75 totalValue.className = "weekly-total-value"; 76 totalValue.textContent = totalText; 77 78 pre.appendChild(totalLabel); 79 pre.appendChild(totalValue); 80 pre.appendChild(document.createTextNode("\n\n")); 81 82 days.forEach((day, index) => { 83 const label = (day.dayText || "").slice(0, 3).padEnd(3, " "); 84 const percent = clampPercent(day.barWidthPercent); 85 const { filled, empty } = asciiBarSegments(percent, width); 86 87 const dayLabel = document.createElement("span"); 88 dayLabel.className = "weekly-day-label"; 89 dayLabel.textContent = `${label} `; 90 91 const barFrame = document.createElement("span"); 92 barFrame.className = "weekly-day-bar-frame"; 93 barFrame.appendChild(document.createTextNode("|")); 94 95 const barFilled = document.createElement("span"); 96 barFilled.className = "weekly-day-bar-filled"; 97 barFilled.textContent = filled; 98 99 const barEmpty = document.createElement("span"); 100 barEmpty.className = "weekly-day-bar-empty"; 101 barEmpty.textContent = empty; 102 103 const dayTime = document.createElement("span"); 104 dayTime.className = "weekly-day-time"; 105 dayTime.textContent = ` ${day.timeText}`; 106 107 barFrame.appendChild(barFilled); 108 barFrame.appendChild(barEmpty); 109 barFrame.appendChild(document.createTextNode("|")); 110 111 pre.appendChild(dayLabel); 112 pre.appendChild(barFrame); 113 pre.appendChild(dayTime); 114 115 if (index < days.length - 1) { 116 pre.appendChild(document.createTextNode("\n")); 117 } 118 }); 119 120 statsContainer.appendChild(pre); 121 } 122 123 function clampPercent(value) { 124 const parsed = Number.parseFloat((value || "0").replace("%", "")); 125 if (!Number.isFinite(parsed)) return 0; 126 if (parsed < 0) return 0; 127 if (parsed > 100) return 100; 128 return parsed; 129 } 130 131 function asciiBarSegments(percent, width) { 132 const filled = Math.round((percent / 100) * width); 133 return { 134 filled: "#".repeat(filled), 135 empty: "-".repeat(width - filled), 136 }; 137 } 138