Greasy Fork is available in English.
检测当前站点是否存在 llms.txt 及其变体;复制按钮 hover 可见;未找到时不提醒
当前为
// ==UserScript==
// @name LLMs.txt Detector
// @namespace http://tampermonkey.net/
// @version 1.4
// @description 检测当前站点是否存在 llms.txt 及其变体;复制按钮 hover 可见;未找到时不提醒
// @author TinsFox
// @license MIT
// @match *://*/*
// @grant GM_xmlhttpRequest
// @grant GM_notification
// ==/UserScript==
(() => {
'use strict';
const filesToCheck = [
'llms.txt',
'llms-full.txt',
'LLMS.txt',
'LLMS-FULL.txt',
'.well-known/llms.txt',
'.well-known/llms-full.txt'
];
const base = location.origin.replace(/\/$/, '');
/* ---- 工具 ---- */
const copy = t => (navigator.clipboard?.writeText(t) ?? Promise.resolve()).then(() => toast('已复制'));
const toast = m => {
const d = document.createElement('div');
d.textContent = m;
d.style = `position:fixed;top:50%;left:50%;transform:translate(-50%,-50%);background:#111;color:#fff;padding:8px 14px;border-radius:6px;font-size:14px;z-index:99999;transition:.2s;opacity:0`;
document.body.appendChild(d);
requestAnimationFrame(() => d.style.opacity = 1);
setTimeout(() => (d.style.opacity = 0, setTimeout(() => d.remove(), 200)), 1200);
};
/* ---- 面板 ---- */
const panel = (() => {
const p = document.createElement('div');
p.id = 'llms-panel';
p.style = `position:fixed;top:20px;right:20px;width:320px;background:#fff;border:2px solid #007cba;border-radius:8px;padding:12px;box-shadow:0 4px 12px rgba(0,0,0,.15);font:14px/1.4 -apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;z-index:10000;display:none`;
document.body.appendChild(p);
return p;
})();
const show = found => {
panel.innerHTML = `
<div style="margin-bottom:10px;font-weight:bold;color:#333">🔍 LLMs.txt 检测结果
<button onclick="this.closest('#llms-panel').style.display='none'" style="float:right;background:#f0f0f0;border:none;border-radius:4px;padding:2px 6px;cursor:pointer;font-size:12px">×</button>
</div>
<div style="margin-bottom:8px">✅ 找到 ${found.length} 个文件:</div>
${found.map(r => `
<div class="file-row" style="display:flex;align-items:center;margin:6px 0;padding:6px;background:#e8f5e8;border-radius:4px">
<a href="${r.url}" target="_blank" style="color:#2d7d2d;text-decoration:none;flex:1">📄 ${r.file}</a>
<button data-url="${r.url}" class="copy-btn" style="margin-left:6px;background:#007cba;color:#fff;border:none;padding:4px 8px;border-radius:4px;font-size:12px;cursor:pointer;display:none">复制</button>
</div>`).join('')}`;
panel.style.display = 'block';
panel.querySelectorAll('.file-row').forEach(row => {
const btn = row.querySelector('.copy-btn');
row.addEventListener('mouseenter', () => btn.style.display = 'inline-block');
row.addEventListener('mouseleave', () => btn.style.display = 'none');
btn.addEventListener('click', e => {
e.preventDefault();
copy(btn.dataset.url);
});
});
if (typeof GM_notification !== 'undefined') GM_notification({title: 'LLMs.txt 检测到文件', text: `${location.hostname} 找到 ${found.length} 个文件`, timeout: 5000});
};
/* ---- 检测 ---- */
const check = f => new Promise(r => {
const u = `${base}/${f.replace(/^\//, '')}`;
const cb = (ok, st) => r({file: f, url: u, found: ok, status: st});
if (typeof GM_xmlhttpRequest !== 'undefined') GM_xmlhttpRequest({method: 'HEAD', url: u, onload: x => cb(x.status >= 200 && x.status < 300, x.status), onerror: x => cb(false, x.status || 'error')});
else fetch(u, {method: 'HEAD'}).then(x => cb(x.ok, x.status)).catch(() => cb(false, 'error'));
});
const run = () => Promise.all(filesToCheck.map(check)).then(res => {
const found = res.filter(r => r.found);
if (found.length) {
show(found);
/* ---- 右下角快捷按钮(仅找到时出现) ---- */
if (!document.getElementById('llms-float-btn')) {
const b = document.createElement('div');
b.id = 'llms-float-btn';
b.textContent = '📄 LLMs.txt';
b.style = `position:fixed;bottom:20px;right:20px;background:#007cba;color:#fff;padding:8px 12px;border-radius:20px;font-size:12px;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,123,.3);z-index:10000`;
b.onclick = () => panel.style.display = panel.style.display === 'none' ? 'block' : 'none';
document.body.appendChild(b);
}
}
});
/* ---- 入口 ---- */
document.addEventListener('keydown', e => { if (e.ctrlKey && e.shiftKey && e.key === 'L') { e.preventDefault(); run(); } });
if (document.readyState === 'loading') document.addEventListener('DOMContentLoaded', () => setTimeout(run, 1000)); else setTimeout(run, 1000);
})();