不是,你代码块层级有问题 ``` 放最外面
其实可以考虑直接不要文字,通过颜色来区分
原来如此,是我帖子回复有问题,md语法```这一块用的少
1 个赞
我用的homeproxy,所以我不知道
噢,也是,颜色更直观,毕竟文字有时候会受到网页背景影响,有时候看不清
1 个赞
改了个版,用起来很舒服
// ==UserScript==
// @name Clash Connection Monitor
// @namespace http://tampermonkey.net/
// @version 0.1
// @description 优雅地显示当前网页的Clash连接信息
// @author Your name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @icon https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png
// ==/UserScript==
(function () {
("use strict");
// Clash API 配置
const CLASH_API = {
BASE_URL: "http://127.0.0.1:7890",
SECRET: "your-secret-key",
};
// 创建样式
const style = document.createElement("style");
style.textContent = `
#clash-monitor {
position: fixed;
bottom: 20px;
right: 20px;
padding: 4px;
border-radius: 12px;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 14px;
font-weight: 500;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: flex-end;
cursor: pointer;
transition: all 0.2s ease;
background: none;
border: none;
color: #333;
}
#clash-monitor:hover {
background: rgba(255, 255, 255, 0.95);
border: none;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
transform: translateY(-2px);
padding: 10px 16px;
backdrop-filter: blur(8px);
}
#status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
}
#connection-details {
max-height: 0;
opacity: 0;
overflow: hidden;
transition: all 0.4s ease-out;
margin-top: 0;
font-size: 12px;
color: #666;
}
#clash-monitor:hover #connection-details {
max-height: 500px;
opacity: 1;
margin-top: 8px;
padding-top: 8px;
border-top: 1px solid rgba(230, 230, 230, 0.8);
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 16px;
margin: 4px 0;
white-space: nowrap;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
#clash-monitor:hover {
background: rgba(40, 40, 40, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
}
}
`;
document.head.appendChild(style);
// 创建监视器元素
const monitor = document.createElement("div");
monitor.id = "clash-monitor";
monitor.innerHTML = `
<div id="connection-indicator">
<div id="status-dot"></div>
</div>
<div id="connection-details">
<div class="detail-item">
<span>代理链路:</span>
<span id="proxy-chain">-</span>
</div>
<div class="detail-item">
<span>规则:</span>
<span id="rule-match">-</span>
</div>
<div class="detail-item">
<span>上传:</span>
<span id="upload-speed">-</span>
</div>
<div class="detail-item">
<span>下载:</span>
<span id="download-speed">-</span>
</div>
</div>
`;
document.body.appendChild(monitor);
// 格式化字节数
function formatBytes(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
// 更新连接信息
function updateConnectionInfo() {
const currentHost = window.location.hostname;
GM_xmlhttpRequest({
method: "GET",
url: `${CLASH_API.BASE_URL}/connections`,
headers: {
Authorization: `Bearer ${CLASH_API.SECRET}`,
},
onload: function (response) {
try {
const data = JSON.parse(response.responseText);
const connections = data.connections;
const currentConn = connections.find(
(conn) => conn.metadata.host === currentHost
);
const statusDot = document.getElementById("status-dot");
const proxyChain = document.getElementById("proxy-chain");
const ruleMatch = document.getElementById("rule-match");
const uploadSpeed = document.getElementById("upload-speed");
const downloadSpeed = document.getElementById("download-speed");
if (currentConn) {
if (currentConn.chains.length > 1) {
// 代理连接 - 蓝色
statusDot.style.backgroundColor = "#3498db";
} else {
// 直连 - 绿色
statusDot.style.backgroundColor = "#2ecc71";
}
proxyChain.textContent = currentConn.chains.join(" → ");
ruleMatch.textContent = currentConn.rule;
uploadSpeed.textContent = formatBytes(currentConn.upload);
downloadSpeed.textContent = formatBytes(currentConn.download);
} else {
// 断开/无连接 - 灰色
statusDot.style.backgroundColor = "#95a5a6";
proxyChain.textContent = "-";
ruleMatch.textContent = "-";
uploadSpeed.textContent = "-";
downloadSpeed.textContent = "-";
}
} catch (error) {
console.error("解析连接信息失败:", error);
}
},
onerror: function (error) {
console.error("连接Clash API失败:", error);
},
});
}
// 初始化更新
updateConnectionInfo();
// 定期更新
setInterval(updateConnectionInfo, 5000);
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
updateConnectionInfo();
}
});
})();
1 个赞
现在还有个小问题,我的代理链路,名字太长了,当我鼠标指到颜色的靠左边一部分,也会展示详情。
css这一块我不会弄,有办法让检测鼠标范围减小到颜色附近,固定这一小块吗
// ==UserScript==
// @name Clash Connection Monitor
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 优雅地显示当前网页的Clash连接信息
// @author Your name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @icon https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png
// ==/UserScript==
(function () {
("use strict");
// Clash API 配置
const CLASH_API = {
BASE_URL: "http://127.0.0.1:7890",
SECRET: "your-secret-key",
};
// 创建样式
const style = document.createElement("style");
style.textContent = `
#clash-monitor {
position: fixed;
bottom: 20px;
right: 20px;
padding: 4px;
border-radius: 12px;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 14px;
font-weight: 500;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: flex-end;
transition: all 0.2s ease;
background: none;
border: none;
color: #333;
}
#connection-indicator {
position: relative;
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
overflow: visible;
}
#status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
flex-shrink: 0;
cursor: pointer;
}
#connection-details {
position: absolute;
right: 0;
bottom: calc(100% + 10px);
max-height: 0;
opacity: 0;
overflow: hidden;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 0;
font-size: 12px;
color: #666;
white-space: nowrap;
pointer-events: none;
backdrop-filter: blur(8px);
}
#status-dot:hover + #connection-details,
#connection-details:hover {
max-height: 500px;
opacity: 1;
padding: 10px 16px;
pointer-events: auto;
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 16px;
margin: 4px 0;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
#clash-monitor {
color: #f0f0f0;
}
#connection-details {
background: rgba(40, 40, 40, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
color: #ddd;
}
}
`;
document.head.appendChild(style);
// 创建监视器元素
const monitor = document.createElement("div");
monitor.id = "clash-monitor";
monitor.innerHTML = `
<div id="connection-indicator">
<div id="status-dot"></div>
<div id="connection-details">
<div class="detail-item">
<span>代理链路:</span>
<span id="proxy-chain">-</span>
</div>
<div class="detail-item">
<span>规则:</span>
<span id="rule-match">-</span>
</div>
<div class="detail-item">
<span>上传:</span>
<span id="upload-speed">-</span>
</div>
<div class="detail-item">
<span>下载:</span>
<span id="download-speed">-</span>
</div>
</div>
</div>
`;
document.body.appendChild(monitor);
// 格式化字节数
function formatBytes(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
// 更新连接信息
function updateConnectionInfo() {
const currentHost = window.location.hostname;
GM_xmlhttpRequest({
method: "GET",
url: `${CLASH_API.BASE_URL}/connections`,
headers: {
Authorization: `Bearer ${CLASH_API.SECRET}`,
},
onload: function (response) {
try {
const data = JSON.parse(response.responseText);
const connections = data.connections;
const currentConn = connections.find(
(conn) => conn.metadata.host === currentHost
);
const statusDot = document.getElementById("status-dot");
const proxyChain = document.getElementById("proxy-chain");
const ruleMatch = document.getElementById("rule-match");
const uploadSpeed = document.getElementById("upload-speed");
const downloadSpeed = document.getElementById("download-speed");
if (currentConn) {
if (currentConn.chains.length > 1) {
// 代理连接 - 蓝色
statusDot.style.backgroundColor = "#3498db";
} else {
// 直连 - 绿色
statusDot.style.backgroundColor = "#2ecc71";
}
proxyChain.textContent = currentConn.chains.join(" → ");
ruleMatch.textContent = currentConn.rule;
uploadSpeed.textContent = formatBytes(currentConn.upload);
downloadSpeed.textContent = formatBytes(currentConn.download);
} else {
// 断开/无连接 - 灰色
statusDot.style.backgroundColor = "#95a5a6";
proxyChain.textContent = "-";
ruleMatch.textContent = "-";
uploadSpeed.textContent = "-";
downloadSpeed.textContent = "-";
}
} catch (error) {
console.error("解析连接信息失败:", error);
}
},
onerror: function (error) {
console.error("连接Clash API失败:", error);
},
});
}
// 初始化更新
updateConnectionInfo();
// 定期更新
setInterval(updateConnectionInfo, 5000);
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
updateConnectionInfo();
}
});
})();
1 个赞
刚发现,这代码,貌似有点问题,if (currentConn.chains.length > 1)
并不能判断出是代理还是直连吧,现在好像一直都是判断为代理
奥,可以通过规则是不是 DIRECT 来判断,不过我的确实是可以判断的,baidu.com 是绿色
对的,你可以根据自己的规则微调,比如链路里面包含 “DIRECT”
1 个赞
这是修改了29楼的代码,原代码意思是判断“代理链路”文字长度>1,则是代理,否则是直连
我将其蓝绿判断改成:代理链路中的字符串是否包含”DIRECT“字符串,来判断。
代码
// ==UserScript==
// @name Clash Connection Monitor
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 优雅地显示当前网页的Clash连接信息
// @author Your name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @icon https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png
// ==/UserScript==
(function () {
("use strict");
// Clash API 配置
const CLASH_API = {
BASE_URL: "http://127.0.0.1:9097",
SECRET: "修改为你的密码或者置空",
};
// 创建样式
const style = document.createElement("style");
style.textContent = `
#clash-monitor {
position: fixed;
bottom: 20px;
right: 20px;
padding: 4px;
border-radius: 12px;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 14px;
font-weight: 500;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: flex-end;
transition: all 0.2s ease;
background: none;
border: none;
color: #333;
}
#connection-indicator {
position: relative;
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
overflow: visible;
}
#status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
flex-shrink: 0;
cursor: pointer;
}
#connection-details {
position: absolute;
right: 0;
bottom: calc(100% + 10px);
max-height: 0;
opacity: 0;
overflow: hidden;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 0;
font-size: 12px;
color: #666;
white-space: nowrap;
pointer-events: none;
backdrop-filter: blur(8px);
}
#status-dot:hover + #connection-details,
#connection-details:hover {
max-height: 500px;
opacity: 1;
padding: 10px 16px;
pointer-events: auto;
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 16px;
margin: 4px 0;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
#clash-monitor {
color: #f0f0f0;
}
#connection-details {
background: rgba(40, 40, 40, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
color: #ddd;
}
}
`;
document.head.appendChild(style);
// 创建监视器元素
const monitor = document.createElement("div");
monitor.id = "clash-monitor";
monitor.innerHTML = `
<div id="connection-indicator">
<div id="status-dot"></div>
<div id="connection-details">
<div class="detail-item">
<span>代理链路:</span>
<span id="proxy-chain">-</span>
</div>
<div class="detail-item">
<span>规则:</span>
<span id="rule-match">-</span>
</div>
<div class="detail-item">
<span>上传:</span>
<span id="upload-speed">-</span>
</div>
<div class="detail-item">
<span>下载:</span>
<span id="download-speed">-</span>
</div>
</div>
</div>
`;
document.body.appendChild(monitor);
// 格式化字节数
function formatBytes(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
// 更新连接信息
function updateConnectionInfo() {
const currentHost = window.location.hostname;
GM_xmlhttpRequest({
method: "GET",
url: `${CLASH_API.BASE_URL}/connections`,
headers: {
Authorization: `Bearer ${CLASH_API.SECRET}`,
},
onload: function (response) {
try {
const data = JSON.parse(response.responseText);
const connections = data.connections;
const currentConn = connections.find(
(conn) => conn.metadata.host === currentHost
);
const statusDot = document.getElementById("status-dot");
const proxyChain = document.getElementById("proxy-chain");
const ruleMatch = document.getElementById("rule-match");
const uploadSpeed = document.getElementById("upload-speed");
const downloadSpeed = document.getElementById("download-speed");
if (currentConn) {
const cCchains = currentConn.chains;
if (cCchains.includes('DIRECT')) {
// 代理连接 - 蓝色
statusDot.style.backgroundColor = "#3498db";
} else {
// 直连 - 绿色
statusDot.style.backgroundColor = "#2ecc71";
}
proxyChain.textContent = currentConn.chains.join(" → ");
ruleMatch.textContent = currentConn.rule;
uploadSpeed.textContent = formatBytes(currentConn.upload);
downloadSpeed.textContent = formatBytes(currentConn.download);
} else {
// 断开/无连接 - 灰色
statusDot.style.backgroundColor = "#95a5a6";
proxyChain.textContent = "-";
ruleMatch.textContent = "-";
uploadSpeed.textContent = "-";
downloadSpeed.textContent = "-";
}
} catch (error) {
console.error("解析连接信息失败:", error);
}
},
onerror: function (error) {
console.error("连接Clash API失败:", error);
},
});
}
// 初始化更新
updateConnectionInfo();
// 定期更新
setInterval(updateConnectionInfo, 5000);
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
updateConnectionInfo();
}
});
})();
在此基础上,我还是加上了文字,
代码
// ==UserScript==
// @name Clash Connection Monitor
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 优雅地显示当前网页的Clash连接信息
// @author Your name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @icon https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png
// ==/UserScript==
(function () {
("use strict");
// Clash API 配置
const CLASH_API = {
BASE_URL: "http://127.0.0.1:9097",
SECRET: "修改为你的密码或者置空",
};
// 创建样式
const style = document.createElement("style");
style.textContent = `
#clash-monitor {
position: fixed;
bottom: 20px;
right: 20px;
padding: 4px;
border-radius: 12px;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 14px;
font-weight: 500;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: flex-end;
transition: all 0.2s ease;
background: none;
border: none;
color: #333;
}
#connection-indicator {
position: relative;
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
overflow: visible;
}
#connection-text {
margin-right: 3px;
color: black;
}
#status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
flex-shrink: 0;
cursor: pointer;
}
#connection-details {
position: absolute;
right: 0;
bottom: calc(100% + 10px);
max-height: 0;
opacity: 0;
overflow: hidden;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 0;
font-size: 12px;
color: #666;
white-space: nowrap;
pointer-events: none;
backdrop-filter: blur(8px);
}
#status-dot:hover + #connection-details,
#connection-text:hover ~ #connection-details,
#connection-details:hover {
max-height: 500px;
opacity: 1;
padding: 10px 16px;
pointer-events: auto;
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 16px;
margin: 4px 0;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
#clash-monitor {
color: #f0f0f0;
}
#connection-details {
background: rgba(40, 40, 40, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
color: #ddd;
}
}
`;
document.head.appendChild(style);
// 创建监视器元素
const monitor = document.createElement("div");
monitor.id = "clash-monitor";
monitor.innerHTML = `
<div id="connection-indicator">
<span id="connection-text">等待连接...</span>
<div id="status-dot"></div>
<div id="connection-details">
<div class="detail-item">
<span>代理链路:</span>
<span id="proxy-chain">-</span>
</div>
<div class="detail-item">
<span>规则:</span>
<span id="rule-match">-</span>
</div>
<div class="detail-item">
<span>上传:</span>
<span id="upload-speed">-</span>
</div>
<div class="detail-item">
<span>下载:</span>
<span id="download-speed">-</span>
</div>
</div>
</div>
`;
document.body.appendChild(monitor);
// 格式化字节数
function formatBytes(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
// 更新连接信息
function updateConnectionInfo() {
const currentHost = window.location.hostname;
GM_xmlhttpRequest({
method: "GET",
url: `${CLASH_API.BASE_URL}/connections`,
headers: {
Authorization: `Bearer ${CLASH_API.SECRET}`,
},
onload: function (response) {
try {
const data = JSON.parse(response.responseText);
const connections = data.connections;
const currentConn = connections.find(
(conn) => conn.metadata.host === currentHost
);
const statusDot = document.getElementById("status-dot");
const connText = document.getElementById('connection-text');
const proxyChain = document.getElementById("proxy-chain");
const ruleMatch = document.getElementById("rule-match");
const uploadSpeed = document.getElementById("upload-speed");
const downloadSpeed = document.getElementById("download-speed");
if (currentConn) {
const cCchains = currentConn.chains;
if (cCchains.includes('DIRECT')) {
// 直连 - 绿色
connText.textContent = '直连';
statusDot.style.backgroundColor = "#2ecc71";
} else {
// 代理连接 - 蓝色
connText.textContent = '代理';
statusDot.style.backgroundColor = "#3498db";
}
proxyChain.textContent = currentConn.chains.join(" → ");
ruleMatch.textContent = currentConn.rule;
uploadSpeed.textContent = formatBytes(currentConn.upload);
downloadSpeed.textContent = formatBytes(currentConn.download);
} else {
// 断开/无连接 - 灰色
connText.textContent = '无连接';
statusDot.style.backgroundColor = "#95a5a6";
proxyChain.textContent = "-";
ruleMatch.textContent = "-";
uploadSpeed.textContent = "-";
downloadSpeed.textContent = "-";
}
} catch (error) {
console.error("解析连接信息失败:", error);
}
},
onerror: function (error) {
console.error("连接Clash API失败:", error);
},
});
}
// 初始化更新
updateConnectionInfo();
// 定期更新
setInterval(updateConnectionInfo, 5000);
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
updateConnectionInfo();
}
});
})();
1 个赞
太强了
top少了一行
少了一行 // ==UserScript==
我```用的还是不熟练,不知道要回车。。。
在此基础上,我又把文字,改成黑色文字,白色描边。这样再也不惧任何网站的背景颜色导致看不清文字了实时反色我不会搞
代码
// ==UserScript==
// @name Clash Connection Monitor
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 优雅地显示当前网页的Clash连接信息
// @author Your name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @icon https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png
// ==/UserScript==
(function () {
("use strict");
// Clash API 配置
const CLASH_API = {
BASE_URL: "http://127.0.0.1:9097",
SECRET: "修改为你的密码或者置空",
};
// 创建样式
const style = document.createElement("style");
style.textContent = `
#clash-monitor {
position: fixed;
bottom: 20px;
right: 20px;
padding: 4px;
border-radius: 12px;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-size: 14px;
font-weight: 500;
z-index: 10000;
display: flex;
flex-direction: column;
align-items: flex-end;
transition: all 0.2s ease;
background: none;
border: none;
color: #333;
}
#connection-indicator {
position: relative;
display: flex;
align-items: center;
justify-content: flex-end;
width: 100%;
overflow: visible;
}
#connection-text {
margin-right: 3px;
color: black;
text-shadow:
1px 1px 2px white,
-1px -1px 2px white,
1px -1px 2px white,
-1px 1px 2px white;
}
#status-dot {
width: 10px;
height: 10px;
border-radius: 50%;
transition: all 0.3s ease;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.15);
flex-shrink: 0;
cursor: pointer;
}
#connection-details {
position: absolute;
right: 0;
bottom: calc(100% + 10px);
max-height: 0;
opacity: 0;
overflow: hidden;
transition: all 0.2s ease;
background: rgba(255, 255, 255, 0.95);
border-radius: 8px;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
padding: 0;
font-size: 12px;
color: #666;
white-space: nowrap;
pointer-events: none;
backdrop-filter: blur(8px);
}
#status-dot:hover + #connection-details,
#connection-text:hover ~ #connection-details,
#connection-details:hover {
max-height: 500px;
opacity: 1;
padding: 10px 16px;
pointer-events: auto;
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 16px;
margin: 4px 0;
}
/* 深色模式 */
@media (prefers-color-scheme: dark) {
#clash-monitor {
color: #f0f0f0;
}
#connection-details {
background: rgba(40, 40, 40, 0.95);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
color: #ddd;
}
}
`;
document.head.appendChild(style);
// 创建监视器元素
const monitor = document.createElement("div");
monitor.id = "clash-monitor";
monitor.innerHTML = `
<div id="connection-indicator">
<span id="connection-text">等待连接...</span>
<div id="status-dot"></div>
<div id="connection-details">
<div class="detail-item">
<span>代理链路:</span>
<span id="proxy-chain">-</span>
</div>
<div class="detail-item">
<span>规则:</span>
<span id="rule-match">-</span>
</div>
<div class="detail-item">
<span>上传:</span>
<span id="upload-speed">-</span>
</div>
<div class="detail-item">
<span>下载:</span>
<span id="download-speed">-</span>
</div>
</div>
</div>
`;
document.body.appendChild(monitor);
// 格式化字节数
function formatBytes(bytes) {
if (bytes === 0) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
// 更新连接信息
function updateConnectionInfo() {
const currentHost = window.location.hostname;
GM_xmlhttpRequest({
method: "GET",
url: `${CLASH_API.BASE_URL}/connections`,
headers: {
Authorization: `Bearer ${CLASH_API.SECRET}`,
},
onload: function (response) {
try {
const data = JSON.parse(response.responseText);
const connections = data.connections;
const currentConn = connections.find(
(conn) => conn.metadata.host === currentHost
);
const statusDot = document.getElementById("status-dot");
const connText = document.getElementById('connection-text');
const proxyChain = document.getElementById("proxy-chain");
const ruleMatch = document.getElementById("rule-match");
const uploadSpeed = document.getElementById("upload-speed");
const downloadSpeed = document.getElementById("download-speed");
if (currentConn) {
const cCchains = currentConn.chains;
if (cCchains.includes('DIRECT')) {
// 直连 - 中国红
connText.textContent = '直连';
statusDot.style.backgroundColor = "#D70026";
} else {
// 代理连接 - 海洋蓝
connText.textContent = '代理';
statusDot.style.backgroundColor = "#00BFFF";
}
proxyChain.textContent = currentConn.chains.join(" → ");
ruleMatch.textContent = currentConn.rule;
uploadSpeed.textContent = formatBytes(currentConn.upload);
downloadSpeed.textContent = formatBytes(currentConn.download);
} else {
// 断开/无连接 - 灰色
connText.textContent = '无连接';
statusDot.style.backgroundColor = "#95a5a6";
proxyChain.textContent = "-";
ruleMatch.textContent = "-";
uploadSpeed.textContent = "-";
downloadSpeed.textContent = "-";
}
} catch (error) {
console.error("解析连接信息失败:", error);
}
},
onerror: function (error) {
console.error("连接Clash API失败:", error);
},
});
}
// 初始化更新
updateConnectionInfo();
// 定期更新
setInterval(updateConnectionInfo, 5000);
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
if (!document.hidden) {
updateConnectionInfo();
}
});
})();
感谢佬们的努力和分享,已经用上了
根据自用的情况优化了一下:
- 状态点固定在左下角
- 信息栏用
<code>
框起来,尽量使用等宽字体 - 信息栏中的规则添加了具体的规则明细
代码
// ==UserScript==
// @name Clash Connection Monitor
// @namespace http://tampermonkey.net/
// @version 0.2
// @description 优雅地显示当前网页的Clash连接信息
// @author Your name
// @match *://*/*
// @grant GM_xmlhttpRequest
// @icon https://cdn.jsdelivr.net/gh/Dreamacro/clash/docs/logo.png
// ==/UserScript==
(function () {
("use strict");
// Clash API 配置
const CLASH_API = {
BASE_URL: "http://127.0.0.1:9097",
SECRET: "修改为你的密码或者置空",
};
// 动态样式
const style = document.createElement("style");
style.textContent = `
#clash-monitor {
position: fixed;
bottom: 20px;
left: 20px;
z-index: 10000;
display: flex;
flex-direction: column-reverse;
align-items: flex-start;
gap: 8px;
}
#status-dot {
width: 12px;
height: 12px;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
background-color: #95a5a6; /* 初始状态:灰色 */
cursor: pointer;
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
#status-dot:hover {
transform: scale(1.2);
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
}
#connection-details {
max-height: 0;
opacity: 0;
overflow: hidden;
padding: 0;
background: rgba(255, 255, 255, 0.95);
color: #333;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
font-size: 12px;
line-height: 1.5;
pointer-events: none;
white-space: nowrap;
backdrop-filter: blur(12px);
transition: all 0.3s ease;
}
#clash-monitor:hover #connection-details {
max-height: 200px;
opacity: 1;
padding: 10px;
pointer-events: auto;
}
.detail-item {
display: flex;
justify-content: space-between;
gap: 16px;
margin: 4px 0;
}
@media (prefers-color-scheme: dark) {
#connection-details {
background: rgba(40, 40, 40, 0.95);
color: #f0f0f0;
}
}
`;
document.head.appendChild(style);
// 创建监视器元素
const monitor = document.createElement("div");
monitor.id = "clash-monitor";
monitor.innerHTML = `
<div id="status-dot"></div>
<div id="connection-details">
<code>
<div class="detail-item">
<span>链路:</span>
<span id="proxy-chain">-</span>
</div>
<div class="detail-item">
<span>规则:</span>
<span id="rule-match">-</span>
</div>
<div class="detail-item">
<span>上传:</span>
<span id="upload-speed">-</span>
</div>
<div class="detail-item">
<span>下载:</span>
<span id="download-speed">-</span>
</div>
</code>
</div>
`;
document.body.appendChild(monitor);
// 格式化字节数
function formatBytes(bytes) {
if (bytes === 0 || !bytes) return "0 B";
const k = 1024;
const sizes = ["B", "KB", "MB", "GB"];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + " " + sizes[i];
}
// 更新连接信息
function updateConnectionInfo() {
GM_xmlhttpRequest({
method: "GET",
url: `${CLASH_API.BASE_URL}/connections`,
headers: {
Authorization: `Bearer ${CLASH_API.SECRET}`,
},
onload: function (response) {
try {
const data = JSON.parse(response.responseText);
const connections = data.connections;
const currentHost = window.location.hostname;
const currentConn = connections.find(
(conn) => conn.metadata.host === currentHost
);
const statusDot = document.getElementById("status-dot");
const proxyChain = document.getElementById("proxy-chain");
const ruleMatch = document.getElementById("rule-match");
const uploadSpeed = document.getElementById("upload-speed");
const downloadSpeed = document.getElementById("download-speed");
if (currentConn) {
const isProxy = !currentConn.chains.includes("DIRECT");
statusDot.style.backgroundColor = isProxy
? "#3498db" // 代理:蓝色
: "#2ecc71"; // 直连:绿色
proxyChain.textContent = currentConn.chains.join(" → ");
//ruleMatch.textContent = currentConn.rule || "未知";
ruleMatch.textContent = currentConn.rulePayload ? currentConn.rulePayload + " → " + currentConn.rule : currentConn.rule;
uploadSpeed.textContent = formatBytes(currentConn.upload);
downloadSpeed.textContent = formatBytes(currentConn.download);
} else {
statusDot.style.backgroundColor = "#95a5a6"; // 断开:灰色
proxyChain.textContent = "-";
ruleMatch.textContent = "-";
uploadSpeed.textContent = "-";
downloadSpeed.textContent = "-";
}
} catch (error) {
console.error("解析连接信息失败:", error);
}
},
onerror: function () {
console.error("Clash API 请求失败,请检查网络或 API 配置。");
},
});
}
// 初始化并设置定时更新
updateConnectionInfo();
setInterval(updateConnectionInfo, 5000);
// 监听页面可见性变化
document.addEventListener("visibilitychange", () => {
if (!document.hidden) updateConnectionInfo();
});
})();