您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
One-click clipboard quote → GitHub Gist, with keyword highlighting, versioning & Lemur compatibility
当前为
// ==UserScript== // @name Clip-to-Gist // @name:zh-CN Clip-to-Gist 金句剪贴脚本(v2.3) // @namespace https://github.com/yourusername // @version 2.3 // @description One-click clipboard quote → GitHub Gist, with keyword highlighting, versioning & Lemur compatibility // @description:zh-CN 一键剪贴板金句并上传至 GitHub Gist,支持关键词标注、高亮、版本号,并兼容 Lemur Browser // @include *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_addStyle // ==/UserScript== ;(function() { 'use strict'; // Storage 回退 const setValue = typeof GM_setValue === 'function' ? GM_setValue : (k, v) => localStorage.setItem(k, v); const getValue = typeof GM_getValue === 'function' ? (k, def) => { const v = GM_getValue(k); return v == null ? def : v; } : (k, def) => { const v = localStorage.getItem(k); return v == null ? def : v; }; // HTTP 回退 const httpRequest = typeof GM_xmlhttpRequest === 'function' ? GM_xmlhttpRequest : opts => { const h = opts.headers || {}; if (opts.method === 'GET') { fetch(opts.url, { headers: h }) .then(r => r.text().then(t => opts.onload({ status: r.status, responseText: t }))); } else { fetch(opts.url, { method: opts.method, headers: h, body: opts.data }) .then(r => r.text().then(t => opts.onload({ status: r.status, responseText: t }))); } }; // 样式注入回退 function addStyle(css) { if (typeof GM_addStyle === 'function') GM_addStyle(css); else document.head.appendChild(Object.assign(document.createElement('style'), { textContent: css })); } // 版本号管理 const VERSION_KEY = 'clip2gistVersion'; if (getValue(VERSION_KEY, null) == null) setValue(VERSION_KEY, 1); // 全局样式 addStyle(` #clip2gist-trigger { position:fixed!important;bottom:20px!important;right:20px!important; width:40px;height:40px;line-height:40px;text-align:center; background:#4CAF50;color:#fff;border-radius:50%;cursor:pointer; z-index:2147483647!important;font-size:24px;box-shadow:0 2px 6px rgba(0,0,0,0.3); } .clip2gist-mask { position:fixed;inset:0;background:rgba(0,0,0,0.5); display:flex;align-items:center;justify-content:center;z-index:2147483646; } .clip2gist-dialog { background:#fff;padding:20px;border-radius:8px; max-width:90%;max-height:90%;overflow:auto;box-shadow:0 2px 10px rgba(0,0,0,0.3); } .clip2gist-dialog input { width:100%;padding:6px;margin:4px 0 12px;box-sizing:border-box;font-size:14px; } .clip2gist-dialog button { margin-left:8px;padding:6px 12px;font-size:14px;cursor:pointer; } .clip2gist-word { display:inline-block;margin:2px;padding:4px 6px; border:1px solid #ccc;border-radius:4px;cursor:pointer;user-select:none; } .clip2gist-word.selected { background:#ffeb3b;border-color:#f1c40f; } #clip2gist-preview { margin-top:12px;padding:8px;border:1px solid #ddd; min-height:40px;font-family:monospace; } `); // 插入触发按钮(延迟直到 body 就绪) function insertTrigger() { if (!document.body) return setTimeout(insertTrigger, 100); const btn = document.createElement('div'); btn.id = 'clip2gist-trigger'; btn.textContent = '📝'; btn.addEventListener('click', mainFlow, false); btn.addEventListener('dblclick', openConfigModal, false); document.body.appendChild(btn); } insertTrigger(); // 主逻辑:读剪贴板 → 弹编辑框 async function mainFlow() { let txt = ''; try { txt = await navigator.clipboard.readText(); } catch (e) { return alert('请在 HTTPS 环境并授权剪贴板访问'); } if (!txt.trim()) return alert('剪贴板为空'); showEditor(txt.trim()); } // 编辑/标注框 function showEditor(text) { const mask = document.createElement('div'); mask.className = 'clip2gist-mask'; const dlg = document.createElement('div'); dlg.className = 'clip2gist-dialog'; // 词块化 const wrap = document.createElement('div'); text.split(/\s+/).forEach(w => { const s = document.createElement('span'); s.className = 'clip2gist-word'; s.textContent = w; s.onclick = () => { s.classList.toggle('selected'); update(); }; wrap.appendChild(s); }); dlg.appendChild(wrap); // 预览区 const prev = document.createElement('div'); prev.id = 'clip2gist-preview'; dlg.appendChild(prev); // 按钮 const row = document.createElement('div'); ['取消','配置','确认'].forEach(l => { const b = document.createElement('button'); b.textContent = l; if (l==='取消') b.onclick = () => document.body.removeChild(mask); if (l==='配置') b.onclick = openConfigModal; if (l==='确认') b.onclick = confirmUpload; row.appendChild(b); }); dlg.appendChild(row); mask.appendChild(dlg); document.body.appendChild(mask); update(); // 更新预览 function update() { const spans = Array.from(wrap.children), segs = []; for (let i=0; i<spans.length;) { if (spans[i].classList.contains('selected')) { const grp=[spans[i].textContent], j=i+1; while (j<spans.length && spans[j].classList.contains('selected')) { grp.push(spans[j].textContent); j++; } segs.push(`{${grp.join(' ')}}`); i=j; } else { segs.push(spans[i].textContent); i++; } } prev.textContent = segs.join(' '); } // 上传 function confirmUpload() { const gistId = getValue('gistId',''); const token = getValue('githubToken',''); if (!gistId||!token) return alert('请先配置 Gist ID 与 Token'); const ver = getValue(VERSION_KEY,1); const hdr = `版本 ${ver}`; const cnt = prev.textContent; // GET httpRequest({ method:'GET', url:`https://api.github.com/gists/${gistId}`, headers:{ Authorization:`token ${token}` }, onload(r1) { if (r1.status!==200) return alert('拉取失败:'+r1.status); const data = JSON.parse(r1.responseText); const fn = Object.keys(data.files)[0]; const oldc = data.files[fn].content; const upd = `\n\n----\n${hdr}\n${cnt}`+oldc; // PATCH httpRequest({ method:'PATCH', url:`https://api.github.com/gists/${gistId}`, headers:{ Authorization:`token ${token}`, 'Content-Type':'application/json' }, data:JSON.stringify({ files:{ [fn]:{ content:upd } } }), onload(r2) { if (r2.status===200) { alert(`上传成功 🎉 版本 ${ver}`); setValue(VERSION_KEY, ver+1); document.body.removeChild(mask); } else alert('上传失败:'+r2.status); } }); } }); } } // 配置框 function openConfigModal() { const mask = document.createElement('div'); mask.className='clip2gist-mask'; const dlg = document.createElement('div'); dlg.className='clip2gist-dialog'; const l1 = Object.assign(document.createElement('label'),{ textContent:'Gist ID:' }); const i1 = Object.assign(document.createElement('input'),{ value:getValue('gistId','') }); const l2 = Object.assign(document.createElement('label'),{ textContent:'GitHub Token:' }); const i2 = Object.assign(document.createElement('input'),{ value:getValue('githubToken','') }); dlg.append(l1,i1,l2,i2); const s = document.createElement('button'); s.textContent='保存'; s.onclick = () => { setValue('gistId', i1.value.trim()); setValue('githubToken', i2.value.trim()); alert('配置已保存'); document.body.removeChild(mask); }; const c = document.createElement('button'); c.textContent='取消'; c.onclick = () => document.body.removeChild(mask); dlg.append(s,c); mask.appendChild(dlg); document.body.appendChild(mask); } })();