X Copy Link First

Move the English "Copy link" item above "Send via Chat" in X/Twitter share menus.

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==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 });
})();