一鍵複製 lkml.org 信件 <pre itemprop="articleBody"> 內的 HTML,三層備援不失敗。
当前为
// ==UserScript==
// @name LKML 內文複製器(多重備援)
// @namespace https://abc0922001.github.io/lkml-userscripts
// @version 1.2
// @description 一鍵複製 lkml.org 信件 <pre itemprop="articleBody"> 內的 HTML,三層備援不失敗。
// @author abc0922001
// @match https://lkml.org/lkml/*
// @grant GM_setClipboard
// @license MIT
// ==/UserScript==
(() => {
'use strict';
/* ---------- 等待元素 ---------- */
function waitForElement(selector, timeout = 5000) {
return new Promise((resolve, reject) => {
const hit = document.querySelector(selector);
if (hit) return resolve(hit);
const ob = new MutationObserver(() => {
const el = document.querySelector(selector);
if (el) {
ob.disconnect();
resolve(el);
}
});
ob.observe(document.body, { childList: true, subtree: true });
setTimeout(() => {
ob.disconnect();
reject(new Error(`等待元素「${selector}」超時 (${timeout}ms)`));
}, timeout);
});
}
/* ---------- 提示 ---------- */
const showAlert = msg => window.alert(msg);
/* ---------- 多重寫入剪貼簿 ---------- */
async function copyToClipboard(html) {
/* ① 原生 Clipboard API(含 text/html),成功率高 */
try {
await navigator.clipboard.write([
new ClipboardItem({ 'text/html': new Blob([html], { type: 'text/html' }) })
]);
showAlert('✅ 已複製 (HTML)!');
return;
} catch (_) { /* fall through */ }
/* ② GM_setClipboard(Violentmonkey API)*/
try {
GM_setClipboard(html, { type: 'html' });
showAlert('✅ 已複製 (GM)!');
return;
} catch (_) { /* fall through */ }
/* ③ textarea + execCommand (最後保底)*/
try {
const ta = document.createElement('textarea');
ta.value = html;
ta.style.position = 'fixed';
ta.style.left = '-9999px';
document.body.appendChild(ta);
ta.select();
document.execCommand('copy');
document.body.removeChild(ta);
showAlert('✅ 已複製 (fallback)!');
} catch (err) {
showAlert(`❌ 複製失敗:${err.message}`);
}
}
/* ---------- 建立按鈕 ---------- */
function createCopyButton() {
const button = document.createElement('button');
button.textContent = '📋 複製內文';
Object.assign(button.style, {
position: 'fixed',
top: '10px',
right: '10px',
zIndex: 1000,
padding: '6px 10px',
fontSize: '14px',
backgroundColor: '#2b7de9',
color: '#fff',
border: 'none',
borderRadius: '4px',
cursor: 'pointer',
});
button.addEventListener('click', () => {
const selector = 'pre[itemprop="articleBody"]';
waitForElement(selector)
.then(el => copyToClipboard(el.innerHTML)) // 保留 <br> 等 HTML 標籤
.catch(() => showAlert('⚠️ 找不到內文區塊。'));
});
document.body.appendChild(button);
}
/* 🚀 啟動腳本 */
createCopyButton();
})();