prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

about.js (5333B)


      1 import { getWakaTimeData, subscribeWakaTimeRefresh } from "./wakatime.js";
      2 
      3 let unsubscribeAboutRefresh = null;
      4 
      5 export function initAbout() {
      6   initSSHTipCopy();
      7 
      8   const statsContainer = document.getElementById("activity-stats");
      9   const section = document.getElementById("activity-section");
     10   if (!statsContainer || !section) return;
     11 
     12   function hideActivity() {
     13     section.style.display = "none";
     14   }
     15 
     16   async function loadActivityData(data = null) {
     17     if (!section.isConnected) {
     18       if (unsubscribeAboutRefresh) {
     19         unsubscribeAboutRefresh();
     20         unsubscribeAboutRefresh = null;
     21       }
     22       return;
     23     }
     24 
     25     try {
     26       const stats = data || (await getWakaTimeData());
     27 
     28       if (!stats || !stats.Weekly || !Array.isArray(stats.Weekly.Days)) {
     29         hideActivity();
     30         return;
     31       }
     32 
     33       const totalText = stats.Weekly.TotalText;
     34       const dayBars = stats.Weekly.Days;
     35       if (!totalText || dayBars.length === 0) {
     36         hideActivity();
     37         return;
     38       }
     39 
     40       const days = dayBars.map((bar) => ({
     41         dayText: (bar.DayText || "").toLowerCase(),
     42         timeText: bar.TimeText || "",
     43         barWidthPercent:
     44           bar.BarWidthPercent && typeof bar.BarWidthPercent === "string"
     45             ? bar.BarWidthPercent.trim()
     46             : "0%",
     47       }));
     48 
     49       renderWeeklyStats(statsContainer, totalText, days);
     50     } catch {
     51       hideActivity();
     52     }
     53   }
     54 
     55   loadActivityData();
     56 
     57   if (unsubscribeAboutRefresh) {
     58     unsubscribeAboutRefresh();
     59   }
     60   unsubscribeAboutRefresh = subscribeWakaTimeRefresh((data) => {
     61     void loadActivityData(data);
     62   });
     63 }
     64 
     65 function initSSHTipCopy() {
     66   const copyButton = document.querySelector("[data-ssh-copy]");
     67   const commandElement = document.querySelector("[data-ssh-command]");
     68   if (!copyButton || !commandElement || copyButton.dataset.bound === "true") return;
     69 
     70   copyButton.dataset.bound = "true";
     71   copyButton.addEventListener("click", async () => {
     72     const command = (commandElement.textContent || "").trim();
     73     if (!command) return;
     74 
     75     const copied = await copyToClipboard(command);
     76     const originalLabel = copyButton.textContent || "copy";
     77     copyButton.textContent = copied ? "copied" : "failed";
     78     copyButton.classList.toggle("copied", copied);
     79 
     80     window.setTimeout(() => {
     81       if (!copyButton.isConnected) return;
     82       copyButton.textContent = originalLabel;
     83       copyButton.classList.remove("copied");
     84     }, 1200);
     85   });
     86 }
     87 
     88 async function copyToClipboard(text) {
     89   if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
     90     try {
     91       await navigator.clipboard.writeText(text);
     92       return true;
     93     } catch {
     94       return false;
     95     }
     96   }
     97 
     98   const input = document.createElement("textarea");
     99   input.value = text;
    100   input.setAttribute("readonly", "");
    101   input.style.position = "absolute";
    102   input.style.left = "-9999px";
    103   document.body.appendChild(input);
    104   input.select();
    105   const copied = document.execCommand("copy");
    106   document.body.removeChild(input);
    107   return copied;
    108 }
    109 
    110 function renderWeeklyStats(statsContainer, totalText, days) {
    111   statsContainer.innerHTML = "";
    112 
    113   const width = 24;
    114   const pre = document.createElement("pre");
    115   pre.className = "weekly-ascii";
    116 
    117   const totalLabel = document.createElement("span");
    118   totalLabel.className = "weekly-total-label";
    119   totalLabel.textContent = "total: ";
    120 
    121   const totalValue = document.createElement("span");
    122   totalValue.className = "weekly-total-value";
    123   totalValue.textContent = totalText;
    124 
    125   pre.appendChild(totalLabel);
    126   pre.appendChild(totalValue);
    127   pre.appendChild(document.createTextNode("\n\n"));
    128 
    129   days.forEach((day, index) => {
    130     const label = (day.dayText || "").slice(0, 3).padEnd(3, " ");
    131     const percent = clampPercent(day.barWidthPercent);
    132     const { filled, empty } = asciiBarSegments(percent, width);
    133 
    134     const dayLabel = document.createElement("span");
    135     dayLabel.className = "weekly-day-label";
    136     dayLabel.textContent = `${label} `;
    137 
    138     const barFrame = document.createElement("span");
    139     barFrame.className = "weekly-day-bar-frame";
    140     barFrame.appendChild(document.createTextNode("|"));
    141 
    142     const barFilled = document.createElement("span");
    143     barFilled.className = "weekly-day-bar-filled";
    144     barFilled.textContent = filled;
    145 
    146     const barEmpty = document.createElement("span");
    147     barEmpty.className = "weekly-day-bar-empty";
    148     barEmpty.textContent = empty;
    149 
    150     const dayTime = document.createElement("span");
    151     dayTime.className = "weekly-day-time";
    152     dayTime.textContent = ` ${day.timeText}`;
    153 
    154     barFrame.appendChild(barFilled);
    155     barFrame.appendChild(barEmpty);
    156     barFrame.appendChild(document.createTextNode("|"));
    157 
    158     pre.appendChild(dayLabel);
    159     pre.appendChild(barFrame);
    160     pre.appendChild(dayTime);
    161 
    162     if (index < days.length - 1) {
    163       pre.appendChild(document.createTextNode("\n"));
    164     }
    165   });
    166 
    167   statsContainer.appendChild(pre);
    168 }
    169 
    170 function clampPercent(value) {
    171   const parsed = Number.parseFloat((value || "0").replace("%", ""));
    172   if (!Number.isFinite(parsed)) return 0;
    173   if (parsed < 0) return 0;
    174   if (parsed > 100) return 100;
    175   return parsed;
    176 }
    177 
    178 function asciiBarSegments(percent, width) {
    179   const filled = Math.round((percent / 100) * width);
    180   return {
    181     filled: "#".repeat(filled),
    182     empty: "-".repeat(width - filled),
    183   };
    184 }