GitHub Network Shortcut

Adds a shortcut to the network graph on the repository's main page

От 12.02.2025. Виж последната версия.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey, Greasemonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да инсталирате разширение, като например Tampermonkey .

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Violentmonkey.

За да инсталирате този скрипт, трябва да имате инсталирано разширение като Tampermonkey или Userscripts.

За да инсталирате скрипта, трябва да инсталирате разширение като Tampermonkey.

За да инсталирате този скрипт, трябва да имате инсталиран скриптов мениджър.

(Вече имам скриптов мениджър, искам да го инсталирам!)

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да инсталирате разширение като Stylus.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

За да инсталирате този стил, трябва да имате инсталиран мениджър на потребителски стилове.

(Вече имам инсталиран мениджър на стиловете, искам да го инсталирам!)

// ==UserScript==
// @name         GitHub Network Shortcut
// @namespace    https://mkps.app/
// @version      0.1
// @description  Adds a shortcut to the network graph on the repository's main page
// @author       MK
// @match        *://github.com/*
// @icon         https://github.com/favicon.ico
// ==/UserScript==

(() => {
	"use strict";

	const ELEMENT_CLASS = "MK_userscript_ghNetworkShortcut_element";
	const CONTAINER_SELECTOR_LG = `#repo-content-turbo-frame .Layout-sidebar .about-margin > div.BorderGrid-row > div.BorderGrid-cell > div.hide-sm.hide-md`;
	const CONTAINER_SELECTOR_SM = `#responsive-meta-container ul[aria-label="Repository details"]`;

	function main() {
		forMatchingElementsForever(CONTAINER_SELECTOR_LG, (element) => {
			if (element.querySelector(`.${ELEMENT_CLASS}`)) {return;}
			for (const child of element.children) {
				if (child.children[0]?.getAttribute("href")?.endsWith("/forks")) {
					child.insertAdjacentElement("afterend", createShortcutLg());
					break;
				}
			}
		});
		forMatchingElementsForever(CONTAINER_SELECTOR_SM, (element) => {
			if (element.querySelector(`.${ELEMENT_CLASS}`)) {return;}
			for (const child of element.children) {
				if (child.getAttribute("href")?.endsWith("/activity")) {
					child.insertAdjacentElement("afterend", createShortcutSm());
					break;
				}
			}
		});
	}

	function createShortcutLg() {
		const div = document.createElement("div");
		div.classList.add(ELEMENT_CLASS);
		div.classList.add("mt-2");
		const a = document.createElement("a");
		a.setAttribute("href", `${location.pathname}/network`);
		a.setAttribute("data-view-component", "true");
		a.classList.add("Link", "Link--muted");
		a.appendChild(createCommitIcon("mr-2"));
		a.appendChild(document.createTextNode(" "));
		const span = document.createElement("span");
		span.classList.add("color-fg-muted");
		span.textContent = "Network";
		a.appendChild(span);
		div.appendChild(a);
		return div;
	}

	function createShortcutSm() {
		const a = document.createElement("a");
		a.classList.add(ELEMENT_CLASS, "Link--secondary", "no-underline", "d-block", "mr-2");
		a.setAttribute("role", "listitem");
		a.setAttribute("href", `${location.pathname}/network`);
		a.appendChild(createCommitIcon("mr-1"));
		a.appendChild(document.createTextNode(" "));
		const span = document.createElement("span");
		span.textContent = "Network";
		a.appendChild(span);
		return a;
	}

	function createCommitIcon(marginClass) {
		const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
		svg.setAttribute("version", "1.1");
		svg.setAttribute("width", "16");
		svg.setAttribute("height", "16");
		svg.setAttribute("viewBox", "0 0 16 16");
		svg.setAttribute("aria-hidden", "true");
		svg.setAttribute("data-view-component", "true");
		svg.classList.add("octicon", "octicon-commit", marginClass);
		svg.innerHTML = `<path d="M11.93 8.5a4.002 4.002 0 0 1-7.86 0H.75a.75.75 0 0 1 0-1.5h3.32a4.002 4.002 0 0 1 7.86 0h3.32a.75.75 0 0 1 0 1.5Zm-1.43-.75a2.5 2.5 0 1 0-5 0 2.5 2.5 0 0 0 5 0Z"></path>`;
		return svg;
	}

	function forMatchingElementsForever(selector, fn) {
		const observer = new MutationObserver((list, observer) => {
			for (const mutation of list) {
				for (const addedNode of mutation.addedNodes) {
					if (addedNode.nodeType !== Node.ELEMENT_NODE) {continue;}
					for (const newNode of addedNode.querySelectorAll(selector)) {
						fn(newNode);
					}
				}
			}
		});
		observer.observe(document.body, {childList: true, subtree: true});

		for (const element of document.querySelectorAll(selector)) {
			fn(element);
		}
	}

	main();
})();