全自动高亮最符合你系统架构的 GitHub Release
// ==UserScript==
// @name GitHub Release Highlighter
// @namespace http://longlone.top/
// @version 1.0
// @description 全自动高亮最符合你系统架构的 GitHub Release
// @author Longlone & Gemini
// @license MIT
// @match https://github.com/*/*/releases*
// @icon https://github.githubassets.com/favicons/favicon.svg
// @run-at document-end
// ==/UserScript==
(function() {
'use strict';
// --- 1. macos 硬件探测 ---
function detectArchitecture(os) {
if (os !== 'darwin') {
const ua = navigator.userAgent.toLowerCase();
if (ua.includes('aarch64') || ua.includes('arm64')) return 'arm64';
return 'amd64';
}
try {
const canvas = document.createElement('canvas');
const gl = canvas.getContext('webgl') || canvas.getContext('experimental-webgl');
if (gl) {
const debugInfo = gl.getExtension('WEBGL_debug_renderer_info');
if (debugInfo) {
const renderer = gl.getParameter(debugInfo.UNMASKED_RENDERER_WEBGL).toLowerCase();
if (renderer.includes('apple') || renderer.includes('m1') || renderer.includes('m2') || renderer.includes('m3')) {
return 'arm64';
}
}
}
} catch (e) {}
return 'amd64';
}
function getSystemProfile() {
const ua = navigator.userAgent.toLowerCase();
let os = '';
if (ua.includes('win')) os = 'windows';
else if (ua.includes('mac')) os = 'darwin';
else if (ua.includes('linux') || ua.includes('x11')) os = 'linux';
const arch = detectArchitecture(os);
return { os, arch };
}
// --- 2. 评分逻辑 ---
function calculateScore(fileName, profile) {
if (!fileName) return -1;
const lowerName = fileName.toLowerCase();
const { os, arch } = profile;
// [A] 绝对忽略列表
const IGNORED_EXTS = ['.sig', '.asc', '.sha256', '.sha1', '.md5', '.sbom', '.pem', '.key', '.blockmap'];
if (IGNORED_EXTS.some(ext => lowerName.endsWith(ext))) return -1;
// [B] 后缀判断
const UNIVERSAL_EXTS = ['.zip', '.tar.gz', '.tgz', '.7z', '.gz', '.xz', '.rar'];
const OS_SPECIFIC_EXTS = {
darwin: ['.dmg', '.app', '.pkg'],
windows: ['.exe', '.msi'],
linux: ['.appimage', '.deb', '.rpm', '.run', '.sh']
};
const isUniversal = UNIVERSAL_EXTS.some(ext => lowerName.endsWith(ext));
const isOsSpecific = OS_SPECIFIC_EXTS[os] && OS_SPECIFIC_EXTS[os].some(ext => lowerName.endsWith(ext));
if (!isUniversal && !isOsSpecific) return -1;
// [C] 互斥关键词
const CONFLICT_KEYWORDS = {
darwin: ['windows', 'win64', 'win32', 'linux', 'ubuntu', 'debian'],
windows: ['macos', 'darwin', 'osx', 'linux', 'ubuntu', 'debian'],
linux: ['windows', 'win64', 'win32', 'macos', 'darwin', 'osx']
};
if (CONFLICT_KEYWORDS[os] && CONFLICT_KEYWORDS[os].some(k => lowerName.includes(k))) {
return -1;
}
// [D] 架构匹配
const ARCH_KEYWORDS = {
arm64: ['aarch64', 'arm64', 'armv8', 'm1', 'm2', 'm3', 'apple silicon'],
amd64: ['x86_64', 'amd64', 'x64', 'win64', 'intel'],
x86: ['x86', 'i386', 'i686', 'win32', '32-bit']
};
const isArm64File = ARCH_KEYWORDS.arm64.some(k => lowerName.includes(k));
const isAmd64File = ARCH_KEYWORDS.amd64.some(k => lowerName.includes(k));
const isX86File = ARCH_KEYWORDS.x86.some(k => lowerName.includes(k) && !lowerName.includes('x86_64'));
// [E] 打分
let baseScore = isOsSpecific ? 20 : 10;
if (arch === 'arm64') {
if (isArm64File) return baseScore + 80;
if (isAmd64File) return baseScore + 40;
if (isX86File) return -1;
return baseScore;
} else if (arch === 'amd64') {
if (isAmd64File) return baseScore + 80;
if (isX86File) return baseScore + 40;
if (isArm64File) return -1;
return baseScore;
}
return 0;
}
// --- 3. 渲染主循环 ---
function processAssets() {
const container = document.querySelector('#repo-content-turbo-frame') || document.body;
const allAssetRows = Array.from(container.querySelectorAll('.Box-row'));
if (allAssetRows.length === 0) return;
const profile = getSystemProfile();
const assetGroups = new Set(allAssetRows.map(row => row.parentElement).filter(el => el));
assetGroups.forEach(group => {
const rows = Array.from(group.querySelectorAll('.Box-row'));
let fileData = [];
rows.forEach(row => {
let fileName = '';
const linkEl = row.querySelector('a span.truncate-start') || row.querySelector('a[href*="/download/"], a[href*="/archive/"]');
if (linkEl) fileName = linkEl.textContent.trim();
if (!fileName) return;
if (fileName.toLowerCase().includes('source code')) return;
const score = calculateScore(fileName, profile);
const linkTarget = row.querySelector('a span.truncate-start') || row.querySelector('a');
if (linkTarget) {
linkTarget.style.color = '';
linkTarget.style.fontWeight = '';
linkTarget.style.textDecoration = '';
}
if (score > 0) {
fileData.push({ row, score, fileName, linkTarget });
}
});
if (fileData.length === 0) return;
const maxScore = Math.max(...fileData.map(d => d.score));
fileData.forEach(item => {
// 只有最高分才高亮
if (item.score === maxScore && item.linkTarget) {
item.linkTarget.style.setProperty('color', '#d29922', 'important'); // GitHub 风格的金黄色
item.linkTarget.style.setProperty('font-weight', 'bold', 'important');
}
});
});
}
let timer = null;
const run = () => {
if (timer) clearTimeout(timer);
timer = setTimeout(processAssets, 500);
};
function init() {
run();
// 仅保留异步加载必须的监听
document.addEventListener('turbo:render', run); // GitHub 页面切换
document.addEventListener('load', (e) => {
if (e.target.tagName === 'INCLUDE-FRAGMENT') run(); // Assets 列表延迟加载
}, true);
const observer = new MutationObserver((mutations) => {
if (mutations.some(m => m.addedNodes.length > 0)) run();
});
observer.observe(document.body, { childList: true, subtree: true });
}
init();
})();