Last active
October 2, 2026 18:34
-
-
Save ZhangYiJiang/8b70e23f4facdfc48aff2c3c19382731 to your computer and use it in GitHub Desktop.
Copy PR stack as Markdown
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 Copy PR stack as Markdown | |
| // @namespace https://github.com/ | |
| // @version 1.0 | |
| // @description Adds a button to the GitHub stack overlay that copies the stacked PRs as a Markdown list of links | |
| // @match https://github.com/* | |
| // @grant GM_setClipboard | |
| // ==/UserScript== | |
| (function () { | |
| 'use strict'; | |
| const BUTTON_ID = 'copy-pr-stack-md'; | |
| function escapeLinkText(text) { | |
| return text.replace(/\[/g, '(').replace(/\]/g, ')'); | |
| } | |
| function stackToMarkdown(nav) { | |
| return [...nav.querySelectorAll('a[href*="/pull/"]')] | |
| .map((a) => { | |
| const title = a.querySelector('[data-component="ActionList.Item.Label"]')?.textContent.trim(); | |
| const number = new URL(a.href).pathname.split('/').pop(); | |
| return `- [#${number} ${escapeLinkText(title)}](${a.href})`; | |
| }) | |
| .join('\n'); | |
| } | |
| function copy(text) { | |
| if (typeof GM_setClipboard === 'function') { | |
| GM_setClipboard(text, 'text'); | |
| return Promise.resolve(); | |
| } | |
| return navigator.clipboard.writeText(text); | |
| } | |
| function addButton(actions) { | |
| if (actions.querySelector(`#${BUTTON_ID}`)) return; | |
| const button = document.createElement('button'); | |
| button.id = BUTTON_ID; | |
| button.type = 'button'; | |
| button.textContent = 'Copy as Markdown'; | |
| button.className = 'btn btn-sm'; | |
| button.addEventListener('click', async () => { | |
| const nav = actions.parentElement.parentElement.querySelector('nav[data-component="NavList"]'); | |
| const markdown = nav && stackToMarkdown(nav); | |
| if (!markdown) return; | |
| await copy(markdown); | |
| button.textContent = 'Copied!'; | |
| setTimeout(() => (button.textContent = 'Copy as Markdown'), 1500); | |
| }); | |
| actions.prepend(button); | |
| } | |
| function scan() { | |
| document | |
| .querySelectorAll('[class*="StackState-module__overlayHeaderActions"]') | |
| .forEach(addButton); | |
| } | |
| new MutationObserver(scan).observe(document.body, { childList: true, subtree: true }); | |
| scan(); | |
| })(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment