Greasy Fork is available in English.
Move the English "Copy link" item above "Send via Chat" in X/Twitter share menus.
// ==UserScript==
// @name X Copy Link First
// @namespace https://greasyfork.org/users/ebisuzawa-kurumi
// @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com
// @version 1.0.0
// @license MIT
//
// @match https://x.com/*
// @match https://twitter.com/*
// @run-at document-idle
// @grant none
//
// @author Ebisuzawa Kurumi
// @description Move the English "Copy link" item above "Send via Chat" in X/Twitter share menus.
// ==/UserScript==
(function () {
"use strict";
const COPY_LINK_TEXT = "copy link";
const SEND_CHAT_TEXT = "send via chat";
function menuItemText(item) {
return item.textContent.trim().toLowerCase();
}
function reorderMenu(menuRoot) {
const items = Array.from(menuRoot.querySelectorAll('[role="menuitem"]'));
const copyLink = items.find((item) => menuItemText(item) === COPY_LINK_TEXT);
const sendChat = items.find((item) => menuItemText(item) === SEND_CHAT_TEXT);
if (!copyLink || !sendChat || copyLink.nextElementSibling === sendChat) {
return;
}
sendChat.parentNode.insertBefore(copyLink, sendChat);
}
function reorderVisibleMenus(root = document) {
const parents = new Set();
const addParent = (item) => {
if (item.parentElement) {
parents.add(item.parentElement);
}
};
if (root.matches?.('[role="menuitem"]')) {
addParent(root);
}
root.querySelectorAll('[role="menuitem"]').forEach(addParent);
parents.forEach(reorderMenu);
}
const observer = new MutationObserver((mutations) => {
for (const mutation of mutations) {
for (const node of mutation.addedNodes) {
if (node.nodeType !== Node.ELEMENT_NODE) {
continue;
}
reorderVisibleMenus(node);
}
}
});
reorderVisibleMenus();
observer.observe(document.body, { childList: true, subtree: true });
})();