// ==UserScript== // @name X.com Timeline Auto-Load with Uninterrupted Reading // @name:de X.com Timeline Auto-Load mit unterbrechungsfreiem Lesen // @name:fr X.com Timeline Auto-Load avec lecture ininterrompue // @name:es Carga automática de la línea de tiempo de X.com con lectura sin interrupciones // @name:it Caricamento automatico della timeline di X.com con lettura ininterrotta // @name:zh X.com 时间线自动加载,无缝阅读 // @name:ja X.com タイムライン自動読み込みと中断のない読書 // @namespace http://tampermonkey.net/ // @version 4.3 // @description Automatically loads new posts on X.com while keeping the reading position intact. Sets a virtual marker at the last visible handler (e.g., @username) before loading new posts and restores the view to this marker. // @description:de Lädt automatisch neue Beiträge auf X.com, ohne die Leseposition zu verlieren. Setzt eine virtuelle Markierung am letzten sichtbaren Handler (z. B. @Benutzername) vor dem Laden neuer Beiträge und stellt die Ansicht zu dieser Markierung wieder her. // @description:fr Charge automatiquement les nouveaux messages sur X.com tout en conservant la position de lecture. Place un marqueur virtuel au dernier handle visible (par exemple, @nomutilisateur) avant de charger les nouveaux messages et restaure la vue à ce marqueur. // @description:es Carga automáticamente nuevos posts en X.com mientras mantiene la posición de lectura intacta. Coloca un marcador virtual en el último manejador visible (por ejemplo, @nombredeusuario) antes de cargar nuevos posts y restaura la vista a ese marcador. // @description:it Carica automaticamente nuovi post su X.com mantenendo intatta la posizione di lettura. Imposta un segnalibro virtuale sull'ultimo handle visibile (es. @nomeutente) prima di caricare nuovi post e ripristina la vista su quel segnalibro. // @description:zh 在X.com上自动加载新帖子,同时保持阅读位置不变。在加载新帖子之前,在最后一个可见的处理器(例如@用户名)处设置一个虚拟标记,并将视图恢复到该标记。 // @description:ja X.comで新しい投稿を自動的に読み込み、読書位置をそのまま保持します。新しい投稿を読み込む前に、最後に見えるハンドル(例:@ユーザー名)に仮想マーカーを設定し、このマーカーにビューを復元します。 // @icon https://drive.google.com/file/d/1MaHF2JyG7qv6NDe6T2qoDDKNjiYOXx_o/view?usp=sharing // @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 // @downloadURL none // ==/UserScript== (function () { 'use strict'; let markerId = "last-visible-handler-marker"; // Funktion: Markierung beim letzten sichtbaren Handler setzen function setMarker() { const handlers = Array.from(document.querySelectorAll('span')).filter( (span) => span.textContent.startsWith("@") ); if (handlers.length > 0) { const lastHandler = handlers[handlers.length - 1]; // Letzter sichtbarer Handler // Existierende Markierung entfernen removeMarker(); // Markierung erstellen const marker = document.createElement('div'); marker.id = markerId; const rect = lastHandler.getBoundingClientRect(); const markerTop = window.scrollY + rect.bottom; // Unterhalb des letzten Handlers marker.style.position = 'absolute'; marker.style.top = `${markerTop}px`; marker.style.width = '1px'; marker.style.height = '1px'; marker.style.backgroundColor = 'transparent'; // Unsichtbar marker.style.zIndex = '-1'; document.body.appendChild(marker); console.log(`Markierung gesetzt bei Y-Position: ${markerTop}`); } else { console.warn("Kein sichtbarer Handler gefunden, Markierung konnte nicht gesetzt werden."); } } // Funktion: Markierung entfernen function removeMarker() { const existingMarker = document.getElementById(markerId); if (existingMarker) { existingMarker.remove(); console.log("Markierung entfernt."); } } // Funktion: Zur Markierung scrollen function scrollToMarker() { const marker = document.getElementById(markerId); if (marker) { const markerTop = parseInt(marker.style.top, 10); // Y-Position der Markierung window.scrollTo({ top: markerTop, behavior: 'smooth' // Sanft scrollen }); // Verifiziere das Scrollen setTimeout(() => { if (Math.abs(window.scrollY - markerTop) <= 5) { console.log("Erfolgreich zur Markierung gescrollt:", markerTop); } else { console.warn( `Scrollen zur Markierung ungenau. Erwartet: ${markerTop}, Tatsächlich: ${window.scrollY}` ); } }, 500); // Warte kurz, um die Scrollposition zu überprüfen removeMarker(); // Markierung nach Verwendung löschen } else { console.warn("Keine Markierung gefunden, konnte nicht scrollen."); } } // Funktion: Klick auf die "Neue Beiträge anzeigen"-Schaltfläche function clickNewPostsButton() { const button = Array.from(document.querySelectorAll('span')).find( (span) => span.textContent.includes("neue Posts anzeigen") || span.textContent.includes("Post anzeigen") ); if (button) { const parentButton = button.closest('button'); if (parentButton) { console.log(`Schaltfläche "${button.textContent}" gefunden, wird geklickt...`); setMarker(); // Markierung setzen vor dem Laden neuer Beiträge parentButton.click(); // Starte Scrollen nach einer kurzen Verzögerung setTimeout(scrollToMarker, 1000); } else { console.warn("Elternelement der Schaltfläche nicht gefunden."); } } else { console.warn("Keine Schaltfläche für neue Beiträge gefunden."); } } // Funktion: DOM-Änderungen überwachen function observeNewPostsButton() { const targetNode = document.body; const config = { childList: true, subtree: true }; const callback = function (mutationsList) { for (const mutation of mutationsList) { if (mutation.type === 'childList') { clickNewPostsButton(); // Versuche die Schaltfläche zu klicken } } }; const observer = new MutationObserver(callback); observer.observe(targetNode, config); console.log("MutationObserver gestartet, um die Schaltfläche zu überwachen."); } // Initialisieren function init() { console.log("Skript zur Überprüfung neuer Beiträge aktiviert..."); observeNewPostsButton(); } init(); })();