Last active
May 13, 2025 12:47
-
-
Save eai04191/d06abacfe198f13db4b143d192817743 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name GitHub | Rich Link Copy | |
// @namespace net.mizle | |
// @version 1.0.0 | |
// @description GitHubのIssue/PRページで番号とタイトルをHTMLリンクとしてコピーする | |
// @match https://github.com/* | |
// @grant GM_registerMenuCommand | |
// @grant GM_addStyle | |
// @grant GM_getResourceText | |
// @require https://cdn.jsdelivr.net/npm/@violentmonkey/shortcut@1 | |
// @require https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.js | |
// @resource Notyf_CSS https://cdn.jsdelivr.net/npm/notyf@3/notyf.min.css | |
// ==/UserScript== | |
// @ts-check | |
// @ts-expect-error Userscript API | |
GM_addStyle(GM_getResourceText("Notyf_CSS")); | |
// @ts-expect-error Userscript API | |
GM_addStyle(` | |
.notyf { | |
color: #1f2328; | |
} | |
`); | |
// @ts-expect-error Notyf API | |
const notyf = new Notyf({ | |
// duration: 3000000, | |
position: { | |
x: "right", | |
y: "top", | |
}, | |
types: [ | |
{ | |
type: "info", | |
background: "#f6f8fa", | |
icon: "", | |
}, | |
{ | |
type: "error", | |
background: "#d73a4a", | |
icon: "", | |
}, | |
{ | |
type: "success", | |
background: "#dafbe1", | |
icon: "", | |
}, | |
], | |
}); | |
/** | |
* @param {number} ms | |
*/ | |
const delay = (ms) => new Promise((r) => setTimeout(r, ms)); | |
/** | |
* @param {ClipboardItem} item | |
* @param {number} ms | |
*/ | |
async function writeToClipboardAfter(item, ms = 500) { | |
await delay(ms); | |
await navigator.clipboard.write([item]); | |
} | |
function isPRPage() { | |
const path = window.location.pathname; | |
return path.includes("/pull/"); | |
} | |
function isIssuePage() { | |
const path = window.location.pathname; | |
return path.includes("/issues/"); | |
} | |
async function copyPageInfo() { | |
if (!isPRPage() && !isIssuePage()) { | |
return; | |
} | |
const loadingNotyf = notyf.open({ | |
type: "info", | |
message: "コピー中...", | |
}); | |
const type = isPRPage() ? "PR" : "Issue"; | |
const headingElement = document.querySelector("h1:has(bdi):has(span)"); | |
if (!headingElement) { | |
alert("Headingが見つかりませんでした。"); | |
return; | |
} | |
// ex: #1234 | |
const prNumber = headingElement.querySelector("span")?.innerText.trim(); | |
const prTitle = headingElement.querySelector("bdi")?.innerText.trim(); | |
const currentUrl = window.location.href; | |
// 表示テキスト | |
const displayText = `${type} ${prNumber} ${prTitle}`; | |
// HTML形式 | |
const htmlText = `<a href="${currentUrl}">${displayText}</a>`; | |
try { | |
const item = new ClipboardItem({ | |
"text/plain": new Blob([displayText], { type: "text/plain" }), | |
"text/html": new Blob([htmlText], { type: "text/html" }), | |
}); | |
await writeToClipboardAfter(item); | |
notyf.dismiss(loadingNotyf); | |
notyf.success("リンクをコピーしました"); | |
} catch (error) { | |
console.error("クリップボードへのコピーに失敗しました:", error); | |
notyf.error("クリップボードへのコピーに失敗しました。", "danger"); | |
} | |
} | |
// コマンドの登録 | |
// @ts-expect-error Userscript API | |
GM_registerMenuCommand("PRリンクをコピー", copyPageInfo); | |
// @ts-expect-error VM.shortcut API | |
VM.shortcut.register( | |
// Windows: ctrl+shift+c | |
// Mac: cmd+shift+c | |
"ctrlcmd-shift-c", | |
() => { | |
copyPageInfo(); | |
} | |
); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment