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.4// @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: #212830 !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: #212830 !important;}.icon-wrapper:hover .profile-icon-tooltip {visibility: visible !important;}.fork-icon {width: 10px !important;height: 10px !important;opacity: 1 !important;}.non-fork-icon {opacity: 0.575 !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: "M11.1,8.7c2.5,1.2,4.1,3.6,4.2,6.3c0,0.5-0.3,0.9-0.9,1c-0.5,0-0.9-0.3-1-0.9c0,0,0,0,0,0c-0.1-3.1-2.7-5.4-5.8-5.3c-2.9,0.1-5.1,2.4-5.3,5.3c0,0.5-0.5,0.9-1,0.9c-0.5,0-0.9-0.4-0.9-0.9c0.1-2.7,1.8-5.2,4.2-6.3C2.8,7,2.5,3.9,4.2,1.8s4.8-2.4,6.9-0.6s2.4,4.8,0.6,6.9C11.6,8.3,11.4,8.5,11.1,8.7z M11.1,4.9c0-1.7-1.4-3.1-3.1-3.1S4.9,3.2,4.9,4.9S6.3,8,8,8S11.1,6.6,11.1,4.9z",organization: "M1.75 16A1.75 1.75 0 0 1 0 14.25V1.75C0 .784.784 0 1.75 0h8.5C11.216 0 12 .784 12 1.75v12.5c0 .085-.006.168-.018.25h2.268a.25.25 0 0 0 .25-.25V8.285a.25.25 0 0 0-.111-.208l-1.055-.703a.749.749 0 1 1 .832-1.248l1.055.703c.487.325.779.871.779 1.456v5.965A1.75 1.75 0 0 1 14.25 16h-3.5a.766.766 0 0 1-.197-.026c-.099.017-.2.026-.303.026h-3a.75.75 0 0 1-.75-.75V14h-1v1.25a.75.75 0 0 1-.75.75Zm-.25-1.75c0 .138.112.25.25.25H4v-1.25a.75.75 0 0 1 .75-.75h2.5a.75.75 0 0 1 .75.75v1.25h2.25a.25.25 0 0 0 .25-.25V1.75a.25.25 0 0 0-.25-.25h-8.5a.25.25 0 0 0-.25.25ZM3.75 6h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 3.75A.75.75 0 0 1 3.75 3h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 3.75Zm4 3A.75.75 0 0 1 7.75 6h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 7 6.75ZM7.75 3h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5ZM3 9.75A.75.75 0 0 1 3.75 9h.5a.75.75 0 0 1 0 1.5h-.5A.75.75 0 0 1 3 9.75ZM7.75 9h.5a.75.75 0 0 1 0 1.5h-.5a.75.75 0 0 1 0-1.5Z"};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 16 16");svg.style.cssText = `width:${isFork ? '10px' : '14px'};height:${isFork ? '10px' : '14px'};cursor:pointer;fill:currentColor;transition:transform .1s`;if (isFork) {svg.classList.add('fork-icon');wrapper.classList.add('fork-wrapper');} else {svg.classList.add('non-fork-icon');}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 {const repoNav = document.querySelector('#repository-container-header');document.querySelectorAll('h3:not(.search-title)').forEach(h3 => {if (h3.closest('#readme') || h3.closest('article')) return;if (repoNav && !h3.closest('#repository-container-header')) return;if (h3.querySelector('.icon-wrapper')) return;const link = h3.querySelector('a');if (!link) return;const href = link.getAttribute('href');if (!href || !href.startsWith('/')) return;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);})();