// ==UserScript== // @name X.com Timeline Auto-Refresh with Accurate Centering // @namespace http://tampermonkey.net/ // @version 4.12 // @description Automatically refreshes the timeline on X.com, setting dual markers (top and bottom) around the last visible post for accurate centering after loading new posts. Includes customizable intervals via Tampermonkey settings. // @icon https://drive.google.com/uc?id=17E9lg_mYtvA66nndCOkfvfalWE1R__b1 // @author Copiis // @match https://x.com/* // @grant GM_setValue // @grant GM_getValue // @grant GM_registerMenuCommand // @license MIT // @contributionURL https://paypal.me/Coopiis?country.x=DE&locale.x=de_DE // @contributionAmount $5 // // @description:de Aktualisiert die Timeline auf X.com automatisch, setzt Marker am Anfang und Ende des letzten sichtbaren Beitrags und zentriert ihn nach dem Neuladen der Beiträge präzise im sichtbaren Bereich. Intervall über Tampermonkey konfigurierbar. // @description:fr Actualise automatiquement la timeline sur X.com, en plaçant des marqueurs en haut et en bas du dernier message visible pour un centrage précis après le chargement de nouveaux messages. Intervalle personnalisable via les paramètres Tampermonkey. // @description:it Aggiorna automaticamente la timeline su X.com, posizionando marcatori nella parte superiore e inferiore dell'ultimo post visibile per centrarlo con precisione dopo il caricamento di nuovi post. Intervallo configurabile tramite le impostazioni di Tampermonkey. // @description:es Actualiza automáticamente la línea de tiempo en X.com, colocando marcadores en la parte superior e inferior de la última publicación visible para centrarla con precisión después de cargar nuevas publicaciones. Intervalo personalizable a través de la configuración de Tampermonkey. // @description:zh 自动刷新X.com的时间线,在最后一个可见帖子顶部和底部设置标记,并在加载新帖子后精确居中。可通过Tampermonkey配置间隔时间。 // @description:ja X.comのタイムラインを自動的に更新し、最後に表示された投稿の上部と下部にマーカーを設定し、新しい投稿の読み込み後に正確に中央揃えします。Tampermonkey設定で間隔を構成可能。 // @downloadURL none // ==/UserScript== (function () { 'use strict'; // Default interval in milliseconds (10 seconds) const DEFAULT_INTERVAL = 10000; // Get the user-defined interval or fall back to the default let refreshInterval = GM_getValue('refreshInterval', DEFAULT_INTERVAL); let intervalId; // Function to set the interval via Tampermonkey menu function configureInterval() { const userInput = prompt( 'Set refresh interval in seconds:', refreshInterval / 1000 ); if (userInput !== null) { const newInterval = parseInt(userInput, 10) * 1000; if (!isNaN(newInterval) && newInterval >= 1000) { GM_setValue('refreshInterval', newInterval); refreshInterval = newInterval; restartInterval(); alert(`Refresh interval set to ${refreshInterval / 1000} seconds.`); } else { alert('Invalid input. Please enter a number greater than or equal to 1.'); } } } // Register Tampermonkey menu command GM_registerMenuCommand('Set Refresh Interval', configureInterval); // Create a marker element function createScrollMarker(id) { const marker = document.createElement('div'); marker.id = id; marker.style.position = 'absolute'; marker.style.width = '1px'; marker.style.height = '1px'; marker.style.backgroundColor = 'transparent'; marker.style.zIndex = '9999'; document.body.appendChild(marker); return marker; } // Place markers at the top and bottom of the last visible post function placeScrollMarkers() { const posts = Array.from(document.querySelectorAll('article')).filter(isElementAccessible); if (posts.length > 0) { const lastVisiblePost = posts[posts.length - 1]; const topMarker = document.getElementById('scroll-marker-top') || createScrollMarker('scroll-marker-top'); const bottomMarker = document.getElementById('scroll-marker-bottom') || createScrollMarker('scroll-marker-bottom'); const postRect = lastVisiblePost.getBoundingClientRect(); // Place the top marker at the top of the post topMarker.style.top = `${window.scrollY + postRect.top}px`; // Place the bottom marker at the bottom of the post bottomMarker.style.top = `${window.scrollY + postRect.bottom}px`; console.log("Scroll markers placed around:", lastVisiblePost); } else { console.warn("No visible posts found to place the scroll markers."); } } // Scroll to the center of the post using precise calculations function scrollToMarkers() { const topMarker = document.getElementById('scroll-marker-top'); const bottomMarker = document.getElementById('scroll-marker-bottom'); if (topMarker && bottomMarker) { const topMarkerRect = topMarker.getBoundingClientRect(); const bottomMarkerRect = bottomMarker.getBoundingClientRect(); // Calculate the exact center of the post const postHeight = bottomMarkerRect.top - topMarkerRect.top; const centerY = topMarkerRect.top + postHeight / 2; // Scroll to center the post window.scrollTo({ top: centerY + window.scrollY - window.innerHeight / 2, behavior: 'smooth' }); console.log("Scrolled to the center of the post."); topMarker.remove(); // Clean up the markers bottomMarker.remove(); } else { console.warn("Markers not found for scrolling."); } } // Helper function to check if an element is visible and not aria-hidden function isElementAccessible(element) { const style = window.getComputedStyle(element); return ( style.display !== 'none' && style.visibility !== 'hidden' && element.getAttribute('aria-hidden') !== 'true' ); } // Find and click the button for new posts function clickShowPostButton() { const buttons = document.querySelectorAll('span.css-1jxf684'); let clicked = false; buttons.forEach(span => { if (span.textContent.includes("Post anzeigen") || span.textContent.includes("neue Posts anzeigen")) { const button = span.closest('button'); if (button) { placeScrollMarkers(); // Place the markers before clicking the button button.click(); console.log(`Clicked button with text: "${span.textContent}"`); clicked = true; // Wait for new posts to load and then scroll to the markers setTimeout(() => { scrollToMarkers(); }, 1000); // Adjust delay if necessary } } }); if (!clicked) { console.warn("No 'Show Posts' button found."); } } // Refresh the timeline and handle scroll restoration function refreshTimeline() { if (window.scrollY === 0) { clickShowPostButton(); // Click the button to refresh and handle marker placement } else { console.log("User is not at the top of the page. Skipping refresh."); } } // Restart the interval with the updated value function restartInterval() { if (intervalId) { clearInterval(intervalId); } intervalId = setInterval(refreshTimeline, refreshInterval); } // Initialize the script function init() { restartInterval(); // Start the interval } init(); })();