在后台标签页打开推文

Add a button to open tweets in a background tab

// ==UserScript==
// @name         在后台标签页打开推文
// @namespace    http://tampermonkey.net/
// @version      0.1
// @description  Add a button to open tweets in a background tab
// @match        https://twitter.com/*
// @match        https://x.com/*
// @match        https://mobile.x.com/*
// @match        https://twitter.com/*
// @license      MIT
// @grant        GM_openInTab
// ==/UserScript==

(function() {
    'use strict';

    const addOpenInTabButton = (article) => {
        if (article.dataset.openInTabButtonAdded) return;
        article.dataset.openInTabButtonAdded = 'true';

        const btnGroup = article.querySelector('div[role="group"]:last-of-type');
        if (!btnGroup) return;

        const shareBtn = Array.from(btnGroup.querySelectorAll(':scope>div>div')).pop();
        if (!shareBtn) return;

        const openInTabBtn = shareBtn.cloneNode(true);
        openInTabBtn.querySelector('svg').innerHTML = '<g><path d="M19 19H5V5h7V3H5c-1.11 0-2 .9-2 2v14c0 1.1.89 2 2 2h14c1.1 0 2-.9 2-2v-7h-2v7zM14 3v2h3.59l-9.83 9.83 1.41 1.41L19 6.41V10h2V3h-7z"/></g>';
        openInTabBtn.setAttribute('aria-label', 'Open tweet in background tab');
        openInTabBtn.title = 'Open in background tab';

        openInTabBtn.onclick = (e) => {
            e.preventDefault();
            e.stopPropagation();
            const tweetLink = article.querySelector('a[href*="/status/"]').href;
            GM_openInTab(tweetLink, { active: false, insert: true, setParent: true });
        };

        // Insert the button to the right side of the block
        btnGroup.appendChild(openInTabBtn);
        openInTabBtn.style.marginLeft = '10px';
    };

    const observeTimeline = () => {
        const observer = new MutationObserver((mutations) => {
            for (let mutation of mutations) {
                for (let node of mutation.addedNodes) {
                    if (node.nodeType === Node.ELEMENT_NODE) {
                        const articles = node.tagName === 'ARTICLE' ? [node] : node.querySelectorAll('article');
                        articles.forEach(addOpenInTabButton);
                    }
                }
            }
        });

        observer.observe(document.body, { childList: true, subtree: true });
    };

    // Initial run for existing tweets
    document.querySelectorAll('article').forEach(addOpenInTabButton);

    // Observe for new tweets being added
    observeTimeline();

    // Add custom styles
    const style = document.createElement('style');
    style.textContent = `
        [aria-label="Open tweet in background tab"]:hover {
            color: rgb(29, 155, 240);
        }
    `;
    document.head.appendChild(style);
})();