From 41a728ce19700fc07e87c71920c8de7f707c43b9 Mon Sep 17 00:00:00 2001 From: multipleof4 Date: Tue, 8 Sep 2026 14:04:59 -0700 Subject: [PATCH] Initial commit: Display Repo Size for GitHub userscript --- DisplayRepoSizeGitHub.user.js | 96 +++++++++++++++++++++++++++++++++++ README.md | 24 +++++++++ 2 files changed, 120 insertions(+) create mode 100644 DisplayRepoSizeGitHub.user.js create mode 100644 README.md diff --git a/DisplayRepoSizeGitHub.user.js b/DisplayRepoSizeGitHub.user.js new file mode 100644 index 0000000..c832037 --- /dev/null +++ b/DisplayRepoSizeGitHub.user.js @@ -0,0 +1,96 @@ +// ==UserScript== +// @name Display Repo Size for GitHub +// @version 1.0.0 +// @description Displays the total repository size on GitHub repository pages (supports public & private repos via GH_PAT). +// @author OpenScript +// @match https://github.com/*/* +// @grant none +// ==/UserScript== + +(function() { + 'use strict'; + + const cache = new Map(); + const BADGE_ID = 'openscript-repo-size'; + + // Format KB to readable size + const formatBytes = kb => { + if (kb < 1024) return `${kb} KB`; + if (kb < 1024 * 1024) return `${(kb / 1024).toFixed(1)} MB`; + return `${(kb / (1024 * 1024)).toFixed(2)} GB`; + }; + + // Parse owner & repo from path + const getRepoInfo = () => { + const [, owner, repo] = location.pathname.split('/'); + const reserved = new Set(['settings', 'orgs', 'organizations', 'notifications', 'search', 'features', 'pricing', 'explore']); + return (owner && repo && !reserved.has(owner)) ? { owner, repo } : null; + }; + + // Fetch size from GitHub API with optional GH_PAT + const fetchRepoSize = async (owner, repo) => { + const key = `${owner}/${repo}`; + if (cache.has(key)) return cache.get(key); + + const token = window.OpenScript?.env?.GH_PAT || window.env?.GH_PAT; + const headers = { Accept: 'application/vnd.github.v3+json' }; + if (token) headers.Authorization = `Bearer ${token}`; + + try { + const res = await fetch(`https://api.github.com/repos/${owner}/${repo}`, { headers }); + if (!res.ok) return res.status === 404 ? 'private/missing GH_PAT' : 'error'; + const data = await res.json(); + const formatted = formatBytes(data.size); + cache.set(key, formatted); + return formatted; + } catch { + return null; + } + }; + + // Inject or update size badge + const updateBadge = async () => { + const info = getRepoInfo(); + if (!info) return; + + // Anchor locations on GitHub repo pages + const anchor = document.querySelector('.file-navigation') || + document.querySelector('[data-testid="latest-commit-details"]') || + document.querySelector('.BorderGrid-cell .d-flex') || + document.querySelector('#repository-container-header ul'); + + if (!anchor || document.getElementById(BADGE_ID)) return; + + const badge = document.createElement('div'); + badge.id = BADGE_ID; + badge.className = 'd-inline-flex flex-items-center mr-2 px-2 py-1 text-bold text-small rounded-2 border color-border-default color-bg-subtle'; + badge.style.cssText = 'align-self: center; font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;'; + badge.innerHTML = ` + + calculating... + `; + + anchor.prepend(badge); + + const size = await fetchRepoSize(info.owner, info.repo); + const sizeSpan = badge.querySelector('.size-text'); + if (sizeSpan) sizeSpan.textContent = size ? size : 'unknown'; + }; + + // Re-run on Turbo navigation & DOM mutations + ['turbo:load', 'turbo:render', 'pjax:end', 'popstate'].forEach(ev => + window.addEventListener(ev, () => { + document.getElementById(BADGE_ID)?.remove(); + updateBadge(); + }) + ); + + const observer = new MutationObserver(() => { + if (!document.getElementById(BADGE_ID)) updateBadge(); + }); + + observer.observe(document.body, { childList: true, subtree: true }); + updateBadge(); +})(); diff --git a/README.md b/README.md new file mode 100644 index 0000000..b085b81 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Display Repo Size for GitHub (OpenScript Userscript) + +A lightweight OpenScript userscript that displays the total repository size directly on GitHub repository pages (for both public and private repositories). + +## Features +- Displays total repo size (formatted in KB, MB, or GB) next to the file navigation header. +- Works seamlessly on public and private repositories. +- Integrates with OpenScript's synced secrets: uses `GH_PAT` (GitHub Personal Access Token) to authenticate API requests, unlock private repo access, and elevate the rate limit from 60 to 5,000 req/hr. +- Supports GitHub's Turbo and SPA client-side navigations. + +## Installation in OpenScript +1. Open the **OpenScript** extension popup. +2. Click **+ New** in the header. +3. Copy and paste the code from [`DisplayRepoSizeGitHub.user.js`](./DisplayRepoSizeGitHub.user.js) into the editor. +4. Click **save script**. + +## Private Repos & GitHub PAT Configuration +To view the size of private repositories: +1. Generate a GitHub Personal Access Token (`repo` scope for private repos) at [github.com/settings/tokens](https://github.com/settings/tokens). +2. Open **OpenScript** and switch to the **Secrets** tab. +3. Add a secret with: + - **Key**: `GH_PAT` + - **Value**: `` +4. Click **+ Add**. The secret will sync across your devices via `chrome.storage.sync` and will be automatically available to the script.