您需要先安装一个扩展,例如 篡改猴、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 // @author Your Name // @include * // @match *://*/* // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_registerMenuCommand // @run-at document-end // @license MIT // ==/UserScript== ;(function() { 'use strict'; // —— API 退回实现 —— // 存储 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(resp => resp.text().then(text => opts.onload({ status: resp.status, responseText: text })) ); } else { fetch(opts.url, { method: opts.method, headers: h, body: opts.data }).then(resp => resp.text().then(text => opts.onload({ status: resp.status, responseText: text })) ); } }; // 样式注入 function addStyle(css) { if (typeof GM_addStyle === 'function') { GM_addStyle(css); } else { const s = document.createElement('style'); s.textContent = css; document.head.appendChild(s); } } // 菜单命令(Lemur 不支持时不用) if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand('配置 Gist 参数', openConfigModal); } // 版本号存储键 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; } `); // 主流程 async function mainFlow() { let text = ''; try { text = await navigator.clipboard.readText(); } catch (e) { return alert('请在 HTTPS 环境并授权剪贴板访问'); } if (!text.trim()) { return alert('剪贴板内容为空'); } showEditDialog(text.trim()); } // 在移动端/桌面延迟插入浮动按钮 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(); // 编辑对话框 function showEditDialog(rawText) { const mask = document.createElement('div'); mask.className = 'clip2gist-mask'; const dlg = document.createElement('div'); dlg.className = 'clip2gist-dialog'; // 词块化 const container = document.createElement('div'); rawText.split(/\s+/).forEach(w => { const sp = document.createElement('span'); sp.className = 'clip2gist-word'; sp.textContent = w; sp.addEventListener('click', () => { sp.classList.toggle('selected'); updatePreview(); }); container.appendChild(sp); }); dlg.appendChild(container); // 预览区 const preview = document.createElement('div'); preview.id = 'clip2gist-preview'; dlg.appendChild(preview); // 按钮 const row = document.createElement('div'); ['取消','配置','确认'].forEach(label => { const b = document.createElement('button'); b.textContent = label; if (label==='取消') b.onclick = () => document.body.removeChild(mask); else if (label==='配置') b.onclick = openConfigModal; else b.onclick = onConfirm; row.appendChild(b); }); dlg.appendChild(row); mask.appendChild(dlg); document.body.appendChild(mask); updatePreview(); function updatePreview() { const spans = Array.from(container.children); const segs = []; for (let i=0; i<spans.length;) { if (spans[i].classList.contains('selected')) { const group=[spans[i].textContent], j=i+1; while (j<spans.length && spans[j].classList.contains('selected')) { group.push(spans[j].textContent); j++; } segs.push(`{${group.join(' ')}}`); i=j; } else { segs.push(spans[i].textContent); i++; } } preview.textContent = segs.join(' '); } async function onConfirm() { const gistId = getValue('gistId',''); const token = getValue('githubToken',''); if (!gistId || !token) { return alert('请先配置 Gist ID 与 GitHub Token'); } const ver = getValue(VERSION_KEY,1); const header = `版本 ${ver}`; const content = preview.textContent; // 拉取 httpRequest({ method: 'GET', url: `https://api.github.com/gists/${gistId}`, headers: { Authorization: `token ${token}` }, onload(resp1) { if (resp1.status !== 200) { return alert('拉取 Gist 失败:'+resp1.status); } const data = JSON.parse(resp1.responseText); const fname = Object.keys(data.files)[0]; const old = data.files[fname].content; const updated = `\n\n----\n${header}\n${content}` + old; // 更新 httpRequest({ method: 'PATCH', url: `https://api.github.com/gists/${gistId}`, headers: { Authorization: `token ${token}`, 'Content-Type': 'application/json' }, data: JSON.stringify({ files:{[fname]:{content:updated}} }), onload(resp2) { if (resp2.status === 200) { alert(`上传成功 🎉 已发布版本 ${ver}`); setValue(VERSION_KEY, ver+1); document.body.removeChild(mask); } else { alert('上传失败:'+resp2.status); } } }); } }); } } // 配置对话框 function openConfigModal() { const mask = document.createElement('div'); mask.className = 'clip2gist-mask'; const dlg = document.createElement('div'); dlg.className = 'clip2gist-dialog'; const l1 = document.createElement('label'); l1.textContent = 'Gist ID:'; const i1 = document.createElement('input'); i1.value = getValue('gistId',''); const l2 = document.createElement('label'); l2.textContent = 'GitHub Token:'; const i2 = document.createElement('input'); i2.value = getValue('githubToken',''); dlg.append(l1,i1,l2,i2); const save = document.createElement('button'); save.textContent = '保存'; save.onclick = () => { setValue('gistId', i1.value.trim()); setValue('githubToken', i2.value.trim()); alert('配置已保存'); document.body.removeChild(mask); }; const cancel = document.createElement('button'); cancel.textContent = '取消'; cancel.onclick = () => document.body.removeChild(mask); dlg.append(save,cancel); mask.appendChild(dlg); document.body.appendChild(mask); } })();