Injects "Clone with GitHub Desktop" button into GitHub's dynamic Code popover.
// ==UserScript==
// @name GitHub Desktop Clone Bridge (Linux)
// @namespace https://github.com/
// @version 1.0.0
// @description Injects "Clone with GitHub Desktop" button into GitHub's dynamic Code popover.
// @author thexmeta
// @match https://github.com/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=github.com
// @grant none
// ==/UserScript==
(function() {
'use strict';
const CONFIG = {
PROTOCOL: 'x-github-client://openRepo/',
INJECT_ID: 'tm-x-github-client-bridge'
};
// [Fact] Extracts canonical repo URL bypassing branch/path artifacts
function extractRepoUri() {
const meta = document.querySelector('meta[name="go-import"]');
if (meta) {
return 'https://' + meta.getAttribute('content').split(' ')[0];
}
const path = window.location.pathname.split('/').filter(Boolean);
return path.length >= 2 ? `https://github.com/${path[0]}/${path[1]}` : null;
}
function buildNode(repoUrl) {
const li = document.createElement('li');
li.className = 'Box-row Box-row--hover-gray p-3 mt-0 rounded-0';
li.id = CONFIG.INJECT_ID;
const a = document.createElement('a');
a.href = `${CONFIG.PROTOCOL}${repoUrl}`;
a.className = 'd-flex flex-items-center color-fg-default text-bold no-underline';
// Native Primer Octicon for Desktop Download
a.innerHTML = `
<svg aria-hidden="true" height="16" viewBox="0 0 16 16" width="16" class="octicon octicon-desktop-download mr-3">
<path d="M1.75 2.5h12.5a.25.25 0 0 1 .25.25v7.5a.25.25 0 0 1-.25.25H1.75a.25.25 0 0 1-.25-.25v-7.5a.25.25 0 0 1 .25-.25ZM0 2.75C0 1.784.784 1 1.75 1h12.5c.966 0 1.75.784 1.75 1.75v7.5A1.75 1.75 0 0 1 14.25 12h-3.727c.099 1.041.52 1.872 1.292 2.757A.75.75 0 0 1 11.25 16h-6.5a.75.75 0 0 1-.565-1.243c.772-.885 1.193-1.716 1.292-2.757H1.75A1.75 1.75 0 0 1 0 10.25Zm8 11.664c-1.03-.546-1.574-1.442-1.724-2.414h3.448c-.15.972-.694 1.868-1.724 2.414Z"></path>
</svg>
Clone with GitHub Desktop
`;
li.appendChild(a);
return li;
}
function interceptDom() {
if (document.getElementById(CONFIG.INJECT_ID)) return;
// Target: Find the Download ZIP node via XPath to ensure structural resilience
const zipXPath = document.evaluate(
"//a[contains(., 'Download ZIP')]",
document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null
).singleNodeValue;
if (zipXPath) {
const listContainer = zipXPath.closest('ul');
if (listContainer && !listContainer.querySelector(`#${CONFIG.INJECT_ID}`)) {
const repoUrl = extractRepoUri();
if (repoUrl) {
listContainer.appendChild(buildNode(repoUrl));
}
}
}
}
// Observer mitigates GitHub Turbo dynamic routing
new MutationObserver(interceptDom).observe(document.body, { childList: true, subtree: true });
})();