适配项目:https://github.com/huilang-me/CF-Server-Monitor
适配皮肤: https://github.com/volcano-1025/CFSM-Theme-LuminaPlus
修改// @match 替换成你的探针域名即可
负载默认是2.XX 你可以自己调 因为我跑到过峰值4.XX 好像也没啥问题

// ==UserScript==
// @name ✅ CFSM-Theme-LuminaPlus 百分比着色
// @namespace https://github.com/volcano-1025/CFSM-Theme-LuminaPlus
// @description CPU / 内存 / 磁盘 / 负载 百分比数值无极过渡着色:<50% 绿 / 66% 黄 / 75% 橙 / >90% 红
// @version 260918
// @author 由 Claude 首发于 260918
// @match *://替换成你的探针域名即可*
// @grant none
// @run-at document-idle
// ==/UserScript==
(function () {
'use strict';
/* ── 颜色映射(oklch 无极插值) ──────────────────────────────── */
// 分段锚点:[阈值, hue, sat%, lgt%]
// 10 档锚点:[阈值%, r, g, b] 0%=亮绿(低占用) → 100%=红(高占用)
// 百分比占用色表(CPU/内存/磁盘):0%=绿 → 100%=红
const STOPS = [
[0, 85, 255, 99],
[10, 78, 255, 65],
[20, 95, 255, 46],
[30, 118, 255, 27],
[40, 147, 255, 7],
[50, 178, 242, 0],
[60, 255, 255, 0],
[70, 255, 140, 0],
[80, 255, 68, 0],
[90, 255, 0, 0],
[100, 255, 0, 0],
];
// 负载专用色表:load1 原始值,0=绿 / 0.7=黄 / 1.0=红 / ≥2=深红
// 10 档,上限取 2.0(超过 2 核满载视为极危)
const LOAD_STOPS = [
[0.0, 85, 255, 99], // 空闲 青绿
[0.2, 78, 255, 65], // 极轻 翠绿
[0.4, 95, 255, 46], // 轻载 亮绿
[0.5, 118, 255, 27], // 正常 绿
[0.6, 147, 255, 7], // 偏高 草绿
[0.7, 178, 242, 0], // 警戒 黄绿
[0.8, 255, 255, 0], // 高负载 黄
[0.9, 255, 140, 0], // 满载 橙
[1.0, 255, 68, 0], // 过载 红橙
[1.5, 255, 0, 0], // 严重过载 红
[2.0, 255, 0, 0], // 危险 红
];
function loadColor(load1) {
const val = Math.max(0, load1);
for (let i = 1; i < LOAD_STOPS.length; i++) {
const [p0, r0, g0, b0] = LOAD_STOPS[i - 1];
const [p1, r1, g1, b1] = LOAD_STOPS[i];
if (val <= p1) {
const t = p1 === p0 ? 0 : (val - p0) / (p1 - p0);
return `rgb(${Math.round(lerp(r0,r1,t))},${Math.round(lerp(g0,g1,t))},${Math.round(lerp(b0,b1,t))})`;
}
}
return `rgb(255,0,0)`;
}
function lerp(a, b, t) { return a + (b - a) * t; }
function pctColor(pct) {
const p = Math.max(0, Math.min(100, pct));
for (let i = 1; i < STOPS.length; i++) {
const [p0, r0, g0, b0] = STOPS[i - 1];
const [p1, r1, g1, b1] = STOPS[i];
if (p <= p1) {
const t = p1 === p0 ? 0 : (p - p0) / (p1 - p0);
const r = Math.round(lerp(r0, r1, t));
const g = Math.round(lerp(g0, g1, t));
const b = Math.round(lerp(b0, b1, t));
return `rgb(${r},${g},${b})`;
}
}
return `rgb(85,255,99)`;
}
/* ── 从文本节点解析百分比数字 ───────────────────────────────── */
function parseNum(el) {
// 取元素内所有文本(排除 small/span 单位)
let raw = '';
for (const node of el.childNodes) {
if (node.nodeType === Node.TEXT_NODE) raw += node.textContent;
// strong / span 里的纯数字也取
if (node.nodeType === Node.ELEMENT_NODE &&
!['SMALL'].includes(node.tagName)) {
raw += node.textContent;
}
}
const n = parseFloat(raw.replace(/[^\d.]/g, ''));
return isFinite(n) ? n : null;
}
/* ── 着色逻辑 ────────────────────────────────────────────────── */
const TRANSITION = 'color .35s ease';
// 通用:给一个元素根据其文本数字着色
function colorEl(el) {
const pct = parseNum(el);
if (pct === null) return;
el.style.color = pctColor(pct);
el.style.transition = TRANSITION;
}
// ① server-card 大卡片 —— div.tabular.text-[13px] 里的 span.font-semibold
// 有单位 span(%)→ pctColor;无单位(负载)→ loadColor(load1原始值)
function colorServerCard(wrapper) {
const num = wrapper.querySelector('span.font-semibold');
if (!num) return;
const val = parseFloat(num.textContent);
if (!isFinite(val)) return;
const unitSpan = num.nextElementSibling;
num.style.color = unitSpan ? pctColor(val) : loadColor(val);
num.style.transition = TRANSITION;
}
// ② compact 卡片 —— .compact-node-gauge-head > strong.tabular
// 负载行文本无 %(如 "1.05"),直接用 loadColor();其余用 pctColor()
function colorCompact(strong) {
const val = parseFloat(strong.textContent);
if (!isFinite(val)) return;
const isLoad = !strong.textContent.includes('%');
strong.style.color = isLoad ? loadColor(val) : pctColor(val);
strong.style.transition = TRANSITION;
}
// ③ mini 卡片 —— .mini-metric-value.tabular > strong
// 负载 strong 无兄弟 small(无单位),其余有 small("%")
function colorMini(strong) {
const val = parseFloat(strong.textContent);
if (!isFinite(val)) return;
const hasUnit = !!strong.nextElementSibling; // <small> 单位元素
strong.style.color = hasUnit ? pctColor(val) : loadColor(val);
strong.style.transition = TRANSITION;
}
// ④ 列表行 —— .node-list-gauge-value.tabular
// 负载列 unit="" 无 <small>;百分比列有 <small>
function colorListRow(span) {
let raw = '';
for (const n of span.childNodes) {
if (n.nodeType === Node.TEXT_NODE) raw += n.textContent;
}
const val = parseFloat(raw.trim());
if (!isFinite(val)) return;
const hasUnit = !!span.querySelector('small');
span.style.color = hasUnit ? pctColor(val) : loadColor(val);
span.style.transition = TRANSITION;
}
/* ── 扫描整个文档 ────────────────────────────────────────────── */
function scanAll() {
// ① 大卡片
document.querySelectorAll(
'.tabular.text-\\[13px\\].text-\\[var\\(--text-primary\\)\\]'
).forEach(colorServerCard);
// ② compact 卡片
document.querySelectorAll(
'.compact-node-gauge-head > strong.tabular'
).forEach(colorCompact);
// ③ mini 卡片
document.querySelectorAll(
'.mini-metric-value.tabular > strong'
).forEach(colorMini);
// ④ 列表行
document.querySelectorAll(
'.node-list-gauge-value.tabular'
).forEach(colorListRow);
}
/* ── MutationObserver 监听 React 动态更新 ───────────────────── */
let timer = null;
const observer = new MutationObserver(() => {
clearTimeout(timer);
timer = setTimeout(scanAll, 80); // 批量防抖
});
observer.observe(document.body, {
childList: true,
subtree: true,
characterData: true, // 监听文本变化(数值实时刷新)
});
// 首次扫描
scanAll();
})();