The GitHub file browser lists a file's name and info about the last commit:
Is there a way to add each file's size to these listings?
If there is no official way to do this, here is a bookmarklet which uses GitHub's API to add size info (won't work in IE):
javascript:(function(){function f(a){var g=document.querySelector('div[role="rowheader"] a[title="'+a.name+'"]').closest('div[role="row"]').lastElementChild,c=document.createElement("div");c.style.width="5em";"file"===a.type&&(c.textContent=(a.size/1024).toLocaleString("en-US",{minimumFractionDigits:1,maximumFractionDigits:1})+" KB",c.style.textAlign="right",c.style.whiteSpace="nowrap");g.insertAdjacentElement("beforebegin",c)}var b=window.location.pathname.split("/"),d=b[1],h=b[2],e=b[4];b=b.slice(5);d=["https://api.github.com/repos",d,h,"contents"].concat(b||[]).join("/")+(e?"?ref="+e:"");console.log(d);fetch(d).then(function(a){return a.json()}).then(function(a){return Array.isArray(a)?a.forEach(f):console.warn(a)})})();
Unminified source:
(function () {
"use strict";
//Parse the current GitHub repo url. Examples:
// Repo root: /Sphinxxxx/vanilla-picker
// Subfolder: /Sphinxxxx/vanilla-picker/tree/master/src/css
// Subfolder at commit: /Sphinxxxx/vanilla-picker/tree/382231756aac75a49f046ccee1b04263196f9a22/src/css
// Subfolder at tag: /Sphinxxxx/vanilla-picker/tree/v2.2.0/src/css
//
//If applicable, the name of the commit/branch/tag is always the 4th element in the url path.
//Here, we put that in the "ref" variable:
const [/* Leading slash */, owner, repo, /* "tree" */, ref, ...path] = window.location.pathname.split('/');
//Create the URL to query GitHub's API: https://developer.github.com/v3/repos/contents/#get-contents
//Example:
// https://api.github.com/repos/Sphinxxxx/vanilla-picker/contents/src/css?ref=382231756aac75a49f046ccee1b04263196f9a22
const query = ['https://api.github.com/repos', owner, repo, 'contents'].concat(path || []),
url = query.join('/') + (ref ? '?ref=' + ref : '');
console.log(url);
//https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
fetch(url).then(r => r.json())
.then(j => Array.isArray(j) ? j.forEach(handleFileInfo) : console.warn(j));
function handleFileInfo(info) {
//console.log(info);
const link = document.querySelector(`div[role="rowheader"] a[title="${info.name}"]`);
const timeCol = link.closest('div[role="row"]').lastElementChild;
const sizeCol = document.createElement('div');
sizeCol.style.width = '5em';
if(info.type === 'file') {
//http://stackoverflow.com/a/17663871/1869660
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#Parameters
sizeCol.textContent = (info.size/1024).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
sizeCol.style.textAlign = 'right';
sizeCol.style.whiteSpace = 'nowrap';
}
timeCol.insertAdjacentElement('beforebegin', sizeCol);
}
})();
Create a bookmarklet from the first piece of code, or just copy and paste it to your browser's console.
For Chrome browsers there exists an extension:
https://chrome.google.com/webstore/detail/github-repository-size/apnjnioapinblneaedefcnopcjepgkci
You can force file sizes to appear with a JavaScript bookmarklet that calls the GitHub API.
GitHub in 2023 now has two kinds of file table (one is a <table>
, the other is <div>
-based) so I prepared this new bookmarklet that detects which style is in use before modifying it:
javascript:(function e(){let t=[...document.querySelectorAll('div[role="row"]')],n;if(t.length||(t=[...document.querySelectorAll("tbody tr")],n=document.querySelector("thead tr")),(t=t.slice(1)).length&&"github.com"===document.location.host){let[,l,i,r,o,...c]=window.location.pathname.split("/");if(i&&("tree"===r||!r)){let s=["https://api.github.com/repos",l,i,"contents"].concat(c||[]).join("/")+(o?"?ref="+o:"");console.log(s),fetch(s).then(e=>e.json()).then(e=>{let l=new Map;if(e.forEach(e=>l.set(e.name,e)),n&&"Size"!==n.children[1].innerText){let i=document.createElement("th");i.style.width="6em",i.textContent="Size",n.firstElementChild.insertAdjacentElement("afterend",i)}for(let r of t){let o=r.children[n?0:1],c=o.innerText;c.indexOf("\n")>0&&(c=c.slice(0,c.indexOf("\n")));let s=l.get(c),d=document.createElement(n?"td":"div");d.style.width="6em",d.className=o.className,d.innerText=a(s?.size),o.insertAdjacentElement("afterend",d)}})}else console.error("GITHUB PATH NOT UNDERSTOOD")}else console.error("GITHUB TABLE NOT FOUND; can't add file sizes");function a(e){if(!e)return null==e?"–":"0";if(e<0)return"-"+a(-e);let t;return e<1024?e+" B":(e<1048576?(t=" KiB",e/=1024):e<1073741824?(t=" MiB",e/=1048576):(t=" GiB",e/=1073741824),e.toFixed(1)+t)}})();
See instructions here for making a bookmarklet.
Here's the original unminified code:
(function addSizes() {
// Sometimes each row is like `<div role="row" class="Box-row...">` with no column headers
// e.g. https://github.com/qwertie/ecsharp
// Sometimes each row is like `<tr class="react-directory-row">` with header in `<thead...><tr...>`
// e.g. https://github.com/qwertie/ecsharp/tree/master/Main
// Look for the first kind of rows; if not found, look for the other kind
let rows = [...document.querySelectorAll(`div[role="row"]`)], header;
if (!rows.length) {
rows = [...document.querySelectorAll(`tbody tr`)];
header = document.querySelector(`thead tr`);
}
rows = rows.slice(1);
if (rows.length && document.location.host === 'github.com') {
// Parse path after https://github.com, e.g.
// /username/repo
// /username/repo/tree/branch-name
// /username/repo/tree/branch-name/folder-name
let [/* Leading slash */, owner, repo, __tree__, branch, ...path] = window.location.pathname.split('/');
if (repo && (__tree__ === 'tree' || !__tree__)) {
let query = ['https://api.github.com/repos', owner, repo, 'contents']
.concat(path || []).join('/') + (branch ? '?ref=' + branch : '');
console.log(query);
// The GitHub API will returns an array of objects like
// [{ "name": "file.py", "path": "folder/file.py", "size": 5226, ... }, ...]
fetch(query).then(r => r.json()).then(files => {
// Index the results by file name
let fileMap = new Map();
files.forEach(file => fileMap.set(file.name, file));
// If there is a header row, add a header cell for the file size. Note:
// If user browses to another folder, the 'Size' header column still exists
// but no file sizes. In that case, avoid adding another 'Size' column.
if (header && header.children[1].innerText !== 'Size') {
let sizeCol = document.createElement('th');
sizeCol.style.width = '6em';
sizeCol.textContent = 'Size';
header.firstElementChild.insertAdjacentElement('afterend', sizeCol);
}
// For each row of the table: get the file name, look it up in
// fileMap, get the file size, and insert a size column.
for (let row of rows) {
let nameCol = row.children[header ? 0 : 1];
let name = nameCol.innerText;
if (name.indexOf('\n') > 0) name = name.slice(0, name.indexOf('\n'));
let file = fileMap.get(name);
let sizeCol = document.createElement(header ? 'td' : 'div');
sizeCol.style.width = '6em';
sizeCol.className = nameCol.className;
sizeCol.innerText = formatFileSize(file?.size);
nameCol.insertAdjacentElement('afterend', sizeCol);
}
});
} else {
console.error('GITHUB PATH NOT UNDERSTOOD');
}
} else {
console.error("GITHUB TABLE NOT FOUND; can't add file sizes");
}
function formatFileSize(size) {
if (!size) return size == null ? '–' : '0';
if (size < 0) return '-' + formatFileSize(-size);
let suffix;
if (size < 1024) {
return size + ' B';
} else if (size < 1024*1024) {
suffix = ' KiB';
size /= 1024;
} else if (size < 1024*1024*1024) {
suffix = ' MiB';
size /= 1024*1024;
} else {
suffix = ' GiB';
size /= 1024*1024*1024;
}
return size.toFixed(1) + suffix;
};
})();
It's based loosely on @Sphinxxx's code, but the file size will be the second column rather than the third, and the units change automatically as the file size increases.
No, the GitHub file browser is not configurable that way.
Getting back that extra information would mean transferring an enormous extra amount of data (for each pages of each repos) for GitHub, so I am not sure this is a feature you would see anytime soon.
Note that 'size' is an acceptable criteria for GitHub search though (meaning that size information is there and can be used, not just for browsing files).
Matches code with the word "element" that's marked as being XML and has exactly 100 bytes.
Updated script/bookmarklet after @Sphinxxx answer, now with comments:
Bookmarklet:
!function(){"use strict";const[,t,e,,n,...i]=window.location.pathname.split("/"),o=["https://api.github.com/repos",t,e,"contents"].concat(i||[]).join("/")+(n?"?ref="+n:"");function r(t){var e=null,n=null;if("file"===t.type){const i=`div[role="rowheader"] a[title="${t.name}"]`;e=document.querySelector(i).closest('div[role="row"]').lastElementChild,(n=document.createElement("div")).style.width="5em",n.textContent=(t.size/1024).toLocaleString("en-US",{minimumFractionDigits:1,maximumFractionDigits:1})+" KB",n.style.textAlign="right",n.style.whiteSpace="nowrap",e.insertAdjacentElement("beforebegin",n)}}fetch(o).then((t=>t.json())).then((t=>Array.isArray(t)?t.forEach(r):console.warn("Not an array of files: ",t)))}();
Usage:
Right click, copy link, paste into address bar (or into bookmark link), prepend "javascript:" without quotes, press ENTER
Variant for new github layout with folders tree on the left and file contents on the right:
!function(){"use strict";const[,t,e,,n,...i]=window.location.pathname.split("/"),o=["https://api.github.com/repos",t,e,"contents"].concat(i||[]).join("/")+(n?"?ref="+n:"");function r(t){var e=null,n=null;if("file"===t.type){const i=`div[title="${t.name}"]`;e=document.querySelector(i).closest('tr[class="react-directory-row"]').lastElementChild,(n=document.createElement("td")).style.width="5em",n.innerHTML=(t.size/1024).toLocaleString("en-US",{minimumFractionDigits:1,maximumFractionDigits:1})+" KB",e.insertAdjacentElement("beforebegin",n)}}fetch(o).then((t=>t.json())).then((t=>Array.isArray(t)?t.forEach(r):console.warn("Not an array of files: ",t)))}();
Script 1:
(function () {
"use strict";
//Parse the current GitHub repo url. Examples:
// Repo root: /Sphinxxxx/vanilla-picker
// Subfolder: /Sphinxxxx/vanilla-picker/tree/master/src/css
// Subfolder at commit: /Sphinxxxx/vanilla-picker/tree/382231756aac75a49f046ccee1b04263196f9a22/src/css
// Subfolder at tag: /Sphinxxxx/vanilla-picker/tree/v2.2.0/src/css
//
//If applicable, the name of the commit/branch/tag is always the 4th element in the url path.
//Here, we put that in the "ref" variable:
const [/* Leading slash */, owner, repo, /* "tree" */, ref, ...path] = window.location.pathname.split('/'); // split url and store pieces into constants.
//Create the URL to query GitHub's API: https://developer.github.com/v3/repos/contents/#get-contents
//Example:
// https://api.github.com/repos/Sphinxxxx/vanilla-picker/contents/src/css?ref=382231756aac75a49f046ccee1b04263196f9a22;
const query = ['https://api.github.com/repos', owner, repo, 'contents'].concat(path || []),
url = query.join('/') + (ref ? '?ref=' + ref : '');
//https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
fetch(url).then(r => r.json())
.then(j => Array.isArray(j) ? j.forEach(handleFileInfo) : console.warn("Not an array of files: ",j));
function handleFileInfo(info) {
var timeCol = null;
var sizeCol = null;
var link = "";
var QR = "";
if(info.type === 'file') { // skip folders
const QR = `div[role="rowheader"] a[title="${info.name}"]`; // select the cell containing the file name
link = document.querySelector(QR);
///// Climb along the html hierarchy until it finds a DIV named "row",
///// and get last element (last cell of row), i.e. the date column:
timeCol = link.closest('div[role="row"]').lastElementChild;
///// Create label for file size
sizeCol = document.createElement('div');
sizeCol.style.width = '5em';
//http://stackoverflow.com/a/17663871/1869660
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#Parameters
sizeCol.textContent = (info.size/1024).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
sizeCol.style.textAlign = 'right';
sizeCol.style.whiteSpace = 'nowrap';
///// Insert new label before last element of row:
timeCol.insertAdjacentElement('beforebegin', sizeCol);
} else {
// skip folders
}
}
})();
Script 2:
(function () {
"use strict";
//Parse the current GitHub repo url. Examples:
// Repo root: /Sphinxxxx/vanilla-picker
// Subfolder: /Sphinxxxx/vanilla-picker/tree/master/src/css
// Subfolder at commit: /Sphinxxxx/vanilla-picker/tree/382231756aac75a49f046ccee1b04263196f9a22/src/css
// Subfolder at tag: /Sphinxxxx/vanilla-picker/tree/v2.2.0/src/css
//
//If applicable, the name of the commit/branch/tag is always the 4th element in the url path.
//Here, we put that in the "ref" variable:
const [/* Leading slash */, owner, repo, /* "tree" */, ref, ...path] = window.location.pathname.split('/'); // split url and store pieces into constants.
//Create the URL to query GitHub's API: https://developer.github.com/v3/repos/contents/#get-contents
//Example:
// https://api.github.com/repos/Sphinxxxx/vanilla-picker/contents/src/css?ref=382231756aac75a49f046ccee1b04263196f9a22;
const query = ['https://api.github.com/repos', owner, repo, 'contents'].concat(path || []),
url = query.join('/') + (ref ? '?ref=' + ref : '');
//https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch
fetch(url).then(r => r.json())
.then(j => Array.isArray(j) ? j.forEach(handleFileInfo) : console.warn("Not an array of files: ",j));
function handleFileInfo(info) {
var timeCol = null;
var sizeCol = null;
var link = "";
var QR = "";
if(info.type === 'file') { // skip folders
const QR = `div[title="${info.name}"]`; // select the cell containing the file name
link = document.querySelector(QR);
///// Climb along the html hierarchy until it finds a DIV named "row",
///// and get last element (last cell of row), i.e. the date column:
timeCol = link.closest('tr[class="react-directory-row"]').lastElementChild;
///// Create label for file size
sizeCol = document.createElement('td');
sizeCol.style.width = '5em';
//http://stackoverflow.com/a/17663871/1869660
//https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString#Parameters
sizeCol.innerHTML = (info.size/1024).toLocaleString('en-US', { minimumFractionDigits: 1, maximumFractionDigits: 1 }) + ' KB';
//sizeCol.style.textAlign = 'right';
//sizeCol.style.whiteSpace = 'nowrap';
///// Insert new label before last element of row:
timeCol.insertAdjacentElement('beforebegin', sizeCol);
} else {
// skip folders
}
}
})();