您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
One-click clipboard quotes → GitHub Gist, with keyword highlighting, versioning & Lemur Browser compatibility
当前为
// ==UserScript== // @name Clip-to-Gist Quote Script (Lemur Compatible) // @namespace http://tampermonkey.net/ // @version 2.3 // @description One-click clipboard quotes → GitHub Gist, with keyword highlighting, versioning & Lemur Browser compatibility // @author Your Name // @match *://*/* // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw== // @grant GM_setValue // @grant GM_getValue // @grant GM_xmlhttpRequest // @grant GM_addStyle // @grant GM_registerMenuCommand // @connect api.github.com // @run-at document-end // ==/UserScript== (function(){ 'use strict'; // Fallback implementations const setValue = typeof GM_setValue === 'function' ? GM_setValue : (key, val) => localStorage.setItem(key, val); const getValue = typeof GM_getValue === 'function' ? (key, def) => { const v = GM_getValue(key); return v == null ? def : v; } : (key, def) => { const v = localStorage.getItem(key); return v == null ? def : v; }; const httpRequest = typeof GM_xmlhttpRequest === 'function' ? GM_xmlhttpRequest : opts => { const headers = opts.headers || {}; if (opts.method === 'GET') { fetch(opts.url, { headers }) .then(res => res.text().then(text => opts.onload({ status: res.status, responseText: text }))); } else { fetch(opts.url, { method: opts.method, headers, body: opts.data }) .then(res => res.text().then(text => opts.onload({ status: res.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); } } // Version management const VERSION_KEY = 'clip2gistVersion'; if (getValue(VERSION_KEY, null) == null) { setValue(VERSION_KEY, 1); } // Global CSS 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; } `); // If supported, register menu command if (typeof GM_registerMenuCommand === 'function') { GM_registerMenuCommand('Configure Gist', openConfigDialog); } // Insert floating trigger button 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', openConfigDialog, false); document.body.appendChild(btn); } insertTrigger(); // Main flow: read clipboard, show editor async function mainFlow() { let text = ''; try { text = await navigator.clipboard.readText(); } catch (e) { return alert('Please use HTTPS and allow clipboard access'); } if (!text.trim()) { return alert('Clipboard is empty'); } showEditor(text.trim()); } // Editor dialog function showEditor(rawText) { const mask = document.createElement('div'); mask.className = 'clip2gist-mask'; const dlg = document.createElement('div'); dlg.className = 'clip2gist-dialog'; // Word-by-word spans const container = document.createElement('div'); rawText.split(/\s+/).forEach(word => { const span = document.createElement('span'); span.className = 'clip2gist-word'; span.textContent = word; span.onclick = () => { span.classList.toggle('selected'); updatePreview(); }; container.appendChild(span); }); dlg.appendChild(container); // Preview area const preview = document.createElement('div'); preview.id = 'clip2gist-preview'; dlg.appendChild(preview); // Buttons row const row = document.createElement('div'); ['Cancel', 'Configure', 'Confirm'].forEach(label => { const btn = document.createElement('button'); btn.textContent = label; if (label === 'Cancel') btn.onclick = () => document.body.removeChild(mask); if (label === 'Configure') btn.onclick = openConfigDialog; if (label === 'Confirm') btn.onclick = confirmUpload; row.appendChild(btn); }); dlg.appendChild(row); mask.appendChild(dlg); document.body.appendChild(mask); updatePreview(); // Build preview string function updatePreview() { const spans = Array.from(container.children); const parts = []; for (let i = 0; i < spans.length;) { if (spans[i].classList.contains('selected')) { const group = [spans[i].textContent]; let j = i + 1; while (j < spans.length && spans[j].classList.contains('selected')) { group.push(spans[j].textContent); j++; } parts.push(`{${group.join(' ')}}`); i = j; } else { parts.push(spans[i].textContent); i++; } } preview.textContent = parts.join(' '); } // Confirm upload function confirmUpload() { const gistId = getValue('gistId', ''); const token = getValue('githubToken', ''); if (!gistId || !token) { return alert('Please configure Gist ID and GitHub Token first'); } const ver = getValue(VERSION_KEY, 1); const header = `Version ${ver}`; const content = preview.textContent; // GET existing gist httpRequest({ method: 'GET', url: `https://api.github.com/gists/${gistId}`, headers: { Authorization: `token ${token}` }, onload(res1) { if (res1.status !== 200) { return alert('Failed to fetch Gist: ' + res1.status); } const data = JSON.parse(res1.responseText); const file = Object.keys(data.files)[0]; const oldContent = data.files[file].content; const updated = `\n\n----\n${header}\n${content}` + oldContent; // PATCH update httpRequest({ method: 'PATCH', url: `https://api.github.com/gists/${gistId}`, headers: { Authorization: `token ${token}`, 'Content-Type': 'application/json' }, data: JSON.stringify({ files: { [file]: { content: updated } } }), onload(res2) { if (res2.status === 200) { alert(`Upload successful! Version ${ver}`); setValue(VERSION_KEY, ver + 1); document.body.removeChild(mask); } else { alert('Failed to update Gist: ' + res2.status); } } }); } }); } } // Configuration dialog function openConfigDialog() { const mask = document.createElement('div'); mask.className = 'clip2gist-mask'; const dlg = document.createElement('div'); dlg.className = 'clip2gist-dialog'; const label1 = document.createElement('label'); label1.textContent = 'Gist ID:'; const input1 = document.createElement('input'); input1.value = getValue('gistId', ''); const label2 = document.createElement('label'); label2.textContent = 'GitHub Token:'; const input2 = document.createElement('input'); input2.value = getValue('githubToken', ''); dlg.append(label1, input1, label2, input2); const saveBtn = document.createElement('button'); saveBtn.textContent = 'Save'; saveBtn.onclick = () => { setValue('gistId', input1.value.trim()); setValue('githubToken', input2.value.trim()); alert('Configuration saved'); document.body.removeChild(mask); }; const cancelBtn = document.createElement('button'); cancelBtn.textContent = 'Cancel'; cancelBtn.onclick = () => document.body.removeChild(mask); dlg.append(saveBtn, cancelBtn); mask.appendChild(dlg); document.body.appendChild(mask); } })();