替换网页的等宽字体。可根据需求修改脚本中的字体设置。使用前需确保已安装所需字体。
// ==UserScript==
// @name 等宽字体替换增强器
// @namespace https://greasyfork.org/scripts/549604
// @description 替换网页的等宽字体。可根据需求修改脚本中的字体设置。使用前需确保已安装所需字体。
// @version 11.0.1
// @license Apache License 2.0
// @author Ckrvxr
// @match *://*/*
// @run-at document-start
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @homepageURL https://greasyfork.org/zh-CN/scripts/549604
// ==/UserScript==
(function () {
"use strict";
const defaultConfig = {mono_latin: ["Cascadia Next SC", "Cascadia Code", "SF Mono"],};
function loadConfig() {
return {
mono_latin: GM_getValue("mono_latin", defaultConfig.mono_latin),
};
}
function saveConfig(config) {
GM_setValue("mono_latin", config.mono_latin);
}
function createConfigUI(config) {
if (document.getElementById('font-config-panel')) return;
document.body.style.overflow = "hidden";
document.documentElement.style.overflowX = "hidden";
document.documentElement.style.overflowY = "hidden";
const panel = document.createElement("div");
panel.id = "font-config-panel";
panel.style.cssText = `
z-index: 999999;
position: fixed;
inset: 0;
background: #ffffff;
overflow-y: auto;
padding: 4rem;
font-family: sans-serif;
`;
const title = document.createElement("h1");
title.textContent = "等宽字体替换增强器 · 配置面板";
title.style.cssText = `
margin: 0 0 2rem;
font-size: 1.5rem;
text-align: center;
color: #333333;
`;
const createInputGroup = (type, label) => {
const container = document.createElement("div");
container.style.marginBottom = "2rem";
const titleLabel = document.createElement("label");
titleLabel.textContent = label;
titleLabel.style.cssText = `
display: block;
margin-bottom: 0.75rem;
font-weight: 600;
color: #333333;
font-size: 1.1rem;
`;
const inputContainer = document.createElement("div");
inputContainer.style.cssText = `
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: 1.5rem;
`;
config[type].forEach((value, index) => {
const input = document.createElement("input");
input.type = "text";
input.value = value;
input.dataset.type = type;
input.dataset.index = index;
input.placeholder = `备选字体 ${index + 1}`;
input.style.cssText = `
width: 100%;
padding: 0.6rem;
border: 1px solid #cccccc;
border-radius: 6px;
font-size: 0.9rem;
color: #333333;
background: #f9f9f9;
`;
inputContainer.appendChild(input);
});
container.appendChild(titleLabel);
container.appendChild(inputContainer);
return container;
};
const createButton = (text, colorScheme) => {
const btn = document.createElement("button");
btn.textContent = text;
btn.style.cssText = `
flex: 1;
padding: 0.75rem 1rem;
border: none;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
transition: all 0.2s;
margin: 0 0.5rem;
color: white;
background: ${colorScheme.base};
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
`;
const hoverEffect = () => {
btn.style.background = colorScheme.hover;
btn.style.transform = "translateY(-1px)";
btn.style.boxShadow = "0 4px 8px rgba(0,0,0,0.15)";
};
const outEffect = () => {
btn.style.background = colorScheme.base;
btn.style.transform = "translateY(0)";
btn.style.boxShadow = "0 2px 4px rgba(0,0,0,0.1)";
};
btn.addEventListener("mouseenter", hoverEffect);
btn.addEventListener("mouseleave", outEffect);
btn.addEventListener("touchstart", hoverEffect, { passive: true });
btn.addEventListener("touchend", outEffect, { passive: true });
return btn;
};
const colorSchemes = {
primary: { base: "#4f46e5", hover: "#4338ca" },
secondary: { base: "#64748b", hover: "#475569" },
success: { base: "#10b981", hover: "#0b8a5e" },
warning: { base: "#f59e0b", hover: "#d97706" },
danger: { base: "#ef4444", hover: "#dc2626" },
};
const importInput = document.createElement("input");
importInput.type = "file";
importInput.accept = ".json";
importInput.style.display = "none";
const importBtn = createButton("导入配置", colorSchemes.warning);
importBtn.onclick = () => importInput.click();
importInput.onchange = async () => {
try {
const file = importInput.files[0];
if (!file) return;
const content = await file.text();
const importedConfig = JSON.parse(content);
if (importedConfig.mono_latin && Array.isArray(importedConfig.mono_latin)) {
config.mono_latin = [...importedConfig.mono_latin];
saveConfig(config);
alert("导入成功:请刷新页面生效");
closePanel();
} else {
alert("导入失败:配置文件格式不正确");
}
} catch (e) {
console.error("导入配置失败:", e);
alert("导入失败:无效的配置文件或读取错误");
}
};
const exportBtn = createButton("导出配置", colorSchemes.secondary);
exportBtn.onclick = () => {
const blob = new Blob([JSON.stringify({ mono_latin: config.mono_latin }, null, 2)], {
type: "application/json",
});
const url = URL.createObjectURL(blob);
const a = document.createElement("a");
a.href = url;
a.download = "monospace-font-config.json";
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
};
const resetBtn = createButton("重置默认", colorSchemes.danger);
resetBtn.onclick = () => {
if (confirm("确定要重置等宽字体配置为默认值吗?")) {
config.mono_latin = [...defaultConfig.mono_latin];
saveConfig(config);
alert("重置成功:请刷新页面生效");
closePanel();
}
};
const saveBtn = createButton("保存配置", colorSchemes.success);
saveBtn.onclick = () => {
const inputs = panel.querySelectorAll("input[data-type='mono_latin']");
inputs.forEach((input) => {
const index = parseInt(input.dataset.index, 10);
if (!isNaN(index)) {
config.mono_latin[index] = input.value.trim();
}
});
saveConfig(config);
alert("保存成功:请刷新页面生效");
closePanel();
};
const closeBtn = createButton("关闭", colorSchemes.primary);
const closePanel = () => {
panel.remove();
document.body.style.overflow = "";
document.documentElement.style.overflowX = "";
document.documentElement.style.overflowY = "";
};
closeBtn.onclick = closePanel;
panel.appendChild(title);
panel.appendChild(createInputGroup("mono_latin", "等宽字体 (Monospace)"));
const actionRow1 = document.createElement("div");
actionRow1.style.cssText = `
display: flex;
justify-content: center;
gap: 1rem;
margin: 2rem 0;
flex-wrap: wrap;
`;
actionRow1.appendChild(importBtn);
actionRow1.appendChild(exportBtn);
actionRow1.appendChild(resetBtn);
panel.appendChild(actionRow1);
const actionRow2 = document.createElement("div");
actionRow2.style.cssText = `
display: flex;
justify-content: center;
gap: 1rem;
margin: 1rem 0;
flex-wrap: wrap;
`;
actionRow2.appendChild(saveBtn);
actionRow2.appendChild(closeBtn);
panel.appendChild(actionRow2);
document.body.appendChild(panel);
}
function generateCSS(config) {
const [mono_latin_1, mono_latin_2, mono_latin_3] = config.mono_latin;
let css = `
@font-face {font-family: "MONO_SPACE"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "monospace"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Monaco"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Consolas"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Courier"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Courier New"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Andale Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Ubuntu Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Fira Code"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Fira Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "DejaVu Sans Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Liberation Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Source Code Pro"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "Menlo"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "San Francisco Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "SF Mono"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
@font-face {font-family: "SFMono-Regular"; src: local("${mono_latin_1}"), local("${mono_latin_2}"), local("${mono_latin_3}");}
`;
const currentUrl = window.location.href;
let additionalStyles = "";
if (currentUrl.startsWith("https://greasyfork.org/")) {
additionalStyles += `
pre {
font-family: "MONO_SPACE", monospace !important;
}
`;
} else if (currentUrl.startsWith("https://chat.qwen.ai/")) {
additionalStyles += `
.ͼ1 .cm-scroller {
font-family: "MONO_SPACE", monospace !important;
}
`;
} else if (currentUrl.startsWith("https://cplusplus.com/")) {
additionalStyles += `
pre, code, tt, samp, var, dfn, cite, kbd {
font-family: "MONO_SPACE", monospace !important;
}
`;
} else if (currentUrl.startsWith("https://www.cppreference.com/")) {
additionalStyles += `
code {
font-family: "MONO_SPACE", monospace !important;
}
`;
} else if (currentUrl.startsWith("https://www.wenxiaobai.com/")) {
additionalStyles += `
.markdown-body code *,
code {
font-family: "MONO_SPACE", monospace !important;
}
`;
}
if (additionalStyles) {css += `${additionalStyles}`;}
css += `
body {
-webkit-font-smoothing: subpixel-antialiased !important;
-moz-osx-font-smoothing: grayscale !important;
font-smoothing: antialiased !important;
}
`;
return css;
}
const config = loadConfig();
GM_registerMenuCommand("配置面板", () => createConfigUI(config));
GM_addStyle(generateCSS(config));
})();