Twitter/X Timeline Sync

Tracks and syncs your last reading position on Twitter/X, with manual and automatic options. Ideal for keeping track of new posts without losing your place.

Zainstaluj skrypt?
Skrypt zaproponowany przez autora

Może Ci się również spodobać. YouTube Video Hider with 🚫 Icon (Transparent)

Zainstaluj skrypt
// ==UserScript==
// @name              Twitter/X Timeline Sync
// @description       Tracks and syncs your last reading position on Twitter/X, with manual and automatic options. Ideal for keeping track of new posts without losing your place.
// @description:de    Verfolgt und synchronisiert Ihre letzte Leseposition auf Twitter/X, mit manuellen und automatischen Optionen. Perfekt, um neue Beiträge im Blick zu behalten, ohne die aktuelle Position zu verlieren.
// @description:es    Rastrea y sincroniza tu última posición de lectura en Twitter/X, con opciones manuales y automáticas. Ideal para mantener el seguimiento de las publicaciones nuevas sin perder tu posición.
// @description:fr    Suit et synchronise votre dernière position de lecture sur Twitter/X, avec des options manuelles et automatiques. Idéal pour suivre les nouveaux posts sans perdre votre place actuelle.
// @description:zh-CN 跟踪并同步您在 Twitter/X 上的最后阅读位置,提供手动和自动选项。完美解决在查看新帖子时不丢失当前位置的问题。
// @description:ru    Отслеживает и синхронизирует вашу последнюю позицию чтения на Twitter/X с ручными и автоматическими опциями. Идеально подходит для просмотра новых постов без потери текущей позиции。
// @description:ja    Twitter/X での最後の読み取り位置を追跡して同期します。手動および自動オプションを提供します。新しい投稿を見逃さずに現在の位置を維持するのに最適です。
// @description:pt-BR Rastrea e sincroniza sua última posição de leitura no Twitter/X, com opções manuais e automáticas. Perfeito para acompanhar novos posts sem perder sua posição atual。
// @description:hi    Twitter/X पर आपकी अंतिम पठन स्थिति को ट्रैक और सिंक करता है, मैनुअल और स्वचालित विकल्पों के साथ। नई पोस्ट देखते समय अपनी वर्तमान स्थिति को खोए बिना इसे ट्रैक करें।
// @description:ar    يتتبع ويزامن آخر موضع قراءة لك على Twitter/X، مع خيارات يدوية وتلقائية. مثالي لتتبع المشاركات الجديدة دون فقدان موضعك الحالي。
// @description:it    Traccia e sincronizza la tua ultima posizione di lettura su Twitter/X, con opzioni manuali e automatiche. Ideale per tenere traccia dei nuovi post senza perdere la posizione attuale。
// @description:ko    Twitter/X에서 마지막 읽기 위치를 추적하고 동기화합니다. 수동 및 자동 옵션 포함. 새로운 게시물을 확인하면서 현재 위치를 잃지 않도록 이상적입니다。
// @icon              https://x.com/favicon.ico
// @namespace         http://tampermonkey.net/
// @version           2025.6.2.3.2
// @author            Copiis
// @license           MIT
// @match             https://x.com/home
// @grant             GM_setValue
// @grant             GM_getValue
// @grant             GM_registerMenuCommand
// ==/UserScript==
//                    If you find this script useful and would like to support my work, consider making a small donation!
//                    Bitcoin (BTC): bc1quc5mkudlwwkktzhvzw5u2nruxyepef957p68r7
//                    PayPal: https://www.paypal.com/paypalme/Coopiis?country.x=DE&locale.x=de_DE

(function () {
    'use strict';
 'use strict';

 // Übersetzungen für alle Popup-Nachrichten
 const translations = {
     en: {
         noValidPosition: "❌ No valid reading position to download.",
         alreadyDownloaded: "ℹ️ This reading position has already been downloaded.",
         downloadSuccess: "✅ Reading position downloaded as {fileName}.",
         downloadFailed: "❌ Download failed. Reading position copied to clipboard. Please paste it into a .json file manually.",
         downloadClipboardFailed: "❌ Download and clipboard copy failed. Please save manually.",
         noPositionFound: "ℹ️ Scroll to set a reading position.",
         scriptError: "❌ Error loading the script.",
         invalidPosition: "❌ Invalid reading position.",
         fileSelectError: "❌ Please select a JSON file.",
         fileReadError: "❌ Error reading the file.",
         fileDialogError: "❌ Error opening file dialog.",
         fileLoadSuccess: "✅ Reading position successfully loaded!",
         buttonsError: "❌ Error displaying buttons.",
         searchPopup: "🔍 Searching... Press SPACE to cancel.",
         searchNoPosition: "❌ No reading position available.",
         searchScrollPrompt: "ℹ️ Please scroll or click the magnifier."
     },
     de: {
         noValidPosition: "❌ Keine gültige Leseposition zum Downloaden.",
         alreadyDownloaded: "ℹ️ Diese Leseposition wurde bereits heruntergeladen.",
         downloadSuccess: "✅ Leseposition als {fileName} heruntergeladen.",
         downloadFailed: "❌ Download fehlgeschlagen. Leseposition wurde in die Zwischenablage kopiert. Bitte manuell in eine .json-Datei einfügen.",
         downloadClipboardFailed: "❌ Download und Kopieren fehlgeschlagen. Bitte manuell speichern.",
         noPositionFound: "ℹ️ Scrolle, um eine Leseposition zu setzen.",
         scriptError: "❌ Fehler beim Laden des Skripts.",
         invalidPosition: "❌ Ungültige Leseposition.",
         fileSelectError: "❌ Bitte wähle eine JSON-Datei aus.",
         fileReadError: "❌ Fehler beim Lesen der Datei.",
         fileDialogError: "❌ Fehler beim Öffnen des Datei-Dialogs.",
         fileLoadSuccess: "✅ Leseposition erfolgreich geladen!",
         buttonsError: "❌ Fehler beim Anzeigen der Buttons.",
         searchPopup: "🔍 Suche läuft... Drücke LEERTASTE zum Abbrechen.",
         searchNoPosition: "❌ Keine Leseposition vorhanden.",
         searchScrollPrompt: "ℹ️ Bitte scrollen oder Lupe klicken."
     },
     es: {
         noValidPosition: "❌ No hay posición de lectura válida para descargar.",
         alreadyDownloaded: "ℹ️ Esta posición de lectura ya ha sido descargada.",
         downloadSuccess: "✅ Posición de lectura descargada como {fileName}.",
         downloadFailed: "❌ Falló la descarga. La posición de lectura se copió al portapapeles. Pégala manualmente en un archivo .json.",
         downloadClipboardFailed: "❌ Falló la descarga y la copia al portapapeles. Por favor, guarda manualmente.",
         noPositionFound: "ℹ️ Desplázate para establecer una posición de lectura.",
         scriptError: "❌ Error al cargar el script.",
         invalidPosition: "❌ Posición de lectura no válida.",
         fileSelectError: "❌ Por favor, selecciona un archivo JSON.",
         fileReadError: "❌ Error al leer el archivo.",
         fileDialogError: "❌ Error al abrir el diálogo de archivo.",
         fileLoadSuccess: "✅ ¡Posición de lectura cargada con éxito!",
         buttonsError: "❌ Error al mostrar los botones.",
         searchPopup: "🔍 Buscando... Presiona ESPACIO para cancelar.",
         searchNoPosition: "❌ No hay posición de lectura disponible.",
         searchScrollPrompt: "ℹ️ Por favor, desplázate o haz clic en la lupa."
     },
     fr: {
         noValidPosition: "❌ Aucune position de lecture valide à télécharger.",
         alreadyDownloaded: "ℹ️ Cette position de lecture a déjà été téléchargée.",
         downloadSuccess: "✅ Position de lecture téléchargée sous {fileName}.",
         downloadFailed: "❌ Échec du téléchargement. Position de lecture copiée dans le presse-papiers. Veuillez la coller manuellement dans un fichier .json.",
         downloadClipboardFailed: "❌ Échec du téléchargement et de la copie dans le presse-papiers. Veuillez sauvegarder manuellement.",
         noPositionFound: "ℹ️ Faites défiler pour définir une position de lecture.",
         scriptError: "❌ Erreur lors du chargement du script.",
         invalidPosition: "❌ Position de lecture invalide.",
         fileSelectError: "❌ Veuillez sélectionner un fichier JSON.",
         fileReadError: "❌ Erreur lors de la lecture du fichier.",
         fileDialogError: "❌ Erreur lors de l'ouverture de la boîte de dialogue.",
         fileLoadSuccess: "✅ Position de lecture chargée avec succès !",
         buttonsError: "❌ Erreur lors de l'affichage des boutons.",
         searchPopup: "🔍 Recherche en cours... Appuyez sur ESPACE pour annuler.",
         searchNoPosition: "❌ Aucune position de lecture disponible.",
         searchScrollPrompt: "ℹ️ Veuillez faire défiler ou cliquer sur la loupe."
     },
     'zh-CN': {
         noValidPosition: "❌ 没有有效的阅读位置可以下载。",
         alreadyDownloaded: "ℹ️ 此阅读位置已下载。",
         downloadSuccess: "✅ 阅读位置已下载为 {fileName}。",
         downloadFailed: "❌ 下载失败。阅读位置已复制到剪贴板。请手动粘贴到 .json 文件中。",
         downloadClipboardFailed: "❌ 下载和剪贴板复制失败。请手动保存。",
         noPositionFound: "ℹ️ 滚动以设置阅读位置。",
         scriptError: "❌ 加载脚本时出错。",
         invalidPosition: "❌ 无效的阅读位置。",
         fileSelectError: "❌ 请选择一个 JSON 文件。",
         fileReadError: "❌ 读取文件时出错。",
         fileDialogError: "❌ 打开文件对话框时出错。",
         fileLoadSuccess: "✅ 阅读位置加载成功!",
         buttonsError: "❌ 显示按钮时出错。",
         searchPopup: "🔍 正在搜索... 按空格键取消。",
         searchNoPosition: "❌ 没有可用的阅读位置。",
         searchScrollPrompt: "ℹ️ 请滚动或点击放大镜。"
     },
     ru: {
         noValidPosition: "❌ Нет действительной позиции чтения для загрузки.",
         alreadyDownloaded: "ℹ️ Эта позиция чтения уже была загружена.",
         downloadSuccess: "✅ Позиция чтения загружена как {fileName}.",
         downloadFailed: "❌ Не удалось выполнить загрузку. Позиция чтения скопирована в буфер обмена. Пожалуйста, вставьте вручную в файл .json.",
         downloadClipboardFailed: "❌ Не удалось выполнить загрузку и копирование в буфер обмена. Пожалуйста, сохраните вручную.",
         noPositionFound: "ℹ️ Прокрутите, чтобы установить позицию чтения.",
         scriptError: "❌ Ошибка при загрузке скрипта.",
         invalidPosition: "❌ Недействительная позиция чтения.",
         fileSelectError: "❌ Пожалуйста, выберите файл JSON.",
         fileReadError: "❌ Ошибка при чтении файла.",
         fileDialogError: "❌ Ошибка при открытии диалогового окна.",
         fileLoadSuccess: "✅ Позиция чтения успешно загружена!",
         buttonsError: "❌ Ошибка при отображении кнопок.",
         searchPopup: "🔍 Поиск... Нажмите ПРОБЕЛ для отмены.",
         searchNoPosition: "❌ Позиция чтения недоступна.",
         searchScrollPrompt: "ℹ️ Прокрутите или нажмите на лупу."
     },
     ja: {
         noValidPosition: "❌ ダウンロードする有効な読み取り位置がありません。",
         alreadyDownloaded: "ℹ️ この読み取り位置はすでにダウンロードされています。",
         downloadSuccess: "✅ 読み取り位置が{fileName}としてダウンロードされました。",
         downloadFailed: "❌ ダウンロードに失敗しました。読み取り位置がクリップボードにコピーされました。手動で.jsonファイルに貼り付けてください。",
         downloadClipboardFailed: "❌ ダウンロードおよびクリップボードへのコピーに失敗しました。手動で保存してください。",
         noPositionFound: "ℹ️ スクロールして読み取り位置を設定してください。",
         scriptError: "❌ スクリプトの読み込み中にエラーが発生しました。",
         invalidPosition: "❌ 無効な読み取り位置です。",
         fileSelectError: "❌ JSONファイルを選択してください。",
         fileReadError: "❌ ファイルの読み込み中にエラーが発生しました。",
         fileDialogError: "❌ ファイルダイアログのオープン中にエラーが発生しました。",
         fileLoadSuccess: "✅ 読み取り位置が正常にロードされました!",
         buttonsError: "❌ ボタンの表示中にエラーが発生しました。",
         searchPopup: "🔍 検索中... スペースキーを押してキャンセル。",
         searchNoPosition: "❌ 読み取り位置がありません。",
         searchScrollPrompt: "ℹ️ スクロールするか、虫眼鏡をクリックしてください。"
     },
     'pt-BR': {
         noValidPosition: "❌ Nenhuma posição de leitura válida para download.",
         alreadyDownloaded: "ℹ️ Esta posição de leitura já foi baixada.",
         downloadSuccess: "✅ Posição de leitura baixada como {fileName}.",
         downloadFailed: "❌ Falha no download. Posição de leitura copiada para a área de transferência. Cole manualmente em um arquivo .json.",
         downloadClipboardFailed: "❌ Falha no download e na cópia para a área de transferência. Por favor, salve manualmente.",
         noPositionFound: "ℹ️ Role para definir uma posição de leitura.",
         scriptError: "❌ Erro ao carregar o script.",
         invalidPosition: "❌ Posição de leitura inválida.",
         fileSelectError: "❌ Por favor, selecione um arquivo JSON.",
         fileReadError: "❌ Erro ao ler o arquivo.",
         fileDialogError: "❌ Erro ao abrir o diálogo de arquivo.",
         fileLoadSuccess: "✅ Posição de leitura carregada com sucesso!",
         buttonsError: "❌ Erro ao exibir os botões.",
         searchPopup: "🔍 Pesquisando... Pressione ESPAÇO para cancelar.",
         searchNoPosition: "❌ Nenhuma posição de leitura disponível.",
         searchScrollPrompt: "ℹ️ Role ou clique na lupa."
     },
     hi: {
         noValidPosition: "❌ डाउनलोड करने के लिए कोई वैध पढ़ने की स्थिति नहीं है।",
         alreadyDownloaded: "ℹ️ यह पढ़ने की स्थिति पहले ही डाउनलोड की जा चुकी है।",
         downloadSuccess: "✅ पढ़ने की स्थिति {fileName} के रूप में डाउनलोड की गई।",
         downloadFailed: "❌ डाउनलोड विफल। पढ़ने की स्थिति क्लिपबोर्ड में कॉपी की गई है। कृपया इसे मैन्युअल रूप से .json फ़ाइल में पेस्ट करें।",
         downloadClipboardFailed: "❌ डाउनलोड और क्लिपबोर्ड कॉपी विफल। कृपया मैन्युअल रूप से सहेजें।",
         noPositionFound: "ℹ️ पढ़ने की स्थिति सेट करने के लिए स्क्रॉल करें।",
         scriptError: "❌ स्क्रिप्ट लोड करने में त्रुटि।",
         invalidPosition: "❌ अमान्य पढ़ने की स्थिति।",
         fileSelectError: "❌ कृपया एक JSON फ़ाइल चुनें।",
         fileReadError: "❌ फ़ाइल पढ़ने में त्रुटि।",
         fileDialogError: "❌ फ़ाइल डायलॉग खोलने में त्रुटि।",
         fileLoadSuccess: "✅ पढ़ने की स्थिति सफलतापूर्वक लोड की गई!",
         buttonsError: "❌ बटनों को प्रदर्शित करने में त्रुटि।",
         searchPopup: "🔍 खोज चल रही है... रद्द करने के लिए स्पेस दबाएं।",
         searchNoPosition: "❌ कोई पढ़ने की स्थिति उपलब्ध नहीं है।",
         searchScrollPrompt: "ℹ️ कृपया स्क्रॉल करें या मैग्नीफायर पर क्लिक करें।"
     },
     ar: {
         noValidPosition: "❌ لا توجد مواضع قراءة صالحة للتحميل.",
         alreadyDownloaded: "ℹ️ تم تحميل موضع القراءة هذا بالفعل.",
         downloadSuccess: "✅ تم تحميل موضع القراءة باسم {fileName}.",
         downloadFailed: "❌ فشل التحميل. تم نسخ موضع القراءة إلى الحافظة. يرجى لصقه يدويًا في ملف .json.",
         downloadClipboardFailed: "❌ فشل التحميل والنسخ إلى الحافظة. يرجى الحفظ يدويًا.",
         noPositionFound: "ℹ️ قم بالتمرير لتحديد موضع القراءة.",
         scriptError: "❌ خطأ أثناء تحميل السكربت.",
         invalidPosition: "❌ موضع قراءة غير صالح.",
         fileSelectError: "❌ يرجى اختيار ملف JSON.",
         fileReadError: "❌ خطأ أثناء قراءة الملف.",
         fileDialogError: "❌ خطأ أثناء فتح حوار الملف.",
         fileLoadSuccess: "✅ تم تحميل موضع القراءة بنجاح!",
         buttonsError: "❌ خطأ أثناء عرض الأزرار.",
         searchPopup: "🔍 جارٍ البحث... اضغط على مفتاح المسافة للإلغاء.",
         searchNoPosition: "❌ لا يوجد موضع قراءة متاح.",
         searchScrollPrompt: "ℹ️ يرجى التمرير أو النقر على العدسة المكبرة."
     },
     it: {
         noValidPosition: "❌ Nessuna posizione di lettura valida da scaricare.",
         alreadyDownloaded: "ℹ️ Questa posizione di lettura è già stata scaricata.",
         downloadSuccess: "✅ Posizione di lettura scaricata come {fileName}.",
         downloadFailed: "❌ Download fallito. Posizione di lettura copiata negli appunti. Incollala manualmente in un file .json.",
         downloadClipboardFailed: "❌ Download e copia negli appunti falliti. Salva manualmente.",
         noPositionFound: "ℹ️ Scorri per impostare una posizione di lettura.",
         scriptError: "❌ Errore durante il caricamento dello script.",
         invalidPosition: "❌ Posizione di lettura non valida.",
         fileSelectError: "❌ Seleziona un file JSON.",
         fileReadError: "❌ Errore durante la lettura del file.",
         fileDialogError: "❌ Errore durante l'apertura della finestra di dialogo.",
         fileLoadSuccess: "✅ Posizione di lettura caricata con successo!",
         buttonsError: "❌ Errore durante la visualizzazione dei pulsanti.",
         searchPopup: "🔍 Ricerca in corso... Premi SPAZIO per annullare.",
         searchNoPosition: "❌ Nessuna posizione di lettura disponibile.",
         searchScrollPrompt: "ℹ️ Scorri o fai clic sulla lente d'ingrandimento."
     },
     ko: {
         noValidPosition: "❌ 다운로드할 유효한 읽기 위치가 없습니다.",
         alreadyDownloaded: "ℹ️ 이 읽기 위치는 이미 다운로드되었습니다.",
         downloadSuccess: "✅ 읽기 위치가 {fileName}으로 다운로드되었습니다.",
         downloadFailed: "❌ 다운로드 실패. 읽기 위치가 클립보드에 복사되었습니다. .json 파일에 수동으로 붙여넣으세요.",
         downloadClipboardFailed: "❌ 다운로드 및 클립보드 복사 실패. 수동으로 저장하세요.",
         noPositionFound: "ℹ️ 읽기 위치를 설정하려면 스크롤하세요.",
         scriptError: "❌ 스크립트 로드 중 오류가 발생했습니다.",
         invalidPosition: "❌ 유효하지 않은 읽기 위치입니다.",
         fileSelectError: "❌ JSON 파일을 선택하세요.",
         fileReadError: "❌ 파일 읽기 중 오류가 발생했습니다.",
         fileDialogError: "❌ 파일 대화 상자를 여는 중 오류가 발생했습니다.",
         fileLoadSuccess: "✅ 읽기 위치가 성공적으로 로드되었습니다!",
         buttonsError: "❌ 버튼 표시 중 오류가 발생했습니다.",
         searchPopup: "🔍 검색 중... 취소하려면 스페이스바를 누르세요.",
         searchNoPosition: "❌ 사용 가능한 읽기 위치가 없습니다.",
         searchScrollPrompt: "ℹ️ 스크롤하거나 돋보기를 클릭하세요."
     }
 };

 // Funktion zur Erkennung der Benutzersprache
 function getUserLanguage() {
     const lang = navigator.language || navigator.languages[0] || 'en';
     const langCode = lang.split('-')[0]; // Nur den Hauptcode (z. B. 'en' aus 'en-US') verwenden
     return translations[langCode] ? langCode : 'en'; // Fallback auf Englisch
 }

 // Funktion zum Abrufen der übersetzten Nachricht
 function getTranslatedMessage(key, lang, params = {}) {
     const translation = translations[lang] || translations['en'];
     let message = translation[key] || translations['en'][key] || key;
     // Ersetze Platzhalter (z. B. {fileName})
     Object.keys(params).forEach(param => {
         message = message.replace(`{${param}}`, params[param]);
     });
     return message;
 }
    let lastReadPost = null;
    let isAutoScrolling = false;
    let isSearching = false;
    let isScriptActivated = false;
    let currentPost = null;
    let lastHighlightedPost = null;
    const downloadedPosts = new Set();

    function loadLastReadPost(callback) {
        try {
            const storedPost = GM_getValue("lastReadPost", null);
            if (storedPost) {
                const parsedPost = JSON.parse(storedPost);
                console.log("🛠️ DEBUG: Geladene Leseposition:", parsedPost);
                callback(parsedPost);
            } else {
                console.log("⏹️ Keine gespeicherte Leseposition gefunden.");
                callback(null);
            }
        } catch (err) {
            console.error("❌ Fehler beim Laden der Leseposition:", err);
            callback(null);
        }
    }

    function saveLastReadPost(post) {
        if (!post || !post.timestamp || !post.authorHandler) {
            console.log("❌ Ungültige Leseposition, Speicherung abgebrochen:", post);
            return;
        }

        let attempts = 0;
        const maxAttempts = 3;

        function trySave() {
            try {
                const postData = JSON.stringify(post);
                GM_setValue("lastReadPost", postData);
                console.log("💾 Leseposition erfolgreich mit GM_setValue gespeichert:", postData);
                localStorage.setItem("lastReadPost", postData);
                console.log("💾 Fallback in localStorage gespeichert:", localStorage.getItem("lastReadPost"));
            } catch (err) {
                attempts++;
                console.error(`❌ Fehler beim Speichern der Leseposition (Versuch ${attempts}/${maxAttempts}):`, err);
                if (attempts < maxAttempts) {
                    console.log("🔄 Wiederhole Speicherversuch...");
                    setTimeout(trySave, 1000);
                } else {
                    console.error("❌ Maximale Speicherversuche erreicht. Fallback auf localStorage.");
                    localStorage.setItem("lastReadPost", JSON.stringify(post));
                    promptManualFallback(post);
                }
            }
        }

        trySave();
    }

    function promptManualFallback(data) {
        const content = JSON.stringify(data);
        const message = `📝 Neue Leseposition: ${content}\nBitte speichere dies manuell, da der Speichervorgang fehlschlug.`;
        showPopup(message, 10000);
        console.log("📝 Bitte manuell speichern:", content);
    }

     function downloadLastReadPost() {
     try {
         if (!currentPost || !currentPost.timestamp || !currentPost.authorHandler) {
             console.warn("⚠️ Keine gültige Leseposition zum Speichern:", currentPost);
             showPopup("noValidPosition", 5000);
             return;
         }

         const postKey = `${currentPost.timestamp}-${currentPost.authorHandler}`;
         if (downloadedPosts.has(postKey)) {
             console.log("⏹️ Leseposition bereits heruntergeladen:", postKey);
             showPopup("alreadyDownloaded", 5000);
             return;
         }

         console.log("🛠️ DEBUG: Starte manuellen Download-Prozess für Leseposition:", currentPost);

         const date = new Date(currentPost.timestamp);
         const year = date.getFullYear();
         const month = String(date.getMonth() + 1).padStart(2, "0");
         const day = String(date.getDate()).padStart(2, "0");
         const hour = String(date.getHours()).padStart(2, "0");
         const minute = String(date.getMinutes()).padStart(2, "0");
         const second = String(date.getSeconds()).padStart(2, "0");
         const fileName = `${year}${month}${day}_${hour}${minute}${second}-${currentPost.authorHandler}.json`;

         console.log("📄 Generierter Dateiname:", fileName);

         const fileContent = JSON.stringify(currentPost, null, 2);
         const blob = new Blob([fileContent], { type: "application/json" });
         const url = URL.createObjectURL(blob);
         const a = document.createElement("a");
         a.href = url;
         a.download = fileName;
         a.style.display = "none";
         document.body.appendChild(a);
         console.log("🔗 Download-Element erstellt:", a);

         try {
             a.click();
             console.log(`💾 Leseposition als Datei gespeichert: ${fileName}`);
             showPopup("downloadSuccess", 5000, { fileName });
             downloadedPosts.add(postKey);
         } catch (clickErr) {
             console.error("❌ Fehler beim Auslösen des Downloads:", clickErr);
             try {
                 navigator.clipboard.writeText(fileContent).then(() => {
                     console.log("📋 Leseposition in Zwischenablage kopiert.");
                     showPopup("downloadFailed", 10000, { fileName });
                     downloadedPosts.add(postKey);
                 }).catch(clipErr => {
                     console.error("❌ Fehler beim Kopieren in die Zwischenablage:", clipErr);
                     showPopup("downloadClipboardFailed", 10000);
                     promptManualFallback(currentPost);
                 });
             } catch (clipErr) {
                 console.error("❌ Fehler beim Zugriff auf die Zwischenablage:", clipErr);
                 showPopup("downloadClipboardFailed", 10000);
                 promptManualFallback(currentPost);
             }
         }

         setTimeout(() => {
             try {
                 document.body.removeChild(a);
                 URL.revokeObjectURL(url);
                 console.log("🧹 Download-Element entfernt und URL freigegeben.");
             } catch (cleanupErr) {
                 console.error("❌ Fehler beim Aufräumen:", cleanupErr);
             }
         }, 3000);
     } catch (err) {
         console.error("❌ Fehler beim Speichern der Datei:", err);
         showPopup("downloadClipboardFailed", 5000);
         promptManualFallback(currentPost);
     }
 }

     function loadNewestLastReadPost() {
     return new Promise(resolve => {
         loadLastReadPost(storedPost => {
             if (storedPost && storedPost.timestamp && storedPost.authorHandler) {
                 lastReadPost = storedPost;
                 console.log("✅ Leseposition geladen:", lastReadPost);
             } else {
                 const localPost = JSON.parse(localStorage.getItem("lastReadPost") || "{}");
                 if (localPost && localPost.timestamp && localPost.authorHandler) {
                     lastReadPost = localPost;
                     console.log("✅ Leseposition aus localStorage:", lastReadPost);
                 } else {
                     console.warn("⚠️ Keine Leseposition gefunden.");
                     showPopup("noPositionFound"); // Geändert
                 }
             }
             resolve();
         });
     });
 }

     async function initializeScript() {
     console.log("🔧 Lade Leseposition...");
     try {
         await loadNewestLastReadPost();
         console.log("✅ Initialisierung erfolgreich.");

         window.addEventListener("scroll", () => {
             if (!isScriptActivated) {
                 isScriptActivated = true;
                 console.log("🛠️ DEBUG: Skript durch Scrollen aktiviert.");
                 observeForNewPosts();
             }

             if (isAutoScrolling || isSearching) {
                 console.log("⏹️ Scroll-Ereignis ignoriert.");
                 return;
             }
             markTopVisiblePost(true);
         }, { passive: true });
     } catch (err) {
         console.error("❌ Fehler bei der Initialisierung:", err);
         showPopup("scriptError"); // Geändert
     }
 }

     function initializeWhenDOMReady() {
     if (!window.location.href.includes("/home")) {
         console.log("🚫 Skript deaktiviert: Nicht auf der Home-Seite.");
         return;
     }
     console.log("🚀 Initialisiere Skript...");

     const observer = new MutationObserver((mutations, obs) => {
         if (document.body) {
             obs.disconnect();
             initializeScript().then(() => {
                 createButtons();
                 startPeriodicSave();
             }).catch(err => {
                 console.error("❌ Fehler bei der Initialisierung:", err);
                 showPopup("scriptError"); // Geändert
             });
         }
     });
     observer.observe(document.documentElement, { childList: true, subtree: true });
 }

    window.addEventListener("load", initializeWhenDOMReady);

     function loadLastReadPostFromFile() {
     try {
         const input = document.createElement("input");
         input.type = "file";
         input.accept = ".json";
         input.style.display = "none";
         document.body.appendChild(input);

         input.addEventListener("change", (event) => {
             const file = event.target.files[0];
             if (!file) {
                 console.warn("⚠️ Keine Datei ausgewählt.");
                 showPopup("fileSelectError", 5000);
                 document.body.removeChild(input);
                 return;
             }

             const reader = new FileReader();
             reader.onload = (e) => {
                 try {
                     const data = JSON.parse(e.target.result);
                     if (data.timestamp && data.authorHandler) {
                         lastReadPost = data;
                         saveLastReadPost(data);
                         console.log("✅ Leseposition aus Datei geladen:", lastReadPost);
                         showPopup("fileLoadSuccess", 3000);
                         startRefinedSearchForLastReadPost();
                     } else {
                         console.warn("⚠️ Ungültige Leseposition in der Datei:", data);
                         showPopup("invalidPosition", 5000);
                     }
                 } catch (err) {
                     console.error("❌ Fehler beim Lesen der Datei:", err);
                     showPopup("fileReadError", 5000);
                 }
                 document.body.removeChild(input);
             };
             reader.readAsText(file);
         });

         input.click();
     } catch (err) {
         console.error("❌ Fehler beim Öffnen des Datei-Dialogs:", err);
         showPopup("fileDialogError", 5000);
     }
 }

    function startPeriodicSave() {
        setInterval(() => {
            if (lastReadPost && isScriptActivated) {
                loadLastReadPost(existingPost => {
                    if (!existingPost || new Date(lastReadPost.timestamp) > new Date(existingPost.timestamp) ||
                        (lastReadPost.timestamp === existingPost.timestamp && lastReadPost.authorHandler !== existingPost.authorHandler)) {
                        saveLastReadPost(lastReadPost);
                        console.log("💾 Periodische Speicherung: Neue Leseposition gespeichert:", lastReadPost);
                    } else {
                        console.log("⏹️ Periodische Speicherung übersprungen: Leseposition nicht neuer oder identisch.");
                    }
                });
            }
        }, 30000);
    }

    function markTopVisiblePost(save = true) {
    const topPost = getTopVisiblePost();
    if (!topPost) {
        console.log("❌ Kein sichtbarer Beitrag.");
        return;
    }

    const postTimestamp = getPostTimestamp(topPost);
    const postAuthorHandler = getPostAuthorHandler(topPost);

    if (postTimestamp && postAuthorHandler) {
        const newPost = { timestamp: postTimestamp, authorHandler: postAuthorHandler };
        console.log("🛠️ DEBUG: Versuche, Leseposition zu speichern:", newPost);

        if (lastHighlightedPost && lastHighlightedPost !== topPost) {
            lastHighlightedPost.style.boxShadow = "none";
        }
        topPost.style.boxShadow = "0 0 20px 10px rgba(246, 146, 25, 0.9)"; // Geändert zu #f69219
        lastHighlightedPost = topPost;
        currentPost = newPost;

        if (save && isScriptActivated) {
            loadLastReadPost(existingPost => {
                console.log("🛠️ DEBUG: markTopVisiblePost - newPost:", newPost, "existingPost:", existingPost);
                if (!existingPost ||
                    new Date(postTimestamp) > new Date(existingPost.timestamp) ||
                    (postTimestamp === existingPost.timestamp && postAuthorHandler !== existingPost.authorHandler)) {
                    lastReadPost = newPost;
                    console.log("💾 Neue Leseposition gesetzt:", lastReadPost);
                    saveLastReadPost(lastReadPost);
                } else {
                    console.log("⏹️ Interne Speicherung übersprungen: Leseposition nicht neuer oder identisch.");
                }
            });
        }
    }
}

    function getTopVisiblePost() {
        const posts = Array.from(document.querySelectorAll("article"));
        return posts.find(post => {
            const rect = post.getBoundingClientRect();
            return rect.top >= 0 && rect.bottom > 0;
        });
    }

    function getPostTimestamp(post) {
        const timeElement = post.querySelector("time");
        return timeElement ? timeElement.getAttribute("datetime") : null;
    }

    function getPostAuthorHandler(post) {
        const handlerElement = post.querySelector('[role="link"][href*="/"]');
        return handlerElement ? handlerElement.getAttribute("href").slice(1) : null;
    }

     function startRefinedSearchForLastReadPost() {
     if (!isScriptActivated) {
         console.log("⏹️ Suche abgebrochen: Skript nicht aktiviert.");
         showPopup("searchScrollPrompt");
         return;
     }

     loadLastReadPost(storedData => {
         if (!storedData) {
             const localData = JSON.parse(localStorage.getItem("lastReadPost") || "{}");
             if (localData && localData.timestamp && localData.authorHandler) {
                 lastReadPost = localData;
             } else {
                 console.log("❌ Keine Leseposition gefunden.");
                 showPopup("searchNoPosition");
                 return;
             }
         } else {
             lastReadPost = storedData;
         }

         if (!lastReadPost.timestamp || !lastReadPost.authorHandler) {
             console.log("❌ Ungültige Leseposition:", lastReadPost);
             showPopup("invalidPosition");
             return;
         }

         console.log("🔍 Starte Suche:", lastReadPost);
         const popup = createSearchPopup();

         let direction = 1;
         let scrollAmount = 2000;
         let previousScrollY = -1;
         let searchAttempts = 0;
         const maxAttempts = 50;

         function handleSpaceKey(event) {
             if (event.code === "Space") {
                 console.log("⏹️ Suche gestoppt.");
                 isSearching = false;
                 popup.remove();
                 window.removeEventListener("keydown", handleSpaceKey);
             }
         }

         window.addEventListener("keydown", handleSpaceKey);

         const search = () => {
             if (!isSearching || searchAttempts >= maxAttempts) {
                 console.log("⏹️ Suche beendet: Max Versuche oder abgebrochen.");
                 isSearching = false;
                 popup.remove();
                 window.removeEventListener("keydown", handleSpaceKey);
                 return;
             }

             const visiblePosts = getVisiblePosts();
             const comparison = compareVisiblePostsToLastReadPost(visiblePosts);

             if (comparison === "match") {
                 const matchedPost = findPostByData(lastReadPost);
                 if (matchedPost) {
                     console.log("🎯 Beitrag gefunden:", lastReadPost);
                     isAutoScrolling = true;
                     scrollToPostWithHighlight(matchedPost);
                     isSearching = false;
                     popup.remove();
                     window.removeEventListener("keydown", handleSpaceKey);
                     return;
                 }
             } else if (comparison === "older") {
                 direction = -1;
             } else if (comparison === "newer") {
                 direction = 1;
             }

             if (window.scrollY === previousScrollY) {
                 scrollAmount = Math.max(scrollAmount / 2, 500);
                 direction = -direction;
             } else {
                 scrollAmount = Math.min(scrollAmount * 1.5, 3000);
             }

             previousScrollY = window.scrollY;
             searchAttempts++;

             requestAnimationFrame(() => {
                 window.scrollBy({
                     top: direction * scrollAmount,
                     behavior: "smooth"
                 });
                 setTimeout(search, 1000);
             });
         };

         isSearching = true;
         search();
     });
 }

     function createSearchPopup() {
     const lang = getUserLanguage();
     const message = getTranslatedMessage('searchPopup', lang);
     const popup = document.createElement("div");
     popup.style.position = "fixed";
     popup.style.top = "20px";
     popup.style.left = "50%";
     popup.style.transform = "translateX(-50%)";
     popup.style.backgroundColor = "rgba(0, 0, 0, 0.9)";
     popup.style.color = "#ffffff";
     popup.style.padding = "10px 20px";
     popup.style.borderRadius = "8px";
     popup.style.fontSize = "14px";
     popup.style.boxShadow = "0 0 10px rgba(246, 146, 25, 0.8)"; // Farbe #f69219
     popup.style.zIndex = "10000";
     popup.textContent = message;
     document.body.appendChild(popup);
     return popup;
 }

    function compareVisiblePostsToLastReadPost(posts, customPosition = lastReadPost) {
        const validPosts = posts.filter(post => post.timestamp && post.authorHandler);

        if (validPosts.length === 0) {
            console.log("⚠️ Keine sichtbaren Beiträge.");
            return null;
        }

        const lastReadTime = new Date(customPosition.timestamp);

        const allOlder = validPosts.every(post => new Date(post.timestamp) < lastReadTime);
        const allNewer = validPosts.every(post => new Date(post.timestamp) > lastReadTime);

        if (validPosts.some(post => post.timestamp === customPosition.timestamp && post.authorHandler === customPosition.authorHandler)) {
            return "match";
        } else if (allOlder) {
            return "older";
        } else if (allNewer) {
            return "newer";
        } else {
            return "mixed";
        }
    }

    function scrollToPostWithHighlight(post) {
    if (!post) {
        console.log("❌ Kein Beitrag zum Scrollen.");
        return;
    }

    isAutoScrolling = true;

    if (lastHighlightedPost && lastHighlightedPost !== post) {
        lastHighlightedPost.style.boxShadow = "none";
    }
    post.style.boxShadow = "0 0 20px 10px rgba(246, 146, 25, 0.9)"; // Geändert zu #f69219
    lastHighlightedPost = post;

    post.scrollIntoView({ behavior: "smooth", block: "center" });

    setTimeout(() => {
        isAutoScrolling = false;
        console.log("✅ Beitrag zentriert.");
    }, 1000);
}

    function getVisiblePosts() {
        const posts = Array.from(document.querySelectorAll("article"));
        return posts.map(post => ({
            element: post,
            timestamp: getPostTimestamp(post),
            authorHandler: getPostAuthorHandler(post),
        }));
    }

    function observeForNewPosts() {
        let isProcessingIndicator = false;

        const observer = new MutationObserver(() => {
            if (!isScriptActivated) {
                console.log("⏹️ Beobachtung abgebrochen: Skript nicht aktiviert.");
                return;
            }

            if (window.scrollY <= 1 && !isSearching && !isProcessingIndicator && lastReadPost) {
                const newPostsIndicator = getNewPostsIndicator();
                if (newPostsIndicator) {
                    console.log("🆕 Neue Beiträge erkannt.");
                    isProcessingIndicator = true;
                    clickNewPostsIndicator(newPostsIndicator);
                    setTimeout(() => {
                        startRefinedSearchForLastReadPost();
                        isProcessingIndicator = false;
                    }, 2000);
                }
            }
        });

        observer.observe(document.body, {
            childList: true,
            subtree: true,
        });
    }

    function getNewPostsIndicator() {
        const buttons = document.querySelectorAll('button[role="button"]');
        for (const button of buttons) {
            const span = button.querySelector('span');
            if (span) {
                const textContent = span.textContent || '';
                const postIndicatorPattern = /^\d+\s*(neue|new)?\s*(Post|Posts|Beitrag|Beiträge|Tweet|Tweets|Publicación|Publications|投稿|게시물|пост|постов|mensagem|mensagens|مشاركة|مشاركات)\b/i;
                if (postIndicatorPattern.test(textContent)) {
                    if (!button.dataset.processed) {
                        console.log(`🆕 Indikator gefunden: ${textContent}`);
                        button.dataset.processed = 'true';
                        return button;
                    }
                }
            }
        }
        console.log("ℹ️ Kein Beitragsindikator gefunden.");
        return null;
    }

    function clickNewPostsIndicator(indicator) {
        if (!indicator) {
            console.log("⚠️ Kein Indikator gefunden.");
            return;
        }

        console.log("✅ Klicke auf Indikator...");
        try {
            indicator.click();
            console.log("✅ Indikator geklickt.");
        } catch (err) {
            console.error("❌ Fehler beim Klicken:", err);
        }
    }

    function findPostByData(data) {
        const posts = Array.from(document.querySelectorAll("article"));
        return posts.find(post => {
            const postTimestamp = getPostTimestamp(post);
            const authorHandler = getPostAuthorHandler(post);
            return postTimestamp === data.timestamp && authorHandler === data.authorHandler;
        });
    }

    function createButtons() {
    setTimeout(() => {
        try {
            if (!document.body) {
                console.warn("⚠️ document.body nicht verfügbar.");
                return;
            }

            const buttonContainer = document.createElement("div");
            buttonContainer.style.position = "fixed";
            buttonContainer.style.top = "10px";
            buttonContainer.style.left = "10px";
            buttonContainer.style.zIndex = "10000";
            buttonContainer.style.display = "flex";
            buttonContainer.style.flexDirection = "column";
            buttonContainer.style.alignItems = "flex-start";
            buttonContainer.style.visibility = "visible";

            const buttonsConfig = [
                {
                    icon: "🔍",
                    title: "Start manual search",
                    onClick: () => {
                        console.log("🔍 Manuelle Suche gestartet.");
                        if (!isScriptActivated) {
                            isScriptActivated = true;
                            console.log("🛠️ DEBUG: Skript durch Lupen-Klick aktiviert.");
                            observeForNewPosts();
                        }
                        startRefinedSearchForLastReadPost();
                    },
                },
                {
                    icon: "📂",
                    title: "Load last read position from file",
                    onClick: () => {
                        console.log("📂 Lade Leseposition aus Datei...");
                        loadLastReadPostFromFile();
                    },
                },
                {
                    icon: "💾",
                    title: "Download current read position",
                    onClick: () => {
                        console.log("💾 Starte manuellen Download der Leseposition...");
                        downloadLastReadPost();
                    },
                },
            ];

            buttonsConfig.forEach(({ icon, title, onClick }) => {
                const button = createButton(icon, title, onClick);
                buttonContainer.appendChild(button);
            });

            document.body.appendChild(buttonContainer);
            console.log("🛠️ DEBUG: Button-Container erstellt:", buttonContainer);
        } catch (err) {
            console.error("❌ Fehler beim Erstellen der Buttons:", err);
            showPopup("buttonsError"); // Geändert
        }
    }, 2000);
}

    function createButton(icon, title, onClick) {
        const button = document.createElement("div");
        button.style.width = "27px";
        button.style.height = "27px";
        button.style.backgroundColor = "rgba(0, 0, 0, 0.9)";
        button.style.color = "#ffffff";
        button.style.borderRadius = "50%";
        button.style.display = "flex";
        button.style.justifyContent = "center";
        button.style.alignItems = "center";
        button.style.cursor = "pointer";
        button.style.fontSize = "14px";
        button.style.boxShadow = "0 0 8px rgba(255, 255, 255, 0.5)";
        button.style.transition = "transform 0.2s, box-shadow 0.3s";
        button.style.zIndex = "10001";
        button.style.marginBottom = "8px";
        button.textContent = icon;
        button.title = title;

        button.addEventListener("click", () => {
            button.style.boxShadow = "0 0 15px rgba(255, 255, 255, 0.8)";
            button.style.transform = "scale(0.9)";
            setTimeout(() => {
                button.style.boxShadow = "0 0 8px rgba(255, 255, 255, 0.5)";
                button.style.transform = "scale(1)";
            }, 300);
            onClick();
        });

        return button;
    }

     function showPopup(messageKey, duration = 3000, params = {}) {
     const lang = getUserLanguage();
     const message = getTranslatedMessage(messageKey, lang, params);
     const popup = document.createElement("div");
     popup.style.position = "fixed";
     popup.style.top = "20px";
     popup.style.left = "50%";
     popup.style.transform = "translateX(-50%)";
     popup.style.backgroundColor = "rgba(0, 0, 0, 0.9)";
     popup.style.color = "#ffffff";
     popup.style.padding = "10px 20px";
     popup.style.borderRadius = "8px";
     popup.style.fontSize = "14px";
     popup.style.boxShadow = "0 0 10px rgba(246, 146, 25, 0.8)"; // Farbe #f69219
     popup.style.zIndex = "10000";
     popup.style.maxWidth = "500px";
     popup.style.whiteSpace = "pre-wrap";
     popup.textContent = message;

     document.body.appendChild(popup);

     setTimeout(() => {
         popup.remove();
     }, duration);
 }
})();