GitHub Repo Markdown Link Copier

Add a copy button to GitHub repos to copy the repo link in markdown format

// ==UserScript==
// @name         GitHub Repo Markdown Link Copier
// @namespace    https://dvel.me/github-repo-markdown-link-copier
// @version      1.0
// @description  Add a copy button to GitHub repos to copy the repo link in markdown format
// @author       Dvel
// @match        https://github.com/*/*
// @grant        GM_setClipboard
// ==/UserScript==

(function() {
    'use strict';

    // SVG图标
    const copyIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-clipboard" viewBox="0 0 16 16"><path d="M0 6.75C0 5.784.784 5 1.75 5h1.5a.75.75 0 0 1 0 1.5h-1.5a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-1.5a.75.75 0 0 1 1.5 0v1.5A1.75 1.75 0 0 1 9.25 16h-7.5A1.75 1.75 0 0 1 0 14.25Z"></path><path d="M5 1.75C5 .784 5.784 0 6.75 0h7.5C15.216 0 16 .784 16 1.75v7.5A1.75 1.75 0 0 1 14.25 11h-7.5A1.75 1.75 0 0 1 5 9.25Zm1.75-.25a.25.25 0 0 0-.25.25v7.5c0 .138.112.25.25.25h7.5a.25.25 0 0 0 .25-.25v-7.5a.25.25 0 0 0-.25-.25Z"></path></svg>`;
    const checkedIconSVG = `<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check2" viewBox="0 0 16 16"><path d="M13.854 3.646a.5.5 0 0 0-.708 0L7 9.793 3.854 6.646a.5.5 0 1 0-.708.708l3.5 3.5a.5.5 0 0 0 .708 0l6.5-6.5a.5.5 0 0 0 0-.708z"/></svg>`;

    // 使用MutationObserver监听DOM变化
    const observer = new MutationObserver(mutations => {
        mutations.forEach(mutation => {
            if (mutation.addedNodes.length > 0 || mutation.type === 'childList') {
                const navList = document.querySelector('nav[role="navigation"][aria-label="Page context"] ul');
                const existingButton = document.querySelector('#custom-copy-button');
                if (navList && !existingButton) {
                    addCopyButton();
                }
            }
        });
    });

    // 观察器配置:观察子节点的变化
    const config = { childList: true, subtree: true };

    // 监听页面加载事件,然后开始观察DOM变化
    window.addEventListener('load', () => {
        observer.observe(document.body, config);
    });

    function addCopyButton() {
        // 定位到GitHub页面导航栏的ul元素
        const navList = document.querySelector('nav[role="navigation"][aria-label="Page context"] ul');
        if (!navList) return;

        // 获取仓库的用户名和名称
        const pathParts = document.location.pathname.split('/').filter(Boolean);
        const repoFullName = document.location.pathname.substring(1);
        const repoUrl = window.location.href;


        // 创建按钮和设置样式
        const listItem = document.createElement('li');
        listItem.id = 'custom-copy-button'; // 确保按钮唯一性
        const copyButton = document.createElement('button');
        copyButton.innerHTML = `${copyIconSVG} Copy`;
        styleButton(copyButton); // 应用样式

        // 点击按钮复制Markdown链接
        copyButton.onclick = function() {
            const markdownLink = `[${pathParts[0]}/${pathParts[1]}](${repoUrl})`;
            GM_setClipboard(markdownLink);
            copyButton.innerHTML = `${checkedIconSVG} Copied!`;
            setTimeout(() => { copyButton.innerHTML = `${copyIconSVG} copy`; }, 10000);
        };

        listItem.appendChild(copyButton);
        navList.appendChild(listItem);
    }

    // 应用按钮样式,模仿GitHub风格
    function styleButton(button) {
        button.style.padding = '5px 10px';
        button.style.fontSize = '12px';
        button.style.fontWeight = '600';
        button.style.lineHeight = '20px';
        button.style.color = '#24292e';
        button.style.backgroundColor = '#eff3f6';
        button.style.border = '1px solid rgba(27,31,35,.15)';
        button.style.borderRadius = '6px';
        button.style.cursor = 'pointer';
        button.style.marginLeft = '8px';
        button.style.display = 'flex';
        button.style.alignItems = 'center';
        button.style.gap = '5px';

        button.onmouseover = function() {
            this.style.backgroundColor = '#e1e4e8';
        };
        button.onmouseout = function() {
            this.style.backgroundColor = '#eff3f6';
        };
    }
})();