🏠 Home 

GitHub Code Language Icons

Replaces GitHub's boring round code language icons with Material Design Icons.

// ==UserScript==
// @name         GitHub Code Language Icons
// @description  Replaces GitHub's boring round code language icons with Material Design Icons.
// @icon         https://github.githubassets.com/favicons/favicon-dark.svg
// @version      1.9
// @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_setValue
// @grant        GM_getValue
// ==/UserScript==
(function() {
'use strict';
const CONFIG = {
case_01: true, // The average of all icons
case_02: true, // Organization's Repositories (https://github.com/orgs/yt-dlp/repositories)
case_03: true, // Languages
case_04: true, // Other Languages
case_05: true, // Filter by (https://github.com/search?q=spotify&type=code)
case_06: true, // More languages... (https://github.com/search?q=spotify&type=repositories)
case_07: true  // Search's Languages (https://github.com/search?q=spotify&type=repositories)
};
const BASE_URL = 'https://raw.githubusercontent.com/afkarxyz/misc-scripts/refs/heads/main/';
const ICON_BASE_URL = `${BASE_URL}icons/`;
let languageMappings = {};
async function fetchLanguageMappings() {
const cacheKey = 'githubLanguageRemapCache';
const currentTime = Date.now();
const cachedData = JSON.parse(GM_getValue(cacheKey, '{}'));
if (cachedData.timestamp && (currentTime - cachedData.timestamp < 7 * 24 * 60 * 60 * 1000)) {
return cachedData.mappings;
}
try {
const response = await fetch(`${BASE_URL}remap.json`);
const data = await response.json();
GM_setValue(cacheKey, JSON.stringify({
mappings: data.iconRemap,
timestamp: currentTime
}));
return data.iconRemap;
} catch (error) {
console.error('Failed to fetch language mappings:', error);
return cachedData.mappings || {};
}
}
function normalizeLanguageName(language) {
const normalizedLanguage = language.toLowerCase();
for (const [iconName, languageList] of Object.entries(languageMappings)) {
if (languageList.includes(normalizedLanguage)) {
return iconName;
}
}
return normalizedLanguage;
}
async function fetchAvailableIcons() {
const cacheKey = 'githubLanguageIconsCache';
const currentTime = Date.now();
const cachedData = JSON.parse(GM_getValue(cacheKey, '{}'));
if (cachedData.timestamp && (currentTime - cachedData.timestamp < 7 * 24 * 60 * 60 * 1000)) {
return cachedData.fileTypes;
}
try {
const response = await fetch(`${BASE_URL}icons.json`);
const data = await response.json();
GM_setValue(cacheKey, JSON.stringify({
fileTypes: data.fileTypes,
timestamp: currentTime
}));
return data.fileTypes;
} catch (error) {
console.error('Failed to fetch icon list:', error);
return cachedData.fileTypes || [];
}
}
async function replaceLanguageIcons() {
let availableIcons;
try {
availableIcons = await fetchAvailableIcons();
} catch (error) {
console.error('Error getting available icons:', error);
return;
}
const processedElements = new Set();
async function replaceOtherIcon(targetSvg) {
if (!CONFIG.case_04) return;
try {
const response = await fetch(`${ICON_BASE_URL}other.svg`);
const svgText = await response.text();
const parser = new DOMParser();
const svgDoc = parser.parseFromString(svgText, 'image/svg+xml');
const newSvg = svgDoc.querySelector('svg');
const attributes = targetSvg.attributes;
for (let i = 0; i < attributes.length; i++) {
const attr = attributes[i];
if (attr.name !== 'class') {
newSvg.setAttribute(attr.name, attr.value);
}
}
newSvg.setAttribute('width', '16');
newSvg.setAttribute('height', '16');
newSvg.setAttribute('viewBox', '0 0 24 24');
const innerGroup = newSvg.querySelector('g') || newSvg.querySelector('path');
if (innerGroup) {
innerGroup.setAttribute('transform', 'scale(0.67)');
}
const targetClasses = Array.from(targetSvg.classList);
targetClasses.forEach(className => {
newSvg.classList.add(className);
});
newSvg.style.width = '16px';
newSvg.style.height = '16px';
newSvg.style.minWidth = '16px';
newSvg.style.minHeight = '16px';
targetSvg.parentNode.replaceChild(newSvg, targetSvg);
} catch (error) {
console.error('Failed to replace Other icon:', error);
}
}
function processElement(element) {
if (processedElements.has(element)) return;
processedElements.add(element);
let langElement, language;
// Case 1: The average of all icons
if (CONFIG.case_01 && element.matches('.repo-language-color')) {
const languageSpan = element.parentElement.querySelector('[itemprop="programmingLanguage"]');
if (languageSpan && !languageSpan.dataset.iconProcessed) {
language = normalizeLanguageName(languageSpan.textContent);
if (availableIcons.includes(language)) {
const iconImg = document.createElement('img');
iconImg.src = `${ICON_BASE_URL}${language}.svg`;
iconImg.alt = `${language} icon`;
iconImg.width = 16;
iconImg.height = 16;
iconImg.style.marginRight = '2px';
iconImg.style.verticalAlign = 'sub';
element.parentElement.insertBefore(iconImg, element);
element.remove();
languageSpan.dataset.iconProcessed = 'true';
}
}
}
// Case 2: Organization's Repositories
else if (CONFIG.case_02 && element.matches('.Box-sc-g0xbh4-0.fCvgBf')) {
const languageSpan = element.querySelector('.prc-Text-Text-0ima0');
if (languageSpan && !languageSpan.dataset.iconProcessed) {
const languageText = languageSpan.textContent.trim();
language = normalizeLanguageName(languageText);
element.innerHTML = '';
const newLanguageSpan = document.createElement('span');
newLanguageSpan.className = 'Box-sc-g0xbh4-0 fVplbS prc-Text-Text-0ima0';
newLanguageSpan.textContent = languageText;
if (availableIcons.includes(language)) {
const iconImg = document.createElement('img');
iconImg.src = `${ICON_BASE_URL}${language}.svg`;
iconImg.alt = `${language} icon`;
iconImg.width = 16;
iconImg.height = 16;
iconImg.style.verticalAlign = 'middle';
element.appendChild(iconImg);
}
element.appendChild(newLanguageSpan);
newLanguageSpan.dataset.iconProcessed = 'true';
}
}
// Case 3: Languages
else if (CONFIG.case_03 && element.matches('.d-inline')) {
langElement = element.querySelector('.text-bold');
if (!langElement || langElement.textContent.toLowerCase() === 'other' || element.dataset.iconChecked) return;
language = normalizeLanguageName(langElement.textContent);
element.dataset.iconChecked = 'true';
const svg = element.querySelector('svg');
if (!svg || !availableIcons.includes(language)) return;
const img = document.createElement('img');
img.src = `${ICON_BASE_URL}${language}.svg`;
img.width = 16;
img.height = 16;
img.className = 'mr-2';
img.style.verticalAlign = 'middle';
svg.parentNode.replaceChild(img, svg);
}
// Case 4: Other Languages
else if (CONFIG.case_04 && element.matches('.octicon-dot-fill')) {
const parentElement = element.closest('.d-inline');
if (parentElement) {
const languageElement = parentElement.querySelector('.text-bold');
if (languageElement && languageElement.textContent.toLowerCase() === 'other' && !element.dataset.iconProcessed) {
element.dataset.iconProcessed = 'true';
replaceOtherIcon(element);
}
}
}
// Case 5: Filter by
else if (CONFIG.case_05 && element.matches('.Box-sc-g0xbh4-0.hjDqIa')) {
const languageSpan = element.nextElementSibling;
if (languageSpan && languageSpan.getAttribute('aria-label') && !languageSpan.dataset.iconProcessed) {
language = normalizeLanguageName(languageSpan.getAttribute('aria-label').replace(' language', ''));
if (availableIcons.includes(language)) {
const iconImg = document.createElement('img');
iconImg.src = `${ICON_BASE_URL}${language}.svg`;
iconImg.alt = `${language} icon`;
iconImg.width = 16;
iconImg.height = 16;
iconImg.style.marginRight = '4px';
iconImg.style.verticalAlign = 'middle';
element.style.display = 'none';
languageSpan.parentNode.insertBefore(iconImg, languageSpan);
languageSpan.dataset.iconProcessed = 'true';
}
}
}
// Case 6: More languages...
else if (CONFIG.case_06 && element.matches('.ActionListItem-visual.ActionListItem-visual--leading')) {
const languageLabel = element.closest('.ActionListContent')?.querySelector('.ActionListItem-label.text-normal');
if (!languageLabel || !languageLabel.textContent || element.dataset.iconChecked) return;
language = normalizeLanguageName(languageLabel.textContent);
element.dataset.iconChecked = 'true';
const colorDiv = element.querySelector('div');
if (!colorDiv || !availableIcons.includes(language)) return;
const img = document.createElement('img');
img.src = `${ICON_BASE_URL}${language}.svg`;
img.width = 16;
img.height = 16;
img.style.verticalAlign = 'middle';
colorDiv.replaceWith(img);
}
// Case 7: Search's Languages
else if (CONFIG.case_07 && element.matches('.prc-ActionList-LeadingVisual-dxXxW, .prc-ActionList-VisualWrap-rfjV-')) {
if (element.dataset.iconChecked) return;
element.dataset.iconChecked = 'true';
const listItem = element.closest('.prc-ActionList-ActionListItem-uq6I7');
if (!listItem) return;
const languageDiv = listItem.querySelector('.Truncate__StyledTruncate-sc-23o1d2-0');
if (!languageDiv || !languageDiv.title) return;
language = normalizeLanguageName(languageDiv.title);
const colorDiv = element.querySelector('.Box-sc-g0xbh4-0');
if (!colorDiv || !availableIcons.includes(language)) return;
const img = document.createElement('img');
img.src = `${ICON_BASE_URL}${language}.svg`;
img.alt = `${language} icon`;
img.width = 16;
img.height = 16;
img.style.verticalAlign = 'middle';
colorDiv.replaceWith(img);
}
}
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches('.d-inline, .Box-sc-g0xbh4-0.fCvgBf, .repo-language-color, .Box-sc-g0xbh4-0.hjDqIa, .ActionListItem-visual.ActionListItem-visual--leading, .octicon-dot-fill, .prc-ActionList-LeadingVisual-dxXxW, .prc-ActionList-VisualWrap-rfjV-')) {
processElement(node);
} else {
node.querySelectorAll('.d-inline, .Box-sc-g0xbh4-0.fCvgBf, .repo-language-color, .Box-sc-g0xbh4-0.hjDqIa, .ActionListItem-visual.ActionListItem-visual--leading, .octicon-dot-fill, .prc-ActionList-LeadingVisual-dxXxW, .prc-ActionList-VisualWrap-rfjV-').forEach(processElement);
}
}
}
}
});
observer.observe(document.body, {
childList: true,
subtree: true
});
document.querySelectorAll('.d-inline, .Box-sc-g0xbh4-0.fCvgBf, .repo-language-color, .Box-sc-g0xbh4-0.hjDqIa, .ActionListItem-visual.ActionListItem-visual--leading, .octicon-dot-fill, .prc-ActionList-LeadingVisual-dxXxW, .prc-ActionList-VisualWrap-rfjV-').forEach(processElement);
}
async function init() {
languageMappings = await fetchLanguageMappings();
replaceLanguageIcons();
}
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', init);
} else {
init();
}
})();