您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Adds a clickable profile icon to identify personal or organizational accounts.
当前为
// ==UserScript== // @name GitHub Profile Icon // @description Adds a clickable profile icon to identify personal or organizational accounts. // @icon https://github.githubassets.com/favicons/favicon-dark.svg // @version 1.0 // @author afkarxyz // @namespace https://github.com/afkarxyz/misc-scripts/ // @supportURL https://github.com/afkarxyz/misc-scripts/issues // @license MIT // @match https://github.com/* // @grant GM_getValue // @grant GM_setValue // ==/UserScript== (function() { 'use strict'; const style = document.createElement('style'); style.textContent = ` .icon-wrapper { position: relative !important; display: inline-block !important; margin-left: 4px !important; } .profile-icon-tooltip { visibility: hidden; position: fixed !important; background: #24292e !important; color: white !important; padding: 4px 8px !important; border-radius: 6px !important; font-size: 12px !important; white-space: nowrap !important; z-index: 9999 !important; pointer-events: none !important; transform: translateX(-50%) !important; } .profile-icon-tooltip::after { content: ''; position: absolute !important; top: 100% !important; left: 50% !important; transform: translateX(-50%) !important; border: 5px solid transparent !important; border-top-color: #24292e !important; } .icon-wrapper:hover .profile-icon-tooltip { visibility: visible !important; } .fork-icon { width: 12px !important; height: 12px !important; } .fork-wrapper { margin-left: 8px !important; } .search-title { display: flex !important; align-items: flex-start !important; } .search-title .icon-wrapper { margin-left: 8px !important; display: inline-flex !important; align-items: center !important; margin-top: 3px !important; } `; document.head.appendChild(style); const ICONS = { user: "M224 256A128 128 0 1 0 224 0a128 128 0 1 0 0 256zm-45.7 48C79.8 304 0 383.8 0 482.3C0 498.7 13.3 512 29.7 512l388.6 0c16.4 0 29.7-13.3 29.7-29.7C448 383.8 368.2 304 269.7 304l-91.4 0z", organization: "M48 0C21.5 0 0 21.5 0 48L0 464c0 26.5 21.5 48 48 48l96 0 0-80c0-26.5 21.5-48 48-48s48 21.5 48 48l0 80 96 0c26.5 0 48-21.5 48-48l0-416c0-26.5-21.5-48-48-48L48 0zM64 240c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zm112-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zm80 16c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM80 96l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16zm80 16c0-8.8 7.2-16 16-16l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32zM272 96l32 0c8.8 0 16 7.2 16 16l0 32c0 8.8-7.2 16-16 16l-32 0c-8.8 0-16-7.2-16-16l0-32c0-8.8 7.2-16 16-16z" }; function getCachedUserType(username) { const cache = GM_getValue('userTypeCache', {}); const cachedData = cache[username]; if (cachedData) { const now = Date.now(); if (now - cachedData.timestamp < 24 * 60 * 60 * 1000) { return cachedData.type; } delete cache[username]; GM_setValue('userTypeCache', cache); } return null; } function cacheUserType(username, type) { const cache = GM_getValue('userTypeCache', {}); cache[username] = { type: type, timestamp: Date.now() }; GM_setValue('userTypeCache', cache); } async function checkUserType(username) { const cachedType = getCachedUserType(username); if (cachedType) { return cachedType; } try { const response = await fetch(`https://api.github.com/users/${username}`); if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`); } const data = await response.json(); const type = data.type?.toLowerCase() === 'organization' ? 'organization' : 'user'; cacheUserType(username, type); return type; } catch (error) { cacheUserType(username, 'user'); return 'user'; } } async function createIcon(username, wrapper, isFork = false) { const type = await checkUserType(username); const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("xmlns", "http://www.w3.org/2000/svg"); svg.setAttribute("viewBox", "0 0 448 512"); svg.style.cssText = `width:${isFork ? '12px' : '16px'};height:${isFork ? '12px' : '16px'};cursor:pointer;fill:currentColor;transition:transform .1s`; if (isFork) { svg.classList.add('fork-icon'); wrapper.classList.add('fork-wrapper'); } const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", ICONS[type]); const tooltip = document.createElement('div'); tooltip.className = 'profile-icon-tooltip'; tooltip.textContent = username; wrapper.addEventListener('mouseenter', (e) => { svg.style.transform = 'scale(1.1)'; const rect = wrapper.getBoundingClientRect(); tooltip.style.left = `${rect.left + (rect.width / 2)}px`; tooltip.style.top = `${rect.top - 35}px`; }); wrapper.addEventListener('mouseleave', () => { svg.style.transform = 'scale(1)'; }); wrapper.addEventListener('mousemove', (e) => { const rect = wrapper.getBoundingClientRect(); tooltip.style.left = `${rect.left + (rect.width / 2)}px`; tooltip.style.top = `${rect.top - 35}px`; }); wrapper.addEventListener('click', () => window.open(`https://github.com/${username}`, '_blank')); svg.appendChild(path); wrapper.appendChild(svg); wrapper.appendChild(tooltip); } async function addGitHubIcons() { const tasks = []; const isSearchPage = window.location.pathname === '/search' || window.location.pathname.startsWith('/search/'); if (isSearchPage) { document.querySelectorAll('.search-title').forEach(titleDiv => { if (titleDiv.querySelector('.icon-wrapper')) return; const link = titleDiv.querySelector('a'); if (!link) return; const href = link.getAttribute('href'); if (!href) return; const username = href.split('/').filter(Boolean)[0]; const wrapper = document.createElement('div'); wrapper.className = 'icon-wrapper'; titleDiv.appendChild(wrapper); tasks.push(createIcon(username, wrapper, false)); }); } else { document.querySelectorAll('h3:not(.search-title)').forEach(h3 => { if (h3.querySelector('.icon-wrapper')) return; const link = h3.querySelector('a'); if (!link) return; const href = link.getAttribute('href'); const username = href.split('/').filter(Boolean)[0]; const wrapper = document.createElement('div'); wrapper.className = 'icon-wrapper'; h3.appendChild(wrapper); tasks.push(createIcon(username, wrapper, false)); }); document.querySelectorAll('.f6.color-fg-muted.mb-1').forEach(forkInfo => { if (forkInfo.querySelector('.icon-wrapper')) return; const link = forkInfo.querySelector('a.Link--muted'); if (!link || !link.href.includes('/')) return; const username = link.getAttribute('href').split('/').filter(Boolean)[0]; const wrapper = document.createElement('div'); wrapper.className = 'icon-wrapper'; link.insertAdjacentElement('afterend', wrapper); tasks.push(createIcon(username, wrapper, true)); }); } await Promise.all(tasks); } addGitHubIcons(); const observer = new MutationObserver(mutations => { if (mutations.some(m => m.addedNodes.length)) addGitHubIcons(); }); observer.observe(document.body, { childList: true, subtree: true }); const pushState = history.pushState; const replaceState = history.replaceState; history.pushState = function() { pushState.apply(history, arguments); addGitHubIcons(); }; history.replaceState = function() { replaceState.apply(history, arguments); addGitHubIcons(); }; window.addEventListener('popstate', addGitHubIcons); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址