您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
共享选定文本到Gist并粘贴到剪贴板
当前为
// ==UserScript== // @name Gist Shared Clipboard // @name:ja Gist 共有クリップボード // @name:zh-CN Gist 共享剪贴板 // @name:zh-TW Gist 共享剪貼簿 // @description Share selected text to Gist and paste it to clipboard // @description:ja Gistに選択したテキストを共有し、クリップボードに貼り付ける // @description:zh-CN 共享选定文本到Gist并粘贴到剪贴板 // @description:zh-TW 共享選定文本到Gist並粘貼到剪貼簿 // @license MIT // @namespace http://tampermonkey.net/ // @version 2025-05-20 // @description Share selected text to Gist and paste it to clipboard // @author Julia Lee // @match *://*/* // @icon https://www.google.com/s2/favicons?sz=64&domain=tampermonkey.net // @grant GM_registerMenuCommand // @grant GM_setValue // @grant GM_getValue // @grant GM_deleteValue // @grant GM_setClipboard // ==/UserScript== (async function () { 'use strict'; const GITHUB_TOKEN = await GM.getValue('GITHUB_TOKEN', ''); // GitHubのPersonal Access Tokenを指定 const GIST_ID = await GM.getValue('GIST_ID', ''); // GistのIDを指定 const FILENAME = 'GM-Shared-Clipboard.txt'; // Gist内のファイル名 await GM.deleteValue('GIST_DOWNLOADING'); await GM.deleteValue('GIST_UPLOADING'); let crtRightTgtContent = null; let crtRightTgtUpdated = 0; if (GITHUB_TOKEN && GIST_ID) { const menu1 = GM_registerMenuCommand("Gist Share", gistUpload, { accessKey: 'c', autoClose: true, title: 'Share selected text to Gist', }); const menu2 = GM_registerMenuCommand("Gist Paste", gistDowload, { accessKey: 'v', autoClose: true, title: 'Paste Gist content to clipboard', }); } const menu3 = GM_registerMenuCommand("Gist Setup", setup, { accessKey: 's', autoClose: true, title: 'Setup Gist ID and Token', }); if (GITHUB_TOKEN && GIST_ID) { const menu4 = GM_registerMenuCommand("Gist Clear", async () =>{ await GM.deleteValue('GITHUB_TOKEN'); await GM.deleteValue('GIST_ID'); setTimeout(() => { location.reload() }, 2500); // Restart Script showMessage('✅ Gist ID and Token cleared!', 'OK', 2500); }, { // accessKey: 'x', autoClose: true, title: 'Clear Gist ID and Token', }); } document.body.addEventListener("mousedown", event => { if (event.button == 0) { // left click for mouse // crtRightTgtContent = null; } else if (event.button == 1) { // wheel click for mouse // crtRightTgtContent = null; } else if (event.button == 2) { // right click for mouse const elm = event.target; const nodName = elm.nodeName.toLowerCase(); switch (nodName) { case 'img': crtRightTgtContent = elm.src; break; case 'a': crtRightTgtContent = elm.href; break; default: crtRightTgtContent = null; break; } if (crtRightTgtContent) { crtRightTgtUpdated = new Date(); } } }); const gistUrl = `https://api.github.com/gists/${GIST_ID}`; const headers = { 'Authorization': `Bearer ${GITHUB_TOKEN}`, 'Content-Type': 'application/json', }; async function gistUpload(_event) { // If the target is too old, reset it if (crtRightTgtContent && (new Date()) - crtRightTgtUpdated > 30*1000) { crtRightTgtContent = null; // crtRightTgtUpdated = 0; } const selectedText = document.getSelection().toString(); if (!crtRightTgtContent && !selectedText) { return } const locked = await GM.getValue('GIST_UPLOADING'); if (locked) { console.log("Gist is already uploading."); return; } const data = { files: { [FILENAME]: { content: selectedText || crtRightTgtContent } } }; try { await GM.setValue('GIST_UPLOADING', true); const res = await fetch(gistUrl, { method: 'POST', headers, body: JSON.stringify(data) }); if (!res.ok) { const error = await res.json(); throw new Error(`Failed to update Gist: ${error.message}`); } const result = await res.json(); await GM.setClipboard(result.html_url, "text") await showMessage('✅ Target Shared!', 'OK', 2500); } catch (error) { console.error("Error: ", error); await showMessage(`❌ ${error.message}`, 'NG', 2500); } finally { await GM.deleteValue('GIST_UPLOADING'); } } async function gistDowload(_event) { if (inIframe()) { console.log("Gist Paste is not available in iframe."); return; } const locked = await GM.getValue('GIST_DOWNLOADING'); if (locked) { console.log("Gist is already Downloading."); return; } try { await GM.setValue('GIST_DOWNLOADING', true); const res = await fetch(gistUrl, { headers }); if (!res.ok) { const error = await res.json(); throw new Error(`Failed to fetch Gist: ${error.message}`); } const result = await res.json(); const content = result.files[FILENAME].content; if (!content) { throw new Error('No content found in the Gist.'); } await GM.setClipboard(content, "text"); console.log("Gist Content: ", content); await showMessage('✅ Clipboard Updated!', 'OK', 2500); } catch (error) { console.error("Error: ", error); await showMessage(`❌ ${error.message}`, 'NG', 2500); } finally { await GM.deleteValue('GIST_DOWNLOADING'); } } async function setup() { if (inIframe()) { console.log("Gist Setup is not available in iframe."); return; } const dialog = await createRegisterDialog(); dialog.showModal(); const button = document.getElementById('save-button'); const input = document.getElementById('gist-id-input'); const input2 = document.getElementById('gist-token-input'); button.addEventListener('click', async () => { const gistId = input.value; const token = input2.value; if (!gistId || !token) { await showMessage('❌ Gist ID and Token are required!', 'NG', 2500); return; } await GM.setValue('GIST_ID', gistId); await GM.setValue('GITHUB_TOKEN', token); dialog.close(); dialog.remove(); setTimeout(() => { location.reload() }, 2500); // Restart Script await showMessage('✅ Gist ID and Token saved!', 'OK', 2500); }); } })(); async function showMessage(text, type = 'OK', duration = 4000) { const htmlId = `GistShare_Message-${type}`; const existingMessage = document.getElementById(htmlId); if (existingMessage) { return; } // 既に表示されている場合は何もしない if (duration < 1000) { duration = 1000; } // 最低1秒は表示する return new Promise((resolve) => { const message = document.createElement('div'); message.id = `GistShare_Message-${type}`; message.textContent = text; // 共通スタイル Object.assign(message.style, { position: 'fixed', top: '20px', right: '20px', padding: '12px 18px', borderRadius: '10px', color: '#fff', fontSize: '14px', boxShadow: '0 4px 12px rgba(0, 0, 0, 0.3)', zIndex: 9999, transform: 'translateY(20px)', opacity: '0', transition: 'opacity 0.4s ease, transform 0.4s ease' }); // タイプ別デザイン if (type === 'OK') { message.style.backgroundColor = '#4caf50'; // 緑 message.style.borderLeft = '6px solid #2e7d32'; } else if (type === 'NG') { message.style.backgroundColor = '#f44336'; // 赤 message.style.borderLeft = '6px solid #b71c1c'; } document.body.appendChild(message); // フェードイン(下から) setTimeout(() => { message.style.opacity = '.95'; message.style.transform = 'translateY(0)'; }, 10); // requestAnimationFrame(() => { // message.style.opacity = '1'; // message.style.transform = 'translateY(0)'; // }); // 指定時間後にフェードアウト setTimeout(() => { message.style.opacity = '0'; message.style.transform = 'translateY(-20px)'; setTimeout(() => { message.remove(); resolve(); // メッセージが削除された後にresolveを呼び出す }, 400); // transition と一致 }, duration - 400); }); } async function createRegisterDialog() { const existing = document.getElementById('tm-gist-dialog'); if (existing) existing.remove(); const dialog = document.createElement('dialog'); dialog.id = 'tm-gist-dialog'; dialog.style.padding = '1em'; dialog.style.zIndex = 9999; const label = document.createElement('label'); label.textContent = 'Gist ID:'; label.style.display = 'block'; label.style.marginBottom = '0.5em'; label.for = 'gist-id-input'; dialog.appendChild(label); const input = document.createElement('input'); input.id = 'gist-id-input'; input.type = 'text'; input.style.width = '100%'; input.style.boxSizing = 'border-box'; input.style.padding = '0.5em'; input.style.border = '1px solid #ccc'; input.style.borderRadius = '4px'; input.style.marginBottom = '1em'; input.value = await GM.getValue('GIST_ID', ''); input.placeholder = 'Your Gist ID'; dialog.appendChild(input); const small = document.createElement('small'); small.style.display = 'block'; small.style.marginBottom = '1.1em'; small.style.color = '#666'; const span = document.createElement('span'); span.textContent = 'Create or Select a Gist: '; small.appendChild(span); const a = document.createElement('a'); a.href = 'https://gist.github.com/mine'; a.target = '_blank'; a.textContent = 'https://gist.github.com/'; small.appendChild(a); dialog.appendChild(small); const label2 = document.createElement('label'); label2.textContent = 'Gist Token:'; label2.style.display = 'block'; label2.style.marginBottom = '0.5em'; label2.for = 'gist-token-input'; dialog.appendChild(label2); const input2 = document.createElement('input'); input2.id = 'gist-token-input'; input2.type = 'password'; input2.style.width = '100%'; input2.style.boxSizing = 'border-box'; input2.style.padding = '0.5em'; input2.style.border = '1px solid #ccc'; input2.style.borderRadius = '4px'; input2.style.marginBottom = '1em'; input2.value = await GM.getValue('GITHUB_TOKEN', ''); input2.placeholder = 'ghp_XXXXXXXXXXXXXXXX'; dialog.appendChild(input2); const small2 = document.createElement('small'); small2.style.display = 'block'; small2.style.marginBottom = '1em'; small2.style.color = '#666'; const span2 = document.createElement('span'); span2.textContent = 'Create a Token: '; small2.appendChild(span2); const a2 = document.createElement('a'); a2.href = 'https://github.com/settings/tokens'; a2.target = '_blank'; a2.textContent = 'https://github.com/settings/tokens'; small2.appendChild(a2); dialog.appendChild(small2); const button = document.createElement('button'); button.textContent = 'Save Info'; button.style.backgroundColor = '#4caf50'; button.style.color = '#fff'; button.style.border = 'none'; button.style.padding = '0.5em 1em'; button.style.borderRadius = '4px'; button.style.cursor = 'pointer'; button.style.marginTop = '1em'; button.style.float = 'right'; button.id = 'save-button'; dialog.appendChild(button); document.body.appendChild(dialog); return dialog; } function inIframe() { try { return window.self !== window.top; } catch (e) { return true; } }