您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add a 'Repositories' tab shortcut to quickly access a user's repositories.
// ==UserScript== // @name GitHub Repositories Shortcut // @description Add a 'Repositories' tab shortcut to quickly access a user's repositories. // @icon https://github.githubassets.com/favicons/favicon-dark.svg // @version 1.0 // @author afkarxyz // @namespace https://github.com/afkarxyz/userscripts/ // @supportURL https://github.com/afkarxyz/userscripts/issues // @license MIT // @match https://github.com/* // @grant none // @run-at document-idle // ==/UserScript== (function() { 'use strict'; const REPO_ICON_SVG_STRING = `<svg aria-hidden="true" height="16" viewBox="0 0 16 16" version="1.1" width="16" data-view-component="true" class="octicon octicon-repo"><path d="M2 2.5A2.5 2.5 0 0 1 4.5 0h8.75a.75.75 0 0 1 .75.75v12.5a.75.75 0 0 1-.75.75h-2.5a.75.75 0 0 1 0-1.5h1.75v-2h-8a1 1 0 0 0-.714 1.7.75.75 0 1 1-1.072 1.05A2.495 2.495 0 0 1 2 11.5Zm10.5-1h-8a1 1 0 0 0-1 1v6.708A2.486 2.486 0 0 1 4.5 9h8ZM5 12.25a.25.25 0 0 1 .25-.25h3.5a.25.25 0 0 1 .25.25v3.25a.25.25 0 0 1-.4.2l-1.45-1.087a.249.249 0 0 0-.3 0L5.4 15.7a.25.25 0 0 1-.4-.2Z"></path></svg>`; const DESKTOP_TAB_ID = 'user-repositories-desktop-tab-v16'; const MOBILE_ICON_ID = 'user-repositories-mobile-icon-v16'; const DESKTOP_NAV_SELECTOR = '.AppHeader-localBar nav.js-repo-nav ul.UnderlineNav-body'; const INSIGHTS_TAB_SELECTOR = 'a#insights-tab'; const MOBILE_BUTTONS_GROUP_SELECTOR = '.d-block.d-md-none .d-flex.gap-2.mt-n3.mb-3.flex-wrap > .d-flex.flex-row.gap-2:first-of-type'; const MOBILE_STAR_CONTAINER_SELECTOR = '.js-toggler-container.starring-container'; const CHECK_INTERVAL_MS = 500; const MAX_CHECKS_PER_PAGE = 20; const GLOBAL_PAGES = ['pulls', 'issues', 'marketplace', 'explore', 'topics', 'sponsors', 'settings', 'notifications', 'new', 'codespaces', 'organizations', 'orgs', 'gist', 'logout', 'login', 'features', 'about', 'pricing', 'security']; let currentUrl = location.href; let mainIntervalId = null; let checksDone = 0; function getElement(selector) { return document.querySelector(selector); } function getUsername() { const pathParts = window.location.pathname.split('/').filter(Boolean); return pathParts[0] || null; } function isRepoPage() { if (!getElement(DESKTOP_NAV_SELECTOR) && !getElement(MOBILE_BUTTONS_GROUP_SELECTOR)) return false; const pathParts = window.location.pathname.split('/').filter(Boolean); if (pathParts.length < 2 || (pathParts.length === 1 && window.location.search.includes('tab=repositories')) || GLOBAL_PAGES.includes(pathParts[0])) { return false; } return true; } function removeElements() { [DESKTOP_TAB_ID, MOBILE_ICON_ID].forEach(id => { const el = document.getElementById(id); if (el) (el.closest('li') || el).remove(); }); } function createLinkElement(id, username, isMobile) { const link = document.createElement('a'); link.id = id; link.href = `https://github.com/${username}?tab=repositories`; link.dataset.viewComponent = 'true'; if (isMobile) { link.className = 'Button Button--iconOnly Button--secondary Button--medium tooltipped tooltipped-s'; link.setAttribute('aria-label', `View ${username}'s repositories`); link.innerHTML = REPO_ICON_SVG_STRING.replace('<svg ', '<svg class="octicon octicon-repo Button-visual" '); } else { link.className = 'UnderlineNav-item no-wrap js-responsive-underlinenav-item'; link.innerHTML = ` ${REPO_ICON_SVG_STRING.replace('<svg ', '<svg class="octicon octicon-repo UnderlineNav-octicon d-none d-sm-inline" ')} <span data-content="Repositories">Repositories</span> <span class="Counter" title="User Repositories" data-view-component="true"></span> `; } return link; } function addDesktopTab(username) { if (document.getElementById(DESKTOP_TAB_ID)) return true; const navBody = getElement(DESKTOP_NAV_SELECTOR); if (!navBody) return false; const insightsTabLink = navBody.querySelector(INSIGHTS_TAB_SELECTOR); if (!insightsTabLink) return false; const insightsTabLi = insightsTabLink.closest('li.d-inline-flex'); if (!insightsTabLi) return false; const newTabLi = document.createElement('li'); newTabLi.className = 'd-inline-flex'; newTabLi.dataset.viewComponent = 'true'; newTabLi.appendChild(createLinkElement(DESKTOP_TAB_ID, username, false)); insightsTabLi.parentNode.insertBefore(newTabLi, insightsTabLi.nextSibling); return true; } function addMobileIconButton(username) { if (document.getElementById(MOBILE_ICON_ID)) return true; const buttonsContainer = getElement(MOBILE_BUTTONS_GROUP_SELECTOR); if (!buttonsContainer) return false; const mobileIconLink = createLinkElement(MOBILE_ICON_ID, username, true); const wrapperDiv = document.createElement('div'); wrapperDiv.className = 'd-inline-block'; wrapperDiv.appendChild(mobileIconLink); const starButtonContainer = buttonsContainer.querySelector(MOBILE_STAR_CONTAINER_SELECTOR) || buttonsContainer.querySelector('.BtnGroup + .js-toggler-container'); if (starButtonContainer && starButtonContainer.parentNode === buttonsContainer) { buttonsContainer.insertBefore(wrapperDiv, starButtonContainer.nextSibling); } else { buttonsContainer.appendChild(wrapperDiv); } return true; } function runCheck() { checksDone++; if (checksDone > MAX_CHECKS_PER_PAGE && mainIntervalId) { clearInterval(mainIntervalId); mainIntervalId = null; return; } if (isRepoPage()) { const username = getUsername(); if (!username) return; const desktopAdded = addDesktopTab(username); const mobileAdded = addMobileIconButton(username); if ((desktopAdded || mobileAdded || document.getElementById(DESKTOP_TAB_ID) || document.getElementById(MOBILE_ICON_ID)) && currentUrl === location.href && mainIntervalId) { clearInterval(mainIntervalId); mainIntervalId = null; } } else { removeElements(); if (!mainIntervalId && checksDone <= MAX_CHECKS_PER_PAGE) { startInterval(); } } } function startInterval() { if (mainIntervalId) clearInterval(mainIntervalId); checksDone = 0; mainIntervalId = setInterval(runCheck, CHECK_INTERVAL_MS); runCheck(); } function handlePageChange() { if (location.href !== currentUrl) { currentUrl = location.href; removeElements(); startInterval(); } else if (!mainIntervalId) { startInterval(); } } startInterval(); document.addEventListener('turbo:load', handlePageChange); let oldHrefForObserver = document.location.href; const bodyObserver = new MutationObserver(() => { if (oldHrefForObserver !== document.location.href) { oldHrefForObserver = document.location.href; handlePageChange(); } }); const bodyElement = getElement("body"); if (bodyElement) bodyObserver.observe(bodyElement, { childList: true, subtree: true }); window.addEventListener('beforeunload', () => { if (mainIntervalId) clearInterval(mainIntervalId); bodyObserver.disconnect(); }); })();