// ==UserScript== // @name Twitter (X) 片思い表示 // @namespace https://kuds.win/ // @version 1.1 // @description Twitter (X) で片思いフォロー中のアカウントのみを表示できます。また、両思いフォロー中のアカウントは強調表示できます。 // @author KUDs // @match https://twitter.com/* // @icon https://www.google.com/s2/favicons?sz=64&domain=twitter.com // @grant none // @license GPL-3.0-or-later // @downloadURL none // ==/UserScript== (function () { "use strict"; // デフォルトは非表示&ハイライト無し let isHiding = false; let isHighlighting = false; // ボタン作成時に共通スタイル適用 function createStyledButton(text, clickHandler) { const button = document.createElement("button"); button.textContent = text; // 共通スタイル Object.assign(button.style, { border: "none", borderRadius: "20px", padding: "10px 20px", marginTop: "10px", cursor: "pointer", transition: "filter 0.3s, transform 0.3s", }); button.addEventListener("mouseover", () => { Object.assign(button.style, { filter: "brightness(1.25)", transform: "scale(1.05)", }); }); button.addEventListener("mouseout", () => { Object.assign(button.style, { filter: "brightness(1)", transform: "scale(1)", }); }); button.addEventListener("click", clickHandler); return button; } // hide button click ハンドラ function handleHideButtonClick() { isHiding = !isHiding; hideButton.textContent = isHiding ? "片思い ONLY: ON" : "片思い ONLY: OFF"; hideButton.style.backgroundColor = isHiding ? "skyblue" : "gray"; updateDOM(); } // highlight button click ハンドラ function handleHighlightButtonClick() { isHighlighting = !isHighlighting; highlightButton.textContent = isHighlighting ? "両思い HIGHLIGHT: ON" : "両思い HIGHLIGHT: OFF"; highlightButton.style.backgroundColor = isHighlighting ? "pink" : "gray"; updateDOM(); } const hideButton = createStyledButton( "片思い ONLY: OFF", handleHideButtonClick ); hideButton.style.backgroundColor = "gray"; const highlightButton = createStyledButton( "両思い HIGHLIGHT: OFF", handleHighlightButtonClick ); highlightButton.style.backgroundColor = "gray"; highlightButton.style.display = "none"; // ボタン配置の初期化 function initializeButtons() { const targetNavElement = document.querySelector( '[data-testid="SideNav_NewTweet_Button"]' ); if (targetNavElement) { const parentNavElement = targetNavElement.parentNode; if (parentNavElement && !hideButton.parentNode) { parentNavElement.appendChild(hideButton); parentNavElement.appendChild(highlightButton); } } } // UserCellの表示/非表示の更新 function updateUserCellVisibility(node) { if ( node.textContent.includes("フォローされています") || node.textContent.includes("両思いです♡") ) { node.style.display = isHiding ? "none" : ""; } } // userFollowIndicatorの表示の更新 function updateFollowIndicator(node) { if ( node.textContent.trim() === "フォローされています" || node.textContent.trim() === "両思いです♡" ) { const spanElement = node.querySelector("span"); if (isHighlighting) { node.style.backgroundColor = "pink"; spanElement.textContent = "両思いです♡"; } else { node.style.backgroundColor = ""; spanElement.textContent = "フォローされています"; } } } // DOMのアップデート function updateDOM(mutationsList = []) { // 既存のDOMノードに対する更新を追加 const userCells = document.querySelectorAll('div[data-testid="UserCell"]'); userCells.forEach(updateUserCellVisibility); const userFollowIndicators = document.querySelectorAll( '[data-testid="userFollowIndicator"]' ); userFollowIndicators.forEach(updateFollowIndicator); for (let mutation of mutationsList) { if (mutation.addedNodes && mutation.addedNodes.length > 0) { mutation.addedNodes.forEach((node) => { // SideNavへのボタン追加 if ( node.matches && node.matches('[data-testid="SideNav_NewTweet_Button"]') ) { initializeButtons(); } // UserCellとuserFollowIndicatorの処理 if (node.matches && node.matches('div[data-testid="UserCell"]')) { updateUserCellVisibility(node); } if ( node.matches && node.matches('[data-testid="userFollowIndicator"]') ) { updateFollowIndicator(node); } }); } } // highlightButtonの表示/非表示 highlightButton.style.display = isHiding ? "none" : ""; // ボタン配置を再確認 initializeButtons(); } const observer = new MutationObserver(updateDOM); observer.observe(document.body, { childList: true, subtree: true, }); // スクリプトの最後で初期化関数を実行 initializeButtons(); })();