// ==UserScript==
// @name One Click Copy Link Button for Twitter(X)
// @namespace http://tampermonkey.net/
// @version 1.0
// @description Add a button to copy the URL of a tweet on Twitter without clicking dropdown. Default to vxtwitter but customizable.
// @author Dinomcworld
// @match https://twitter.com/*
// @match https://mobile.twitter.com/*
// @match https://tweetdeck.twitter.com/*
// @match https://x.com/*
// @icon https://www.google.com/s2/favicons?domain=twitter.com
// @grant none
// @license MIT
// @downloadURL none
// ==/UserScript==
(function() {
'use strict';
const baseUrl = 'https://vxtwitter.com';
const defaultSVG = '';
const copiedSVG = '';
function addCopyButtonToTweets() {
const tweets = document.querySelectorAll('article[data-testid="tweet"]');
tweets.forEach(tweet => {
if (!tweet.querySelector('.custom-copy-icon')) {
const copyIcon = document.createElement('span');
copyIcon.classList.add('custom-copy-icon');
copyIcon.innerHTML = defaultSVG;
adjustIconStyle(tweet, copyIcon);
copyIcon.addEventListener('click', (event) => {
event.stopPropagation();
const tweetUrl = extractTweetUrl(tweet);
if (tweetUrl) {
navigator.clipboard.writeText(tweetUrl)
.then(() => {
console.log('Tweet link copied!');
copyIcon.innerHTML = copiedSVG;
})
.catch(err => console.error('Error copying link: ', err));
}
});
tweet.appendChild(copyIcon);
}
});
}
function extractTweetUrl(tweetElement) {
const linkElement = tweetElement.querySelector('a[href*="/status/"]');
if (!linkElement) {
return;
}
let url = linkElement.getAttribute('href').split('?')[0]; // Remove any query parameters
if (url.includes('/photo/')) {
url = url.split('/photo/')[0];
}
return `${baseUrl}${url}`;
}
function adjustIconStyle(tweet, copyIcon) {
// Find all spans within the tweet
const spans = tweet.querySelectorAll('span');
let isIndividualTweet = false;
spans.forEach(span => {
if (span.textContent.includes("Views")) {
isIndividualTweet = true;
}
});
if (isIndividualTweet) {
copyIcon.style.cssText = 'width: 22px; height: 22px; cursor: pointer; padding: 2px 5px; margin: 2px; position: absolute; bottom: 9px; right: 64px';
} else {
copyIcon.style.cssText = 'width: 19px; height: 19px; cursor: pointer; padding: 2px 5px; margin: 2px; position: absolute; bottom: 9px; right: 72px';
}
}
const observer = new MutationObserver(addCopyButtonToTweets);
observer.observe(document.body, { childList: true, subtree: true });
addCopyButtonToTweets();
})();