// ==UserScript== // @name 替换网页字体 // @author ai // @version 2.46 // @match *://*/* // @run-at document-start // @early-start // @grant GM_addStyle // @grant GM_getValues // @grant GM_getValue // @grant GM_setValues // @grant GM_setValue // @grant GM_listValues // @grant GM_addValueChangeListener // @grant GM_registerMenuCommand // @grant GM_unregisterMenuCommand // @description 替换网页的字体 // ==/UserScript== (function() { 'use strict'; const DEFAULT_CONFIG = { enabled: true, font: '微软雅黑', exclusions: 'code' }; class FontManager { constructor() { this.config = GM_getValues(DEFAULT_CONFIG); this.menuCommands = new Map(); this.styleElement = GM_addStyle(''); this.init(); } init() { this.refreshStyles(); this.setupListeners(); if (window.top === window) this.updateMenu(); this.updateWebFonts(); } setupListeners() { GM_addValueChangeListener("enabled", (k, o, n) => { this.config.enabled = n; this.refreshStyles(); this.updateMenu(); }); GM_addValueChangeListener("font", (k, o, n) => { this.config.font = n; this.refreshStyles(); }); GM_addValueChangeListener("exclusions", (k, o, n) => { this.config.exclusions = n; this.refreshStyles(); }); } refreshStyles() { if (!this.config.enabled) { this.styleElement.textContent = ''; return; } const fontStack = this.webFonts ? `${this.config.font}, ${this.webFonts}` : this.config.font; const baseExclusions = 'i, [class*="ico"], [class*="fa-"], [class*="symbol"]'; const userExclusions = this.config.exclusions.trim(); const fullExclusions = userExclusions ? `${baseExclusions}, ${userExclusions}` : baseExclusions; this.styleElement.textContent = ` @scope (body) to (${fullExclusions}) { * { font-family: ${fontStack} !important; } *::before, *::after { font-family: ${this.webFonts} !important; } } `; } async updateWebFonts() { await document.fonts.ready; const fonts = new Set(); document.fonts.forEach(font => fonts.add(`"${font.family}"`)); if (fonts.size > 0) { this.webFonts = [...fonts].join(','); this.refreshStyles(); } } updateMenu() { if (window.top !== window) return; this.menuCommands.forEach(id => GM_unregisterMenuCommand(id)); const title = this.config.enabled ? '❌ 禁用字体替换' : '✅ 启用字体替换'; this.registerMenu('enabled', title, () => GM_setValue("enabled", !this.config.enabled)); this.registerMenu('font', '🖋️ 配置字体名称', () => this.promptConfig('font', '字体名称(如: "Inter", "Microsoft YaHei")')); this.registerMenu('exclusions', '🚫 配置排除元素', () => this.promptConfig('exclusions', 'CSS 选择器')); this.registerMenu('reset', '🗑️ 恢复默认配置', () => confirm('确定恢复吗?') && this.reset()); } registerMenu(key, label, cb) { this.menuCommands.set(key, GM_registerMenuCommand(label, cb)); } promptConfig(key, title) { const res = prompt(`请输入${title}`, this.config[key]); if (res !== null) GM_setValue(key, res); } reset() { if (confirm('确定要将所有配置恢复为默认值吗?')) GM_setValues(DEFAULT_CONFIG); } } new FontManager(); })();