// Paste your token here (fine-grained PATs starting with "github_pat_" are OK) // Leave empty "" if you only care about public repos (lower rate-limit). const TOKEN = "PASTE_YOUR_NEW_TOKEN_HERE";
const sizeCache = new Map(); // href -> humanReadableSize let scanTimer = null;
// Determine where the label should be appended (same logic as original) let parent = elem.parentElement; if (pageType === "repo") { parent = elem.parentElement.parentElement; }
// Skip if label already exists (prevents repeat API calls on SPA re-render) if (parent.querySelector(`#mshll-repo-size`) !== null) return;
try { const sizeText = await fetchRepoSize(href); if (!sizeText) return; ensureSizeLabel(parent, sizeText); } catch (_) { // swallow errors silently to preserve original behavior (no error box) return; } }); };
// Run once, then keep re-checking as GitHub loads content dynamically addSizeToRepos();
let lastUrl = location.href; new MutationObserver(() => { const url = location.href; // Re-run both when URL changes and when content loads for the same URL. // Debounced to avoid hammering during heavy DOM updates. if (url !== lastUrl) lastUrl = url; scheduleScan(); }).observe(document, { subtree: true, childList: true });
// ==UserScript==
// @name Github Repo Size
// @namespace mshll
// @description Adds the repo size next to the repo name on github search and repo pages
// @version 0.1.1-patched
// @author mshll (patched)
// @match *://github.com/search*
// @match *://github.com/*/*
// @grant none
// @icon https://www.google.com/s2/favicons?domain=github.com
// @license MIT
// @source https://github.com/mshll/repo-size
// ==/UserScript==
"use strict";
// Paste your token here (fine-grained PATs starting with "github_pat_" are OK)
// Leave empty "" if you only care about public repos (lower rate-limit).
const TOKEN = "PASTE_YOUR_NEW_TOKEN_HERE";
const sizeCache = new Map(); // href -> humanReadableSize
let scanTimer = null;
const getPageType = () => {
const { pathname, search } = window.location;
const params = new URLSearchParams(search);
const [, username, repo] = pathname.split("/");
const q = params.get("q")?.toLocaleLowerCase();
const type = params.get("type")?.toLocaleLowerCase();
if (username && repo) return "repo";
if (q && type === "code") return "code_search";
if (q) return "search";
};
const scheduleScan = () => {
if (scanTimer) clearTimeout(scanTimer);
scanTimer = setTimeout(() => {
scanTimer = null;
addSizeToRepos();
}, 250);
};
const buildHeaders = () => {
// GitHub docs: Bearer or token both work for PATs. :contentReference[oaicite:2]{index=2}
const headers = {
"Accept": "application/vnd.github+json",
"X-GitHub-Api-Version": "2022-11-28",
};
if (TOKEN && TOKEN.trim()) {
headers["Authorization"] = `Bearer ${TOKEN.trim()}`;
}
return headers;
};
const humanizeBytes = (bytes) => {
const sizes = ["B", "KB", "MB", "GB", "TB"];
if (!bytes || bytes <= 0) return "0 B";
const i = Math.floor(Math.log(bytes) / Math.log(1024));
return (bytes / Math.pow(1024, i)).toFixed(1) + " " + sizes[i];
};
const ensureSizeLabel = (parent, sizeText) => {
let sizeContainer = parent.querySelector(`#mshll-repo-size`);
if (sizeContainer !== null) return; // already added
sizeContainer = document.createElement("span");
sizeContainer.id = "mshll-repo-size";
sizeContainer.classList.add("Label", "Label--info", "v-align-middle", "ml-1");
sizeContainer.setAttribute("title", "Repository size");
// Create the size icon (same as original)
let sizeSVG = document.createElementNS("http://www.w3.org/2000/svg", "svg");
sizeSVG.setAttribute("aria-hidden", "true");
sizeSVG.setAttribute("viewBox", "-4 -4 22 22");
sizeSVG.setAttribute("width", "16");
sizeSVG.setAttribute("height", "16");
sizeSVG.setAttribute("fill", "currentColor");
sizeSVG.setAttribute("data-view-component", "true");
sizeSVG.classList.add("octicon", "octicon-file-directory", "mr-1");
let sizeSVGPath = document.createElementNS("http://www.w3.org/2000/svg", "path");
sizeSVGPath.setAttribute("fill-rule", "evenodd");
sizeSVGPath.setAttribute(
"d",
"M1 3.5c0-.626.292-1.165.7-1.59.406-.422.956-.767 1.579-1.041C4.525.32 6.195 0 8 0c1.805 0 3.475.32 4.722.869.622.274 1.172.62 1.578 1.04.408.426.7.965.7 1.591v9c0 .626-.292 1.165-.7 1.59-.406.422-.956.767-1.579 1.041C11.476 15.68 9.806 16 8 16c-1.805 0-3.475-.32-4.721-.869-.623-.274-1.173-.62-1.579-1.04-.408-.426-.7-.965-.7-1.591Zm1.5 0c0 .133.058.318.282.551.227.237.591.483 1.101.707C4.898 5.205 6.353 5.5 8 5.5c1.646 0 3.101-.295 4.118-.742.508-.224.873-.471 1.1-.708.224-.232.282-.417.282-.55 0-.133-.058-.318-.282-.551-.227-.237-.591-.483-1.101-.707C11.102 1.795 9.647 1.5 8 1.5c-1.646 0-3.101.295-4.118.742-.508.224-.873.471-1.1.708-.224.232-.282.417-.282.55Zm0 4.5c0 .133.058.318.282.551.227.237.591.483 1.101.707C4.898 9.705 6.353 10 8 10c1.646 0 3.101-.295 4.118-.742.508-.224.873-.471 1.1-.708.224-.232.282-.417.282-.55V5.724c-.241.15-.503.286-.778.407C11.475 6.68 9.805 7 8 7c-1.805 0-3.475-.32-4.721-.869a6.15 6.15 0 0 1-.779-.407Zm0 2.225V12.5c0 .133.058.318.282.55.227.237.592.484 1.1.708 1.016.447 2.471.742 4.118.742 1.647 0 3.102-.295 4.117-.742.51-.224.874-.47 1.101-.707.224-.233.282-.418.282-.551v-2.275c-.241.15-.503.285-.778.406-1.247.549-2.917.869-4.722.869-1.805 0-3.475-.32-4.721-.869a6.327 6.327 0 0 1-.779-.406Z"
);
sizeSVG.appendChild(sizeSVGPath);
sizeContainer.innerHTML = `${sizeText}`;
sizeContainer.prepend(sizeSVG);
parent.appendChild(sizeContainer);
};
const fetchRepoSize = async (href) => {
if (sizeCache.has(href)) return sizeCache.get(href);
const res = await fetch(`https://api.github.com/repos${href}`, {
headers: buildHeaders(),
});
// If token is invalid, GitHub returns 401; if rate-limited, 403.
const jsn = await res.json();
if (jsn?.message) return null;
// GitHub API returns size in KB; convert to bytes.
const bytes = jsn.size * 1024;
const pretty = humanizeBytes(bytes);
sizeCache.set(href, pretty);
return pretty;
};
const addSizeToRepos = () => {
const pageType = getPageType();
let repoSelector;
switch (pageType) {
case "repo":
repoSelector = "#repository-container-header strong a";
break;
case "search":
repoSelector = "li.repo-list-item .f4 a";
break;
case "code_search":
repoSelector = ".code-list-item text-small Link--secondary"; // left as-is from original
break;
default:
return;
}
document.querySelectorAll(repoSelector).forEach(async (elem) => {
const href = elem.getAttribute("href");
if (!href || !href.startsWith("/")) return;
// Determine where the label should be appended (same logic as original)
let parent = elem.parentElement;
if (pageType === "repo") {
parent = elem.parentElement.parentElement;
}
// Skip if label already exists (prevents repeat API calls on SPA re-render)
if (parent.querySelector(`#mshll-repo-size`) !== null) return;
try {
const sizeText = await fetchRepoSize(href);
if (!sizeText) return;
ensureSizeLabel(parent, sizeText);
} catch (_) {
// swallow errors silently to preserve original behavior (no error box)
return;
}
});
};
// Run once, then keep re-checking as GitHub loads content dynamically
addSizeToRepos();
let lastUrl = location.href;
new MutationObserver(() => {
const url = location.href;
// Re-run both when URL changes and when content loads for the same URL.
// Debounced to avoid hammering during heavy DOM updates.
if (url !== lastUrl) lastUrl = url;
scheduleScan();
}).observe(document, { subtree: true, childList: true });