您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Replaces GitHub's code language icons with Material Design Icons.
当前为
- // ==UserScript==
- // @name GitHub Code Language Icons
- // @description Replaces GitHub's code language icons with Material Design Icons.
- // @icon https://github.githubassets.com/favicons/favicon-dark.svg
- // @version 1.2
- // @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 BASE_URL = 'https://raw.githubusercontent.com/afkarxyz/misc-scripts/refs/heads/main/';
- const ICON_BASE_URL = `${BASE_URL}icons/`;
- 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 || {};
- }
- }
- let languageMappings = {};
- 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();
- function processElement(element) {
- if (processedElements.has(element)) return;
- processedElements.add(element);
- let langElement, language;
- if (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);
- }
- else if (element.matches('.f6.color-fg-muted .repo-language-color + span[itemprop="programmingLanguage"]')) {
- language = normalizeLanguageName(element.textContent);
- if (!availableIcons.includes(language)) return;
- const parentSpan = element.parentElement;
- const colorSpan = parentSpan.querySelector('.repo-language-color');
- const img = document.createElement('img');
- img.src = `${ICON_BASE_URL}${language}.svg`;
- img.width = 16;
- img.height = 16;
- img.style.marginRight = '2px';
- img.style.verticalAlign = 'sub';
- if (colorSpan) {
- colorSpan.parentNode.insertBefore(img, colorSpan);
- colorSpan.remove();
- }
- }
- else if (element.matches('.mb-3 .no-wrap span[itemprop="programmingLanguage"]')) {
- language = normalizeLanguageName(element.textContent);
- if (!availableIcons.includes(language)) return;
- const parentSpan = element.parentElement;
- const colorSpan = parentSpan.querySelector('.repo-language-color');
- const img = document.createElement('img');
- img.src = `${ICON_BASE_URL}${language}.svg`;
- img.width = 16;
- img.height = 16;
- img.style.marginRight = '4px';
- const flexContainer = document.createElement('span');
- flexContainer.style.display = 'inline-flex';
- flexContainer.style.alignItems = 'center';
- if (colorSpan) {
- colorSpan.remove();
- flexContainer.appendChild(img);
- flexContainer.appendChild(element.cloneNode(true));
- parentSpan.replaceWith(flexContainer);
- }
- }
- else if (element.matches('.Box-sc-g0xbh4-0.fCvgBf')) {
- const preLanguageElement = element.querySelector('.Box-sc-g0xbh4-0:not(.fVplbS)');
- if (preLanguageElement) {
- preLanguageElement.style.display = 'none';
- }
- const languageSpan = element.querySelector('.Box-sc-g0xbh4-0.fVplbS');
- if (languageSpan && !languageSpan.dataset.iconProcessed) {
- language = normalizeLanguageName(languageSpan.textContent.trim());
- 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 = 'middle';
- languageSpan.insertAdjacentElement('beforebegin', iconImg);
- languageSpan.dataset.iconProcessed = 'true';
- }
- }
- }
- else if (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';
- }
- }
- }
- else if (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';
- }
- }
- }
- }
- 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, .f6.color-fg-muted .repo-language-color + span[itemprop="programmingLanguage"], .mb-3 .no-wrap span[itemprop="programmingLanguage"], .Box-sc-g0xbh4-0.fCvgBf, .repo-language-color, .Box-sc-g0xbh4-0.hjDqIa')) {
- processElement(node);
- } else {
- node.querySelectorAll('.d-inline, .f6.color-fg-muted .repo-language-color + span[itemprop="programmingLanguage"], .mb-3 .no-wrap span[itemprop="programmingLanguage"], .Box-sc-g0xbh4-0.fCvgBf, .repo-language-color, .Box-sc-g0xbh4-0.hjDqIa').forEach(processElement);
- }
- }
- }
- }
- });
- observer.observe(document.body, {
- childList: true,
- subtree: true
- });
- document.querySelectorAll('.d-inline, .f6.color-fg-muted .repo-language-color + span[itemprop="programmingLanguage"], .mb-3 .no-wrap span[itemprop="programmingLanguage"], .Box-sc-g0xbh4-0.fCvgBf, .repo-language-color, .Box-sc-g0xbh4-0.hjDqIa').forEach(processElement);
- }
- function hideAndReplaceDivs() {
- document.querySelectorAll('span.Box-sc-g0xbh4-0.fCBLcl').forEach(spanElement => {
- if (!spanElement) return;
- const divToHide = spanElement.querySelector('div[class^="Box-sc-g0xbh4-0"]');
- const titleElement = spanElement.closest('a')?.querySelector('div[title]');
- const title = titleElement?.getAttribute('title');
- if (divToHide && title && !divToHide.dataset.processed) {
- const normalizedTitle = normalizeLanguageName(title);
- divToHide.style.display = 'none';
- divToHide.dataset.processed = 'true';
- const svgElement = document.createElement('img');
- svgElement.src = `${ICON_BASE_URL}${normalizedTitle}.svg`;
- svgElement.alt = title;
- svgElement.width = svgElement.height = 16;
- divToHide.parentNode.insertBefore(svgElement, divToHide);
- }
- });
- }
- function observeDOMChanges() {
- const observer = new MutationObserver((mutations) => {
- mutations.forEach((mutation) => {
- if (mutation.type === 'childList') {
- hideAndReplaceDivs();
- }
- });
- });
- const config = { childList: true, subtree: true };
- observer.observe(document.body, config);
- hideAndReplaceDivs();
- }
- async function init() {
- languageMappings = await fetchLanguageMappings();
- replaceLanguageIcons();
- observeDOMChanges();
- }
- if (document.readyState === 'loading') {
- document.addEventListener('DOMContentLoaded', init);
- } else {
- init();
- }
- })();