XEnhancer ช่วยเพิ่มประสิทธิภาพการใช้งาน Twitter/X — บันทึกสื่อด้วยคลิกเดียว, จัดรูปแบบเวลาสำหรับความชัดเจน, และปรับปรุงฟีดโซเชียลของคุณอย่างง่ายดาย
// ==UserScript==
// @name XEnhancer
// @name:ar XEnhancer
// @name:bg XEnhancer
// @name:ckb XEnhancer
// @name:cs XEnhancer
// @name:da XEnhancer
// @name:de XEnhancer
// @name:el XEnhancer
// @name:en XEnhancer
// @name:eo XEnhancer
// @name:es XEnhancer
// @name:es-419 XEnhancer
// @name:fi XEnhancer
// @name:fr XEnhancer
// @name:fr-CA XEnhancer
// @name:he XEnhancer
// @name:hr XEnhancer
// @name:hu XEnhancer
// @name:id XEnhancer
// @name:it XEnhancer
// @name:ja XEnhancer
// @name:ka XEnhancer
// @name:ko XEnhancer
// @name:nb XEnhancer
// @name:nl XEnhancer
// @name:pl XEnhancer
// @name:pt-BR XEnhancer
// @name:ro XEnhancer
// @name:ru XEnhancer
// @name:sk XEnhancer
// @name:sr XEnhancer
// @name:sv XEnhancer
// @name:th XEnhancer
// @name:tr XEnhancer
// @name:uk XEnhancer
// @name:ug XEnhancer
// @name:vi XEnhancer
// @description XEnhancer empowers your Twitter/X browsing — save media in one click, format timestamps for clarity, and streamline your social feed with ease.
// @description:ar XEnhancer يعزز تجربتك على تويتر (X) — احفظ الوسائط بنقرة واحدة، نسق الطوابع الزمنية لتوضيح أفضل، ونظّم تغذيتك الاجتماعية بسهولة.
// @description:bg XEnhancer подобрява разглеждането на Twitter (X) — запазвайте медии с едно кликване, форматирайте времевите отметки за яснота и оптимизирайте социалния си поток лесно.
// @description:ckb XEnhancer بەرز دەکاتەوە بەرەوپێشگای تیوتر (X) — وێنە و ڤیدیۆکان بە یەک کرتە پاشکەوت بکە، کاتی ڕووداوەکان ڕوون بکەرەوە، و فیدەکانی کۆمەڵایەتی بە ئاسانیدا ڕێکبخە.
// @description:cs XEnhancer vylepšuje prohlížení Twitteru (X) — ukládejte média jedním kliknutím, formátujte časová razítka pro přehlednost a snadno optimalizujte svůj sociální feed.
// @description:da XEnhancer forbedrer din oplevelse på Twitter (X) — gem medier med et enkelt klik, formater tidsstempler for klarhed, og strømlin din sociale feed med lethed.
// @description:de XEnhancer verbessert Ihr Twitter/X-Erlebnis — speichern Sie Medien mit einem Klick, formatieren Sie Zeitstempel zur besseren Übersicht und optimieren Sie Ihren Social-Feed mühelos.
// @description:el Το XEnhancer ενισχύει την περιήγησή σας στο Twitter (X) — αποθηκεύστε πολυμέσα με ένα κλικ, μορφοποιήστε χρονικές σημάνσεις για μεγαλύτερη σαφήνεια και οργανώστε εύκολα το κοινωνικό σας feed.
// @description:en XEnhancer empowers your Twitter/X browsing — save media in one click, format timestamps for clarity, and streamline your social feed with ease.
// @description:eo XEnhancer plibonigas vian retumadon de Twitter (X) — konservu amaskomunikilojn per unu klako, formatu tempstampon por pli granda klareco, kaj faciligu vian socian fluon.
// @description:es XEnhancer mejora tu experiencia en Twitter (X): guarda medios con un clic, formatea las marcas de tiempo para mayor claridad y optimiza tu feed social con facilidad.
// @description:es-419 XEnhancer mejora tu navegación en Twitter (X) — guarda medios con un clic, formatea las marcas de tiempo para mayor claridad y agiliza tu feed social fácilmente.
// @description:fi XEnhancer parantaa Twitter (X) -selailuasi — tallenna media yhdellä napsautuksella, muotoile aikaleimat selkeyden lisäämiseksi ja tehosta sosiaalista syötettäsi vaivattomasti.
// @description:fr XEnhancer améliore votre navigation sur Twitter (X) — enregistrez des médias en un clic, formatez les horodatages pour plus de clarté et optimisez facilement votre flux social.
// @description:fr-CA XEnhancer améliore votre expérience sur Twitter (X) — sauvegardez les médias en un clic, formatez les horodatages pour plus de clarté et simplifiez votre flux social facilement.
// @description:he XEnhancer משדרג את הגלישה שלך ב-Twitter (X) — שמור מדיה בלחיצה אחת, עצב חותמות זמן להבהרה וייעל את הפיד החברתי שלך בקלות.
// @description:hr XEnhancer poboljšava pregledavanje Twittera (X) — spremite medije jednim klikom, formatirajte vremenske oznake radi preglednosti i pojednostavite svoj društveni feed.
// @description:hu Az XEnhancer fokozza a Twitter/X böngészést — egy kattintással mentheted a médiát, formázhatod az időbélyegeket az átláthatóság érdekében, és könnyedén optimalizálhatod a közösségi hírcsatornát.
// @description:id XEnhancer meningkatkan pengalaman menjelajahi Twitter/X — simpan media dengan satu klik, format timestamp untuk kejelasan, dan permudah feed sosial Anda.
// @description:it XEnhancer potenzia la navigazione su Twitter/X — salva i media con un clic, formatta i timestamp per maggiore chiarezza e ottimizza il tuo feed sociale con facilità.
// @description:ja XEnhancer は Twitter (X) の閲覧を強化します — メディアをワンクリックで保存し、タイムスタンプを見やすくフォーマットし、ソーシャルフィードを簡単に整理できます。
// @description:ka XEnhancer აძლიერებს Twitter/X-ს — დაარეგისტრირე მედია ერთ ক্লიკზე, დააწყობ დროის ნიშანებს გასაგებად და გამარტივე სოციალური ფიდი.
// @description:ko XEnhancer는 Twitter/X 탐색을 강화합니다 — 미디어를 한 번의 클릭으로 저장하고, 타임스탬프를 명확하게 포맷하며, 소셜 피드를 쉽게 정리할 수 있습니다.
// @description:nb XEnhancer forbedrer din Twitter/X-opplevelse — lagre medier med ett klikk, formater tidsstempler for klarhet, og strømlin feeden din enkelt.
// @description:nl XEnhancer verbetert je Twitter/X-ervaring — sla media op met één klik, formatteer tijdstempels voor duidelijkheid en stroomlijn je sociale feed eenvoudig.
// @description:pl XEnhancer usprawnia przeglądanie Twittera (X) — zapisuj media jednym kliknięciem, formatuj znaczniki czasu dla przejrzystości i usprawnij swój feed społecznościowy.
// @description:pt-BR XEnhancer potencializa sua navegação no Twitter/X — salve mídias com um clique, formate os timestamps para maior clareza e organize seu feed social com facilidade.
// @description:ro XEnhancer îmbunătățește navigarea pe Twitter/X — salvează media cu un singur clic, formatează timestamp-urile pentru claritate și optimizează-ți feedul social cu ușurință.
// @description:ru XEnhancer улучшает просмотр Twitter/X — сохраняйте медиа в один клик, форматируйте временные метки для наглядности и упрощайте вашу социальную ленту.
// @description:sk XEnhancer zlepšuje prehliadanie Twitteru (X) — ukladajte médiá jedným kliknutím, formátujte časové značky pre prehľadnosť a zjednodušte svoj sociálny feed.
// @description:sr XEnhancer unapređuje pregledanje Twittera (X) — sačuvajte medije jednim klikom, formatirajte vremenske oznake radi preglednosti i olakšajte svoj društveni feed.
// @description:sv XEnhancer förbättrar din Twitter/X-upplevelse — spara media med ett klick, formatera tidsstämplar för tydlighet och effektivisera ditt sociala flöde enkelt.
// @description:th XEnhancer ช่วยเพิ่มประสิทธิภาพการใช้งาน Twitter/X — บันทึกสื่อด้วยคลิกเดียว, จัดรูปแบบเวลาสำหรับความชัดเจน, และปรับปรุงฟีดโซเชียลของคุณอย่างง่ายดาย
// @description:tr XEnhancer, Twitter/X deneyiminizi güçlendirir — medyaları tek tıkla kaydedin, zaman damgalarını netlik için biçimlendirin ve sosyal akışınızı kolayca düzenleyin.
// @description:uk XEnhancer покращує перегляд Twitter/X — зберігайте медіа одним кліком, форматування часових міток для наочності та спрощення вашої соціальної стрічки.
// @description:ug XEnhancer Twitter/X تەجرىبىسىڭىزنى كۈچەيتىدۇ — ۋىدىئولارنى ۋە رەسىملەرنى بىر قېتىملىق چېكىش بىلەن ساقلاڭ، ۋاقىت بەلگىلىرىنى ئاچچىق-ئاشكارا قىلىپ بەلگىلەڭ، ۋە ئىجتىمائىي فېدڭىزنى ئاسانلاشتۇرۇڭ.
// @description:vi XEnhancer nâng cao trải nghiệm duyệt Twitter/X — lưu phương tiện chỉ với một cú nhấp, định dạng dấu thời gian rõ ràng và tối ưu hóa luồng xã hội của bạn dễ dàng.
// @namespace levivi_myself
// @version 1.0.1
// @author PeterParker, Levivi
// @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADAAAAAwCAYAAABXAvmHAAAEBklEQVR4AeyZXVbjMAyFy2wJ3ikro6wMeIc1de7nOh7LlmM7yRwOHDhR4h9ZuleSnbb8OX3zv18CX53An5WBh4eHywY5H5kF+T9LXByeH5OB6/X6eL1en6+T4hneMgZw+X6VVBhk701SXYbA5+fnkzRcRY23rvP9/f1ra3J0PIJ/9vTv7u5ePj4+XFyGAItR5jkpIe2Ta5K6wJ+JehrIGuAR+Es2ZJoVASm/scho3TqMP2lukZfb8O0OAIDcenN3rXUzKF9EvgkeLxUBBkWCRWXKwmbV3FuUSkdA3BLAZkta5TcCHpsuASa8/VACxAm6mUztB2WMIITAZDZoEiTmaK9KkwCregDJhKcTgWGiKeiUAVmUY/CW7upzlcAIQOlcShIAE0AvsgGM5i7ohE5xky1OwmK03V0lwLIRgOhI1+yZFsAeeNkydmR39eoSYLWMUo/GsACak0ORM6eS1lX7oQOeE8f4kI3uNUQAKw7AU36CiCTHbEUC0KzXc/NZz/qWDBOIAMv6NC8w6TT3Q5mxBRCBYd3Sn30OE8CwHFVRFrBnoss8Ip1uuaGH7AWPjSkCLGgBzEkADN2ODJ/1a3amCWDMO6fJBHOISJKpstyYSuLZSJMTjU0EsK8olwDL/QCJclOz9OSsDeNbbhMErPkYZQOQLKiU2ANBWTq0q6NR49VYWLDhtpkAvgSkAhhJpLewom1Isi4/funvkV0EcBxr2UQUEswhIkkprZYbeltlNwEcO1Hu7gdIqtxSprCzRQ4hEKNsSiUCpMQCLunQLjP1upfEIQRAKMCPPHPRmHnJOZk6oZOvmW0fQkBRJLpuOeQAlYXD98NuAoDPQToRNJ9KIwmv3NwAOPbM0C4CA+AXZ+WmJmOH7IfNBNbAU+vIgp4nWdKaFOV4/DKVBJ3UGWxsIgCQljOAq0wuiDBUUdZYuqS7+/2wiYDAm29jCyIB4lsV5RGG6IdGdsvfwiLJpt61H6YJ5AAyXHxAM+CZiwBXoywdCFeZIsvY6MkUgQg+1XFmvPnZXgC7Ud6zH4YJKCJEygN/8gBk5E4iwdrVKKvcVjOV28vbQwQAr7p3fzZ0HOf2U9sjmdsUyVamIJ/slI0ugR54HJdGW32H7Mj7wXwcKW2vEuiAZ9OasiiNl33IioR36qQox0wZu3mmSptNAgL/X37HEQnAVgDlL/1bqQSpvsmU+ulqEhDrobM+WZpotKIsn+FfSzJVHRbMQVJz5nIJxOPSKNIh/TGCdHcJtmYNRBKGXEUggjdK0VHzrI/z/x4DLQWiOnUGllXfHwwBpeisyLxLXkqJaR/xMawjEuGnyNJXp/+eOzAEZDBEWc/wYSx/5ouObOc+Rtu5f0Mgn/gu7V8CX52pvwAAAP//pQZ+UwAAAAZJREFUAwBCEOZ/U8dgbwAAAABJRU5ErkJggg==
// @include https://x.com/*
// @include https://twitter.com/*
// @exclude *://x.com/i/flow/*
// @license MIT
// @run-at document-start
// @noframes
// @grant GM_registerMenuCommand
// @grant GM_openInTab
// @grant GM.openInTab
// @grant GM_addStyle
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_deleteValue
// @grant GM_xmlhttpRequest
// @grant GM_download
// ==/UserScript==
(function () {
'use strict';
/*!
* Copyright (c) 2026 - 2026, Levivi. All rights reserved.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
*
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
var css_248z$1 = "li[role=listitem]>div>div>div>div:not(:last-child){filter:none}li[role=listitem]>div>div>div>div+div:last-child{display:none}";
var css_248z = ".x-master-dl{margin-left:12px;order:99}.x-master-dl:hover>div>div>div>div{color:#1da1f2}.x-master-dl:hover>div>div>div>div>div{background-color:rgba(29,161,242,.1)}.x-master-dl:active>div>div>div>div>div{background-color:rgba(29,161,242,.2)}.x-master-dl:hover svg{color:#1da1f2}.x-master-dl:hover div:first-child:not(:last-child){background-color:rgba(29,161,242,.1)}.x-master-dl:active div:first-child:not(:last-child){background-color:rgba(29,161,242,.2)}.x-master-dl.tmd-media{position:absolute;right:0}.x-master-dl.tmd-media>div{border-radius:99px;display:flex;margin:2px}.x-master-dl.tmd-media>div>div{color:#fff;display:flex;margin:6px}.x-master-dl.tmd-media:hover>div{background-color:hsla(0,0%,100%,.6)}.x-master-dl.tmd-media:hover>div>div{color:#1da1f2}.x-master-dl.tmd-media:not(:hover)>div>div{filter:drop-shadow(0 0 1px #000)}.x-master-dl g{display:none}.x-master-dl.completed g.completed,.x-master-dl.download g.download,.x-master-dl.failed g.failed,.x-master-dl.loading g.loading{display:unset}.x-master-dl.loading svg{animation:spin 1s linear infinite}.x-master-dl.download g.download{color:#1da1f2}.tmd-btn{background-color:#1da1f2;border-radius:99px;color:#fff;padding:0 20px}.tmd-btn,.tmd-tag{display:inline-block}.tmd-tag{background-color:#fff;border:1px solid #1da1f2;border-radius:10px;color:#1da1f2;font-weight:700;margin:5px;padding:0 10px}.tmd-btn:hover{background-color:rgba(29,161,242,.9)}.tmd-tag:hover{background-color:rgba(29,161,242,.1)}.tmd-notifier{background:#fff;border:1px solid #ccc;border-radius:8px;bottom:16px;color:#000;display:none;left:16px;padding:4px;position:fixed}.tmd-notifier.running{align-items:center;display:flex}.tmd-notifier label{align-items:center;display:inline-flex;margin:0 8px}.tmd-notifier label:before{background-position:50%;background-repeat:no-repeat;content:\" \";height:16px;width:32px}.tmd-notifier label:first-child:before{background-image:url(\"data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2216%22 height=%2216%22 viewBox=%220 0 24 24%22><path d=%22M3,14 v5 q0,2 2,2 h14 q2,0 2,-2 v-5 M7,10 l4,4 q1,1 2,0 l4,-4 M12,3 v11%22 fill=%22none%22 stroke=%22%23666%22 stroke-width=%222%22 stroke-linecap=%22round%22 /></svg>\")}.tmd-notifier label:nth-child(2):before{background-image:url(\"data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2216%22 height=%2216%22 viewBox=%220 0 24 24%22><path d=%22M12,2 a1,1 0 0 1 0,20 a1,1 0 0 1 0,-20 M12,5 v7 h6%22 fill=%22none%22 stroke=%22%23999%22 stroke-width=%222%22 stroke-linejoin=%22round%22 stroke-linecap=%22round%22 /></svg>\")}.tmd-notifier label:nth-child(3):before{background-image:url(\"data:image/svg+xml;charset=utf8,<svg xmlns=%22http://www.w3.org/2000/svg%22 width=%2216%22 height=%2216%22 viewBox=%220 0 24 24%22><path d=%22M12,0 a2,2 0 0 0 0,24 a2,2 0 0 0 0,-24%22 fill=%22%23f66%22 stroke=%22none%22 /><path d=%22M14.5,5 a1,1 0 0 0 -5,0 l0.5,9 a1,1 0 0 0 4,0 z M12,17 a2,2 0 0 0 0,5 a2,2 0 0 0 0,-5%22 fill=%22%23fff%22 stroke=%22none%22 /></svg>\")}.x-master-dl.tmd-img{bottom:0;display:none!important;position:absolute;right:0}.x-master-dl.tmd-img>div{background-color:hsla(0,0%,100%,.6);border-radius:99px;display:flex;margin:2px}.x-master-dl.tmd-img>div>div{color:#fff!important;display:flex;margin:6px}.x-master-dl.tmd-img:not(:hover)>div>div{filter:drop-shadow(0 0 1px #000)}.x-master-dl.tmd-img:hover>div>div{color:#1da1f2}.tmd-img.completed,.tmd-img.failed,.tmd-img.loading,:hover>.x-master-dl.tmd-img{display:block!important}.tweet-detail-action-item{width:20%!important}@keyframes spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}";
const language = {
"zh": {
"dateFormat": {
"week": ["日", "一", "二", "三", "四", "五", "六"]
},
"download": {
"download": "下载",
"completed": "下载完成",
"tip": "点击下载视频",
"preparing": "正在准备下载(如果失败,请手动操作)"
},
"menuCommand": {
"settings": "设置",
"titleDateFormat": "时间格式设置:",
"buttonClose": "关闭",
"simplifyMode": "简化模式",
"turnOn": "打开",
"turnOff": "关闭"
}
},
"en": {
"dateFormat": {
"week": ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]
},
"download": {
"download": "Download",
"completed": "Download Completed",
"tip": "Click to download video",
"preparing": "Preparing to download (if failed, please do it manually)"
},
"menuCommand": {
"settings": "Settings",
"titleDateFormat": "Time format settings:",
"buttonClose": "Close",
"simplifyMode": "Simplify Mode",
"turnOn": "Turn on",
"turnOff": "Turn off"
}
},
"ja": {
"dateFormat": {
"week": ["日", "月", "火", "水", "木", "金", "土"]
},
"download": {
"download": "ダウンロード",
"completed": "ダウンロード完了",
"tip": "クリックしてビデオをダウンロード",
"preparing": "ダウンロードの準備中(失敗する場合は手動で行ってください)"
},
"menuCommand": {
"settings": "設定",
"titleDateFormat": "時刻形式の設定:",
"buttonClose": "閉鎖",
"simplifyMode": "簡易モード",
"turnOn": "オン",
"turnOff": "オフ"
}
},
"fr": {
"dateFormat": {
"week": ["Dim", "Lun", "Mar", "Mer", "Jeu", "Ven", "Sam"]
},
"download": {
"download": "télécharger",
"completed": "éléchargement terminé",
"tip": "Cliquez pour télécharger la vidéo",
"preparing": "Préparation du téléchargement (en cas d'échec, veuillez le faire manuellement)"
},
"menuCommand": {
"settings": "installation",
"titleDateFormat": "Paramètres du format de l'heure :",
"buttonClose": "fermeture",
"simplifyMode": "Mode simplifié",
"turnOn": "Activer",
"turnOff": "Désactiver"
}
},
"de": {
"dateFormat": {
"week": ["Son", "Mon", "Die", "Mit", "Don", "Fre", "Sam"]
},
"download": {
"download": "herunterladen",
"completed": "Download abgeschlossen",
"tip": "Klicken Sie hier, um das Video herunterzuladen",
"preparing": "Vorbereitung für den Download (falls der Download fehlschlägt, führen Sie ihn bitte manuell durch)"
},
"menuCommand": {
"settings": "aufstellen",
"titleDateFormat": "Einstellungen für das Zeitformat:",
"buttonClose": "Schließung",
"simplifyMode": "Vereinfachter Modus",
"turnOn": "Einschalten",
"turnOff": "Ausschalten"
}
},
"it": {
"dateFormat": {
"week": ["Dom", "Lun", "Mar", "Mer", "Gio", "Ven", "Sab"]
},
"download": {
"download": "scaricamento",
"completed": "Download completato",
"tip": "Fare clic per scaricare il video",
"preparing": "Preparazione per il download (se fallisce, eseguilo manualmente)"
},
"menuCommand": {
"settings": "impostare",
"titleDateFormat": "Impostazioni del formato dell'ora:",
"buttonClose": "chiusura",
"simplifyMode": "Modalità semplificata",
"turnOn": "Attiva",
"turnOff": "Disattiva"
}
},
"ko": {
"dateFormat": {
"week": ["일", "월", "화", "수", "목", "금", "토"]
},
"download": {
"download": "다운로드",
"completed": "다운로드 완료",
"tip": "비디오를 다운로드하려면 클릭하세요",
"preparing": "다운로드 준비 중 (실패할 경우 수동으로 진행해주세요)"
},
"menuCommand": {
"settings": "설정",
"titleDateFormat": "시간 형식 설정:",
"buttonClose": "폐쇄",
"simplifyMode": "간소화 모드",
"turnOn": "켜기",
"turnOff": "끄기"
}
},
"ru": {
"dateFormat": {
"week": ["ВС", "ПН", "ВТ", "СР", "ЧТ", "ПТ", "СБ"]
},
"download": {
"download": "скачать",
"completed": "Загрузка завершена",
"tip": "Нажмите, чтобы скачать видео",
"preparing": "Подготовка к загрузке (если не получается, сделайте это вручную)"
},
"menuCommand": {
"settings": "настраивать",
"titleDateFormat": "Настройки формата времени:",
"buttonClose": "закрытие",
"simplifyMode": "Упрощенный режим",
"turnOn": "Включить",
"turnOff": "Выключить"
}
},
"pt": {
"dateFormat": {
"week": ["Dom", "Seg", "Ter", "Qua", "Qui", "Sex", "Sáb"]
},
"download": {
"download": "descargar",
"completed": "Descarga completa",
"tip": "Clique para baixar o vídeo",
"preparing": "Preparação para download (se falhar, faça-o manualmente)"
},
"menuCommand": {
"settings": "configuración",
"titleDateFormat": "Configuración de formato de hora:",
"buttonClose": "cierre",
"simplifyMode": "Modo simplificado",
"turnOn": "Ativar",
"turnOff": "Desativar"
}
},
"es": {
"dateFormat": {
"week": ["DOM", "LUN", "MAR", "MIER", "JUE", "VIE", "SÁB"]
},
"download": {
"download": "descargar",
"completed": "Descarga completa",
"tip": "Haga clic para descargar el vídeo",
"preparing": "Preparándose para la descarga (si falla, hágalo manualmente)"
},
"menuCommand": {
"settings": "configuración",
"titleDateFormat": "Configuración de formato de hora:",
"buttonClose": "cierre",
"simplifyMode": "Modo simplificado",
"turnOn": "Activar",
"turnOff": "Desactivar"
}
},
"th": {
"dateFormat": {
"week": ["วันอาทิตย์", "วันจันทร์", "วันอังคาร", "วันพุธ", " วันพฤหัสบดี", "วันศุกร์ ", "วันเสาร์ "]
},
"download": {
"download": "ดาวน์โหลด",
"completed": "ดาวน์โหลดเสร็จสมบูรณ์",
"tip": "คลิกเพื่อดาวน์โหลดวิดีโอ",
"preparing": "กำลังเตรียมการดาวน์โหลด (หากล้มเหลว กรุณาดำเนินการด้วยตนเอง)"
},
"menuCommand": {
"settings": "ตั้งค่า",
"titleDateFormat": "การตั้งค่ารูปแบบเวลา:",
"buttonClose": "ปิด",
"simplifyMode": "โหมดแบบย่อ",
"turnOn": "เปิด",
"turnOff": "ปิด"
}
},
"tr": {
"dateFormat": {
"week": ["Pazar", "Pazartesi", "Salı", "Çarşamba", "Perşembe", "Cuma", "Cumartesi"]
},
"download": {
"download": "indirmek",
"completed": "İndirme tamamlandı",
"tip": "Videoyu indirmek için tıklayın",
"preparing": "İndirmeye hazırlanıyor (başarısız olursa lütfen manuel olarak yapın)"
},
"menuCommand": {
"settings": "kurmak",
"titleDateFormat": "Saat formatı ayarları:",
"buttonClose": "kapatma",
"simplifyMode": "Basitleştirilmiş mod",
"turnOn": "Aç",
"turnOff": "Kapat"
}
},
"nl": {
"dateFormat": {
"week": ["zondag", "maandag", "dinsdag", "woensdag", "donderdag", "vrijdag", "zaterdag"]
},
"download": {
"download": "downloaden",
"completed": "Downloaden voltooid",
"tip": "Klik om video te downloaden",
"preparing": "Voorbereiden voor downloaden (als dit mislukt, doe dit dan handmatig)"
},
"menuCommand": {
"settings": "opgezet",
"titleDateFormat": "Instellingen tijdformaat:",
"buttonClose": "sluiting",
"simplifyMode": "Vereenvoudigde modus",
"turnOn": "Inschakelen",
"turnOff": "Uitschakelen"
}
}
};
const lang = (navigator.language || navigator.userLanguage || "").slice(0, 2).toLowerCase() || "en";
const Commonlanguage = language[lang] ?? language["en"];
const FMT = 16;
const XSettingsDialog = {
number: Math.ceil(Math.random() * 1e8),
formats: [
{ format: "Do nothing", example: "N/A" },
{ format: "ISO 8601 T", example: "2025-07-09T22:57:30" },
{ format: "ISO 8601 (space + s)", example: "2025-07-09 22:57:30" },
{ format: "ISO 8601 (space, no s)", example: "2025-07-09 22:57" },
{ format: "US: MMM d, yyyy h:mm A", example: "Jul 9, 2025, 10:57 PM" },
{ format: "US: EEE, MMM d, yyyy h:mm A", example: "Wed, Jul 9, 2025, 10:57 PM" },
{ format: "US: MM/dd/yyyy h:mm A", example: "07/09/2025 10:57 PM" },
{ format: "US: MM/dd/yyyy HH:mm", example: "07/09/2025 22:57" },
{ format: "EU/UK: dd/MM/yyyy HH:mm", example: "09/07/2025 22:57" },
{ format: "DE: dd.MM.yyyy, HH:mm", example: "09.07.2025, 22:57" },
{ format: "EU long: d MMMM yyyy, HH:mm", example: "9 July 2025, 22:57" },
{ format: "CN: yyyy年M月d日 HH:mm", example: "2025年7月9日 22:57" },
{ format: "East Asia: yyyy/MM/dd HH:mm", example: "2025/07/09 22:57" },
{ format: "UK short: EEE d MMM yyyy HH:mm", example: "Wed 9 Jul 2025 22:57" },
{ format: "Unix ctime (en)", example: "Wed Jul 9 22:57:30 2025" },
{ format: "US full: EEEE, MMMM d, yyyy h:mm:ss A", example: "Wednesday, July 9, 2025, 10:57:30 PM" },
{ format: "Compact: hh.mm A·mmm d,yy", example: "10.57 PM·Jul 9,25" },
{ format: "TW ROC: Myyy-MM-dd HH:mm", example: "M114-07-09 22:57" }
],
make() {
const dialog = document.createElement("div");
dialog.className = "dialog_u_" + this.number;
dialog.style.all = "initial";
dialog.style.backgroundColor = "#fff";
dialog.style.border = "1px solid #e1e8ed";
dialog.style.borderRadius = "10px";
dialog.style.boxShadow = "0 16px 48px rgba(15, 20, 25, 0.14), 0 4px 16px rgba(15, 20, 25, 0.08)";
dialog.style.fontFamily = "monospace";
dialog.style.fontSize = "12px";
dialog.style.width = "640px";
dialog.style.maxWidth = "calc(100vw - 24px)";
dialog.style.boxSizing = "border-box";
dialog.style.paddingLeft = "8px";
dialog.style.paddingRight = "8px";
dialog.style.paddingTop = "8px";
dialog.style.paddingBottom = "8px";
dialog.style.position = "fixed";
dialog.style.right = "8px";
dialog.style.top = "8px";
dialog.style.zIndex = "2147483647";
dialog.style.overflow = "auto";
const escHtml = (s) => String(s).replace(/&/g, "&").replace(/</g, "<").replace(/>/g, ">");
let formatsHtml = `<table style="width:100%;border: 1px solid #c0bfbf;border-collapse: collapse;">`;
for (let i = 1; i <= this.formats.length; i++) {
if (i % 2 !== 0) {
formatsHtml += `<tr style="width:100%;border: 1px solid #c0bfbf;">`;
}
const item = this.formats[i - 1];
const exTitle = String(item.example).replace(/"/g, """);
const fmtLine = escHtml(item.format);
const exLine = escHtml(item.example);
formatsHtml += `<td width="50%" style="border: 1px solid #c0bfbf;padding: 5px;vertical-align:top;" title="${exTitle}"><div><div style="color:#000;font-size:13px;"><input type="radio" name="fmt" value="${i - 1}" class="top_r" /><b>【${i}】${fmtLine}</b></div><div style="color:#555;font-size:11px;margin-top:4px;padding-left:20px;">${exLine}</div></div></td>`;
if (i % 2 === 0) {
formatsHtml += `</tr>`;
}
}
if (this.formats.length % 2 !== 0) {
formatsHtml += `</tr>`;
}
formatsHtml += `</table>`;
const btnLabel = escHtml(Commonlanguage.menuCommand.buttonClose);
const titleText = escHtml(Commonlanguage.menuCommand.titleDateFormat);
dialog.innerHTML = `<div style="font-size:17px;font-weight:700;margin:15px auto;padding:0 4px;text-align:center;font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;color:#0f1419;">` + titleText + `</div><div>` + formatsHtml + `</div><div style="margin-top:15px;text-align:center;"><button type="button" name="closex" style="appearance:none;-webkit-appearance:none;margin:0;border:1px solid #1d9bf0;border-radius:999px;padding:5px 18px;font-size:14px;font-weight:600;font-family:system-ui,-apple-system,'Segoe UI',Roboto,sans-serif;background:linear-gradient(180deg,#1d9bf0 0%,#1a8cd8 100%);color:#fff;cursor:pointer;box-shadow:0 2px 10px rgba(29,155,240,0.35);">` + btnLabel + `</button></div>`;
dialog.style.display = "none";
return dialog;
},
addEvent(dialog) {
dialog.querySelector("button[name='closex']").addEventListener(
"click",
() => {
for (const e of dialog.querySelectorAll('input[name="fmt"]')) {
if (e.checked) {
fmt = e.value;
break;
}
}
GM_setValue("fmt", fmt);
dialog.style.display = "none";
},
false
);
},
init() {
const dialog = this.make();
this.addEvent(dialog);
document.body.appendChild(dialog);
GM_registerMenuCommand(Commonlanguage.menuCommand.settings, () => {
if (dialog.style.display !== "none")
return;
const input = dialog.querySelector(
`input[name="fmt"][value="${String(fmt)}"]`
);
if (input)
input.checked = true;
dialog.style.display = "block";
});
const isSimplifyMode = GM_getValue("x_simplify_mode", "") === "true";
GM_registerMenuCommand(Commonlanguage.menuCommand.simplifyMode + `(${isSimplifyMode ? Commonlanguage.menuCommand.turnOff : Commonlanguage.menuCommand.turnOn})`, () => {
if (isSimplifyMode) {
GM_deleteValue("x_simplify_mode");
} else {
GM_setValue("x_simplify_mode", "true");
}
location.reload(true);
});
}
};
let fmt = GM_getValue("fmt", FMT);
(function syncFmtIndex() {
const max = XSettingsDialog.formats.length - 1;
const v = parseInt(String(fmt), 10);
if (Number.isNaN(v) || v < 0 || v > max) {
const next = Math.min(Math.max(parseInt(String(FMT), 10), 0), max);
fmt = String(next);
GM_setValue("fmt", fmt);
} else {
fmt = String(v);
}
})();
const XDateFormat = {
df: function(date, f) {
const WEEK_FULL = [
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday"
];
const MONTH_SHORT = [
"Jan",
"Feb",
"Mar",
"Apr",
"May",
"Jun",
"Jul",
"Aug",
"Sep",
"Oct",
"Nov",
"Dec"
];
const MONTH_FULL = [
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
];
const pad = (num) => ("0" + num).slice(-2);
const YE = date.getFullYear();
const YE2 = YE.toString().slice(-2);
const YM = YE - 1911;
const MO = pad(date.getMonth() + 1);
const MO_IDX = date.getMonth();
const MO_NAME = MONTH_SHORT[MO_IDX];
const MO_NAME_FULL = MONTH_FULL[MO_IDX];
const DA = pad(date.getDate());
const dNum = parseInt(DA, 10);
const weekAbbr = () => WEEK_FULL[date.getDay()].slice(0, 3);
const HO = pad(date.getHours());
const MI = pad(date.getMinutes());
const SE = pad(date.getSeconds());
const h12 = date.getHours() % 12 || 12;
const HO12 = pad(h12);
const AMPM = date.getHours() >= 12 ? "PM" : "AM";
const F = [
`${YE}-${MO}-${DA}T${HO}:${MI}:${SE}`,
`${YE}-${MO}-${DA} ${HO}:${MI}:${SE}`,
`${YE}-${MO}-${DA} ${HO}:${MI}`,
`${MO_NAME} ${dNum}, ${YE}, ${HO12}:${MI} ${AMPM}`,
`${weekAbbr()}, ${MO_NAME} ${dNum}, ${YE}, ${HO12}:${MI} ${AMPM}`,
`${MO}/${DA}/${YE} ${HO12}:${MI} ${AMPM}`,
`${MO}/${DA}/${YE} ${HO}:${MI}`,
`${DA}/${MO}/${YE} ${HO}:${MI}`,
`${DA}.${MO}.${YE}, ${HO}:${MI}`,
`${dNum} ${MO_NAME_FULL} ${YE}, ${HO}:${MI}`,
`${YE}年${MO_IDX + 1}月${dNum}日 ${HO}:${MI}`,
`${YE}/${MO}/${DA} ${HO}:${MI}`,
`${weekAbbr()} ${dNum} ${MO_NAME} ${YE} ${HO}:${MI}`,
`${weekAbbr()} ${MO_NAME} ${String(dNum).padStart(2, " ")} ${HO}:${MI}:${SE} ${YE}`,
`${WEEK_FULL[date.getDay()]}, ${MO_NAME_FULL} ${dNum}, ${YE}, ${HO12}:${MI}:${SE} ${AMPM}`,
`${HO12}.${MI} ${AMPM}·${MO_NAME} ${dNum},${YE2}`,
`M${YM}-${MO}-${DA} ${HO}:${MI}`
];
return F[f] ?? F[0];
},
repldatetime: function() {
const MYNAME = "peter_parker_x1190";
const SEL = 'main div[data-testid="primaryColumn"] section article time[datetime*=":"]';
const SEL_2 = 'div[aria-labelledby="modal-header"] div[data-testid^="User-Name"] time[datetime]';
const SEL_3 = 'div[aria-labelledby="modal-header"] div[aria-label] time[datetime]';
const SEL_4 = 'main section[aria-labelledby="detail-header"] article div[data-testid^="User-Name"] time[datetime]';
const SEL_5 = 'main section div[data-testid="conversation"] div[aria-label] time[datetime]';
document.querySelectorAll(
SEL + ", " + SEL_2 + ", " + SEL_3 + ", " + SEL_4 + ", " + SEL_5
).forEach((e) => {
if (fmt != 0) {
const SEL_ADD = "span.us-" + MYNAME;
let d = e.getAttribute("datetime");
let df = this.df(new Date(d), fmt - 1);
let pe = e.parentNode;
let old = pe.querySelectorAll(SEL_ADD);
if (!old.length) {
let span = document.createElement("span");
span.className = "us-" + MYNAME;
span.setAttribute("datetime", d);
span.setAttribute("local-datetime", df);
span.textContent = df;
span.style = e.style;
e.style.setProperty("display", "none");
pe.appendChild(span);
} else if (old[0].getAttribute("local-datetime") != df) {
old[0].setAttribute("local-datetime", df);
old[0].textContent = df;
old[0].style = e.style;
}
}
});
}
};
const XDownloadUI = {
showSensitive: true,
svg: `
<g class="download"><path d="M11.99 16l-5.7-5.7L7.7 8.88l3.29 3.3V2.59h2v9.59l3.3-3.3 1.41 1.42-5.71 5.7zM21 15l-.02 3.51c0 1.38-1.12 2.49-2.5 2.49H5.5C4.11 21 3 19.88 3 18.5V15h2v3.5c0 .28.22.5.5.5h12.98c.28 0 .5-.22.5-.5L19 15h2z" /></g>
<g class="completed"><path d="M3,14 v5 q0,2 2,2 h14 q2,0 2,-2 v-5 M7,10 l3,4 q1,1 2,0 l8,-11" fill="none" stroke="#1DA1F2" stroke-width="2" stroke-linecap="round" /></g>
<g class="loading"><circle cx="12" cy="12" r="10" fill="none" stroke="#1DA1F2" stroke-width="4" opacity="0.4" /><path d="M12,2 a10,10 0 0 1 10,10" fill="none" stroke="#1DA1F2" stroke-width="4" stroke-linecap="round" /></g>
<g class="failed"><circle cx="12" cy="12" r="11" fill="#f33" stroke="currentColor" stroke-width="2" opacity="0.8" /><path d="M14,5 a1,1 0 0 0 -4,0 l0.5,9.5 a1.5,1.5 0 0 0 3,0 z M12,17 a2,2 0 0 0 0,4 a2,2 0 0 0 0,-4" fill="#fff" stroke="none" /></g>
`,
isTweetdeck: () => window.location.host.includes("tweetdeck"),
extractStatusId: (url) => url ? (url.match(/\/status\/(\d+)/) || [null, null])[1] : null,
uniqueArray: (arr) => Array.from(new Set(arr)),
getExtension: (url) => new URL(url).pathname.split(".").pop() || null,
sanitizeFilename: (filename) => filename.replace(/[\/\\\?\%\*\:\|\\"<>\r\n]/g, "_"),
setStatus: function(btn, classnames, title, style) {
if (classnames) {
btn.classList.remove("download", "completed", "loading", "failed");
btn.classList.add(...classnames);
}
if (title)
btn.title = title;
if (style)
btn.style.cssText = style;
},
clickDownloadEvent: function(btn, statusIds) {
const filenameTemplate = "{name}";
const uniqueStatusIds = this.uniqueArray(statusIds);
const handleDownload = (url, filename, defaultExt) => {
return new Promise((resolve, reject) => {
const finalFilename = filename + "." + (this.getExtension(url) ?? defaultExt);
GM_download({
url,
name: finalFilename,
onload: () => {
XDownloadUI.setStatus(btn, ["completed"], "下载完成");
resolve();
},
onerror: (error) => {
reject(error);
}
});
});
};
this.setStatus(btn, ["loading"], "正在准备下载...");
const downloadPromises = uniqueStatusIds.map((statusId) => {
const media = XDownload.mediaMap.get(statusId);
if (!media) {
return Promise.reject(
`Media data not found for status ID: ${statusId}`
);
}
const { entityId, video, photo, text } = media;
const filename = filenameTemplate.replace(
"{name}",
this.sanitizeFilename(text) || entityId
);
if (video) {
return handleDownload(video, filename, "mp4");
} else if (photo) {
return handleDownload(photo, filename, "jpg");
}
return Promise.reject("No media to download");
});
Promise.allSettled(downloadPromises).then((results) => {
const anySuccess = results.some(
(result) => result.status === "fulfilled"
);
if (anySuccess) {
this.setStatus(btn, ["completed"], "下载成功");
} else {
this.setStatus(btn, ["failed"], "未找到媒体资源");
}
}).catch((error) => {
this.setStatus(btn, ["failed"], "下载失败");
});
},
addButtonTo: function(article) {
if (article.dataset.detected)
return;
article.dataset.detected = "true";
const statusIds = Array.from(
article.querySelectorAll('a[href*="/status/"]')
).map((el) => this.extractStatusId(el.href)).filter((id) => id);
if (statusIds.length === 0)
return;
const mediaSelector = [
'a[href*="/photo/1"]',
'div[role="progressbar"]',
'button[data-testid="playButton"]',
'div[data-testid="videoComponent"]',
'a[href="/settings/content_you_see"]',
"div.media-image-container",
"div.media-preview-container",
'div[aria-labelledby]>div:first-child>div[role="button"][tabindex="0"]'
];
const hasMedia = article.querySelector(mediaSelector.join(","));
if (hasMedia) {
const btnGroup = article.querySelector(
'div[role="group"]:last-of-type, ul.tweet-actions, ul.tweet-detail-actions'
);
if (btnGroup) {
const btnShare = Array.from(
btnGroup.querySelectorAll(
":scope>div>div, li.tweet-action-item>a, li.tweet-detail-action-item>a"
)
).pop().parentNode;
const btnDownload = btnShare.cloneNode(true);
btnDownload.style.marginLeft = "10px";
btnDownload.querySelector("button")?.removeAttribute("disabled");
const svgContainer = this.isTweetdeck() ? btnDownload.firstElementChild : btnDownload.querySelector("svg");
if (svgContainer) {
if (this.isTweetdeck()) {
svgContainer.innerHTML = `<svg viewBox="0 0 20 20" width="15" height="15">${this.svg}</svg>`;
svgContainer.removeAttribute("rel");
btnDownload.classList.replace("pull-left", "pull-right");
} else {
svgContainer.innerHTML = this.svg;
}
}
this.setStatus(btnDownload, ["x-master-dl", "download"], "下载媒体");
btnGroup.insertBefore(btnDownload, btnShare.nextSibling);
btnDownload.onclick = () => this.clickDownloadEvent(btnDownload, statusIds);
if (this.showSensitive) {
article.querySelector(
'div[aria-labelledby] div[role="button"][tabindex="0"]:not([data-testid]) > div[dir] > span > span'
)?.click();
}
}
}
const imgs = article.querySelectorAll('a[href*="/photo/"]');
if (imgs.length > 1) {
imgs.forEach((img) => {
const imgStatusId = this.extractStatusId(img.href);
if (!imgStatusId || img.parentNode.querySelector(".x-master-dl.tmd-img"))
return;
const btnDownload = document.createElement("div");
btnDownload.style.cssText = "position: absolute; top: 0; right: 0; z-index: 10; margin: 5px;";
btnDownload.innerHTML = `<div><div><svg viewBox="0 0 20 20" width="15" height="15">${this.svg}</svg></div></div>`;
this.setStatus(
btnDownload,
["x-master-dl", "tmd-img", "download"],
"下载图片"
);
img.parentNode.appendChild(btnDownload);
btnDownload.onclick = (e) => {
e.preventDefault();
e.stopPropagation();
this.clickDownloadEvent(btnDownload, [imgStatusId]);
};
});
}
},
addButtonToMedia: function(listitems) {
listitems.forEach((li) => {
if (li.dataset.detected)
return;
li.dataset.detected = "true";
const statusElement = li.querySelector('a[href*="/status/"]');
const statusId = statusElement ? this.extractStatusId(statusElement.href) : null;
if (!statusId)
return;
const btnDownload = document.createElement("div");
btnDownload.innerHTML = `<div><div><svg viewBox="0 0 20 20" width="15" height="15">${this.svg}</svg></div></div>`;
this.setStatus(
btnDownload,
["x-master-dl", "tmd-media", "download"],
"下载媒体"
);
li.appendChild(btnDownload);
btnDownload.onclick = () => this.clickDownloadEvent(btnDownload, [statusId]);
});
},
detect: function(node) {
const article = node.tagName === "ARTICLE" && node || node.tagName === "DIV" && (node.querySelector("article") || node.closest("article"));
if (article) {
this.addButtonTo(article);
}
const listitems = node.tagName === "LI" && node.getAttribute("role") === "listitem" ? [node] : node.tagName === "DIV" && node.querySelectorAll('li[role="listitem"]');
if (listitems) {
this.addButtonToMedia(listitems);
}
}
};
const XDownload = {
mediaMap: /* @__PURE__ */ new Map(),
findParent: function(obj, targetKey) {
let result = [];
if (typeof obj === "object" && obj !== null) {
for (let key in obj) {
if (obj.hasOwnProperty(key)) {
if (key === targetKey) {
result.push(obj);
}
result = result.concat(this.findParent(obj[key], targetKey));
}
}
} else if (Array.isArray(obj)) {
for (let item of obj) {
result = result.concat(this.findParent(item, targetKey));
}
}
return result;
},
extractMediaFromResponse: function(url, responseText) {
try {
const data = JSON.parse(responseText);
const entities = this.findParent(data, "extended_entities");
if (entities.length === 0) {
return;
}
for (let entity of entities) {
if (!entity.extended_entities)
continue;
const entityId = entity.id_str || entity.conversation_id_str;
(entity.extended_entities.media || []).filter((m) => ["video", "animated_gif", "photo"].includes(m.type)).forEach((m) => {
const bestVideo = m.video_info?.variants?.filter((v) => v.content_type === "video/mp4").sort((a, b) => b.bitrate - a.bitrate)[0];
const text = (entity.full_text || "").split("https://t.co")[0]?.trim()?.slice(0, 50) || entityId;
const mediaItem = {
entityId,
id: m.id_str,
thumbnail: m.media_url_https?.split(".jpg")[0],
video: bestVideo?.url,
photo: m.media_url_https,
text
};
this.mediaMap.set(entityId, mediaItem);
});
}
} catch (e) {
}
},
hookXMLHttpRequest: function() {
const self = this;
const originalOpen = XMLHttpRequest.prototype.open;
const originalSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.open = function(method, url) {
this._url = url;
return originalOpen.apply(this, arguments);
};
XMLHttpRequest.prototype.send = function() {
this.addEventListener("load", function() {
if (this._url && this.response) {
try {
if (this.responseType === "" || this.responseType === "text") {
self.extractMediaFromResponse(this._url, this.responseText);
}
} catch (e) {
}
}
});
return originalSend.apply(this, arguments);
};
},
init: function() {
this.hookXMLHttpRequest();
GM_addStyle(
css_248z + (this.showSensitive ? css_248z$1 : "")
);
}
};
const X = {
start: function() {
XDownload.init();
XSettingsDialog.init();
const observer = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === 1) {
XDownloadUI.detect(node);
XDateFormat.repldatetime();
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true
});
}
};
(function() {
const enabled = GM_getValue("x_simplify_mode", "") === "true";
if (!enabled) {
return;
}
function update() {
const width = Math.min(document.documentElement.offsetWidth || 800, 800);
if (window.innerWidth === width && document.documentElement.clientWidth === width) {
return;
}
window.__defineGetter__("innerWidth", () => width);
document.documentElement.__defineGetter__("clientWidth", () => width);
if (window.visualViewport) {
window.visualViewport.__defineGetter__("width", () => width);
}
window.dispatchEvent(new Event("resize"));
if (window.visualViewport) {
window.visualViewport.dispatchEvent(new Event("resize"));
}
}
window.addEventListener("load", update);
window.addEventListener("resize", update);
if (window.visualViewport) {
window.visualViewport.addEventListener("resize", update);
}
document.addEventListener("visibilitychange", update);
GM_addStyle(`
#react-root main {
-webkit-flex-grow: 1 !important;
flex-grow: 1 !important;
}
@media (min-width: 800px) {
[role='listbox'] {
max-width: 500px !important;
}
}
`);
update();
})();
const Init = {
async start() {
X.start();
}
};
Init.start();
}());