// ==UserScript== // @name 网页字体替换增强器 // @namespace chNt6w8D6cVSQE93BSC8VS6QxNshGaSP9QcK82kruzbN5E4K2TJKxbNjpAXDfJKe // @description 替换网页默认字体,默认为 MiSans、FZYouSong GBK 509R(方正悠宋)、Cascadia Mono。可根据需求修改脚本中的字体设置。使用前需确保已安装所需字体。 // @version 7 // @license Apache License 2.0 // @author Anonymous // @compatible firefox // @compatible safari // @compatible chrome // @match *://*/* // @grant GM_addStyle // @grant GM_getValue // @grant GM_setValue // @grant GM_registerMenuCommand // @run-at document-start // @downloadURL none // ==/UserScript== (function () { "use strict"; const defaultConfig = { sans_namespace: ["MiSans", "MiSans L3", ""], sans_latin: ["MiSans", "MiSans L3", ""], sans_simplified: ["MiSans", "MiSans L3", ""], sans_traditional: ["MiSans", "MiSans L3", ""], serif_namespace: ["FZYouSong GBK 509R", "Times New Roman", ""], serif_simplified: ["FZYouSong GBK 509R", "Times New Roman", ""], serif_traditional: ["FZYouSong GBK 509R", "Times New Roman", ""], mono_namespace: ["Cascadia Mono", "Arial", ""], mono: ["Cascadia Mono", "Arial", ""], }; function loadConfig() { return { sans_namespace: GM_getValue("sans_namespace", defaultConfig.sans_namespace), sans_latin: GM_getValue("sans_latin", defaultConfig.sans_latin), serif_namespace: GM_getValue("serif_namespace", defaultConfig.serif_namespace), sans_simplified: GM_getValue("sans_simplified", defaultConfig.sans_simplified), sans_traditional: GM_getValue("sans_traditional", defaultConfig.sans_traditional), serif_simplified: GM_getValue("serif_simplified", defaultConfig.serif_simplified), serif_traditional: GM_getValue("serif_traditional", defaultConfig.serif_traditional), mono_namespace: GM_getValue("mono_namespace", defaultConfig.mono_namespace), mono: GM_getValue("mono", defaultConfig.mono), }; } function saveConfig() { Object.keys(config).forEach(key => { GM_setValue(key, config[key]); }); } function createConfigUI() { const overlay = document.createElement("div"); overlay.style = ` position: fixed; inset: 0; background: rgba(255, 255, 255, 0.1); backdrop-filter: blur(5px); /* 高斯模糊 */ display: grid; place-items: center; z-index: 999999; transition: opacity 0.3s; `; const panel = document.createElement("div"); panel.style = ` background: #ffffff; /* 白色背景 */ border-radius: 16px; width: min(95vw, 800px); /* 确保最大宽度不超过屏幕宽度 */ max-height: 90vh; overflow-y: auto; /* 垂直滚动条 */ padding: 2rem; box-shadow: 0 10px 25px rgba(0,0,0,0.1); /* 柔和阴影 */ color: #333333; /* 文字颜色 */ font-family: MiSans, sans-serif; animation: slideIn 0.3s ease; `; const title = document.createElement("h2"); title.textContent = "字体替换增强器 · 配置面板"; title.style = ` margin: 0 0 1.5rem; font-size: 1.5rem; text-align: center; color: #333333; /* 深灰色标题 */ position: relative; padding-bottom: 0.5rem; `; const titleUnderline = document.createElement("div"); titleUnderline.style = ` position: absolute; bottom: 0; left: 50%; transform: translateX(-50%); width: 60px; height: 3px; background: linear-gradient(90deg, #4f46e5, #ec4899); border-radius: 1px; `; title.appendChild(titleUnderline); const createInputGroup = (type, label) => { const container = document.createElement("div"); container.style.marginBottom = "1.5rem"; const titleLabel = document.createElement("label"); titleLabel.textContent = label; titleLabel.style = ` display: block; margin-bottom: 0.75rem; font-weight: 600; color: #333333; /* 深灰色标签 */ font-size: 1rem; position: relative; padding-left: 1.25rem; `; titleLabel.innerHTML += ` `; const inputContainer = document.createElement("div"); inputContainer.style = ` display: grid; grid-template-columns: repeat(3, calc(33.33% - 10px)); /* 每个输入框占三分之一宽度,减去间距 */ gap: 10px; /* 输入框之间的间距 */ `; for (let i = 0; i < 3; i++) { const input = document.createElement("input"); input.type = "text"; input.value = config[type][i]; input.dataset.type = type; input.dataset.index = i; input.placeholder = `备选字体 ${i + 1}`; input.style = ` width: 100%; /* 输入框占满网格单元 */ padding: 0.75rem 1rem; border: 1px solid #cccccc; border-radius: 8px; font-size: 0.75rem; color: #333333; /* 深灰色文字 */ background: #f9f9f9; /* 浅灰色背景 */ transition: all 0.2s; `; input.addEventListener('focus', () => { input.style.borderColor = '#4f46e5'; input.style.boxShadow = '0 0 0 2px rgba(79,70,229,0.2)'; }); input.addEventListener('blur', () => { input.style.borderColor = '#cccccc'; input.style.boxShadow = 'none'; }); inputContainer.appendChild(input); } container.appendChild(titleLabel); container.appendChild(inputContainer); return container; }; const createButton = (text, bgColor, hoverColor) => { const btn = document.createElement("button"); btn.textContent = text; btn.style = ` width: 100%; padding: 0.75rem 1rem; background: ${bgColor}; border: none; border-radius: 8px; color: white; font-weight: 600; cursor: pointer; transition: all 0.2s; margin: 0.5rem 0; `; btn.addEventListener('mouseover', () => btn.style.backgroundColor = hoverColor); btn.addEventListener('mouseout', () => btn.style.backgroundColor = bgColor); return btn; }; const importExportSection = document.createElement("div"); importExportSection.style.margin = "1.5rem 0"; const buttonContainer = document.createElement("div"); buttonContainer.style.display = "flex"; buttonContainer.style.gap = "0.75rem"; const importInput = document.createElement("input"); importInput.type = "file"; importInput.accept = ".json"; importInput.style.display = "none"; const importBtn = createButton("导入配置", "#4f46e5", "#4338ca"); importBtn.onclick = () => importInput.click(); importInput.onchange = async () => { try { const file = importInput.files[0]; const content = await file.text(); const importedConfig = JSON.parse(content); Object.assign(config, importedConfig); saveConfig(); alert("配置已导入,请刷新页面生效"); overlay.remove(); } catch (e) { alert("导入失败:无效的配置文件"); } }; const exportBtn = createButton("导出配置", "#16a34a", "#15803d"); exportBtn.onclick = () => { const blob = new Blob([JSON.stringify(config, null, 2)], { type: "application/json", }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.href = url; a.download = "font-config.json"; a.click(); URL.revokeObjectURL(url); }; const saveBtn = createButton("保存配置", "#ec4899", "#be185d"); saveBtn.style.marginTop = "1rem"; saveBtn.onclick = () => { const inputs = panel.querySelectorAll("input"); inputs.forEach(input => { config[input.dataset.type][input.dataset.index] = input.value; }); saveConfig(); alert("配置已保存,请刷新页面生效"); overlay.remove(); }; const resetBtn = createButton("重置默认", "#f43f5e", "#e11d48"); resetBtn.style.marginTop = "0.5rem"; resetBtn.onclick = () => { if (confirm("确定要重置所有配置为默认值吗?")) { Object.keys(defaultConfig).forEach(key => { config[key] = [...defaultConfig[key]]; }); saveConfig(); overlay.remove(); alert("配置已重置,请刷新页面生效"); } }; const closeBtn = document.createElement("button"); closeBtn.innerHTML = "×"; closeBtn.style = ` position: absolute; top: 1rem; right: 1rem; background: none; border: none; color: #64748b; font-size: 1.5rem; cursor: pointer; transition: color 0.2s; `; closeBtn.addEventListener('click', () => overlay.remove()); closeBtn.addEventListener('mouseover', () => closeBtn.style.color = '#f43f5e'); closeBtn.addEventListener('mouseout', () => closeBtn.style.color = '#64748b'); panel.appendChild(title); panel.appendChild(createInputGroup("sans_namespace", "无衬线 浏览器命名空间")); panel.appendChild(createInputGroup("sans_latin", "无衬线 拉丁文")); panel.appendChild(createInputGroup("sans_simplified", "无衬线 简化字形")); panel.appendChild(createInputGroup("sans_traditional", "无衬线 传统字形")); panel.appendChild(createInputGroup("serif_namespace", "衬线 浏览器命名空间")); panel.appendChild(createInputGroup("serif_simplified", "衬线 简化字形")); panel.appendChild(createInputGroup("serif_traditional", "衬线 传统字形")); panel.appendChild(createInputGroup("mono_namespace", "等宽字体 浏览器命名空间")); panel.appendChild(createInputGroup("mono", "等宽字体")); buttonContainer.appendChild(importBtn); buttonContainer.appendChild(exportBtn); importExportSection.appendChild(buttonContainer); panel.appendChild(importExportSection); panel.appendChild(saveBtn); panel.appendChild(resetBtn); panel.appendChild(closeBtn); overlay.appendChild(panel); document.body.appendChild(overlay); } function generateCSS() { const [sans_namespace_1, sans_namespace_2, sans_namespace_3] = config.sans_namespace; const [sans_latin_1, sans_latin_2, sans_latin_3] = config.sans_latin; const [sans_simplified_1, sans_simplified_2, sans_simplified_3] = config.sans_simplified; const [sans_traditional_1, sans_traditional_2, sans_traditional_3] = config.sans_traditional; const [serif_namespace_1, serif_namespace_2, serif_namespace_3] = config.serif_namespace; const [serif_simplified_1, serif_simplified_2, serif_simplified_3] = config.serif_simplified; const [serif_traditional_1, serif_traditional_2, serif_traditional_3] = config.serif_traditional; const [mono_namespace_1, mono_namespace_2, mono_namespace_3] = config.mono_namespace; const [mono_1, mono_2, mono_3] = config.mono; return ` /* 浏览器命名空间 */ @font-face{font-family:sans-serif;src:local(${sans_namespace_1}),local(${sans_namespace_2}),local(${sans_namespace_3});} @font-face{font-family:serif;src:local(${serif_namespace_1}),local(${serif_namespace_2}),local(${serif_namespace_3});} @font-face{font-family:monospace;src:local(${mono_namespace_1}),local(${mono_namespace_2}),local(${mono_namespace_3});} /* 无衬线字体 拉丁文 */ @font-face{font-family:"Arial";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Cambria";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Calibri";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Verdana";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Helvetica";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Helvetica Neue";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"HelveticaNeue";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"San Francisco";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"San Francisco Pro";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Segoe UI";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Google Sans";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Google Sans Text";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Roboto";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Noto Sans";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Lucida Sans";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Lucida Grande";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"DejaVu Sans";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Liberation Sans";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} @font-face{font-family:"Open Sans";src:local(${sans_latin_1}),local(${sans_latin_2}),local(${sans_latin_3});} /* 无衬线字体 简化字形 */ @font-face{font-family:"HarmonyOS Sans";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"Noto Sans SC";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"SimHei";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"黑体";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"Microsoft YaHei";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"微软雅黑";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"Microsoft YaHei UI";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"微软雅黑 UI";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"PingFang SC";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"Hiragino Sans GB";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} @font-face{font-family:"STHeiti";src:local(${sans_simplified_1}),local(${sans_simplified_2}),local(${sans_simplified_3});} /* 无衬线字体 传统字形 */ @font-face{font-family:"Noto Sans TC";src:local(${sans_traditional_1}),local(${sans_traditional_2}),local(${sans_traditional_3});} @font-face{font-family:"Microsoft JhengHei";src:local(${sans_traditional_1}),local(${sans_traditional_2}),local(${sans_traditional_3});} @font-face{font-family:"微軟正黑體";src:local(${sans_traditional_1}),local(${sans_traditional_2}),local(${sans_traditional_3});} @font-face{font-family:"Microsoft JhengHei UI";src:local(${sans_traditional_1}),local(${sans_traditional_2}),local(${sans_traditional_3});} @font-face{font-family:"MHei";src:local(${sans_traditional_1}),local(${sans_traditional_2}),local(${sans_traditional_3});} /* 衬线字体 简化字形 */ @font-face{font-family:"SimSun";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"宋体";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"NSimSun";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"新宋体";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"FangSong";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"FangSong_GB2312";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"仿宋";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"STSong";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"STFangsong";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} @font-face{font-family:"STZhongsong";src:local(${serif_simplified_1}),local(${serif_simplified_2}),local(${serif_simplified_3});} /* 等宽字体(代码) */ @font-face{font-family:"Menlo";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Monaco";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Consolas";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Courier";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Courier New";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Andale Mono";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Ubuntu Mono";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Fira Code";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Fira Mono";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"DejaVu Sans Mono";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Liberation Mono";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} @font-face{font-family:"Source Code Pro";src:local(${mono_1}),local(${mono_2}),local(${mono_3});} /* 字体渲染优化 */ body{-webkit-font-smoothing:subpixel-antialiased;-moz-osx-font-smoothing:grayscale;} `; } const config = loadConfig(); GM_registerMenuCommand("配置面板", createConfigUI); GM_addStyle(generateCSS()); })();