// ==UserScript==
// @name Autoclear ChatGPT History
// @name:af Verwyder ChatGPT Geskiedenis 🕶️
// @name:am በተሻለ ChatGPT ጉዳዮ ማግኘት 🕶️
// @name:ar مسح تاريخ ChatGPT 🕶️
// @name:az ChatGPT Tarixini Təmizləyin 🕶️
// @name:be Ачысціць гісторыю ChatGPT 🕶️
// @name:bem Lekeni ChatGPT History 🕶️
// @name:bg Изчистете ChatGPT История 🕶️
// @name:bn চ্যাটজিপিটি ইতিহাস মুছে ফেলুন 🕶️
// @name:bo ChatGPT སྐད་དོན་ཚར་བཟོ། 🕶️
// @name:bs Obriši ChatGPT historiju 🕶️
// @name:ca Esborra l'historial de ChatGPT 🕶️
// @name:ceb Autoclear ChatGPT Kasaysayan 🕶️
// @name:ckb بسڕەوەی مێژووی ChatGPT 🕶️
// @name:cs Vymazat ChatGPT Historii 🕶️
// @name:cy Clirio Hanes ChatGPT 🕶️
// @name:da Ryd ChatGPT Historik 🕶️
// @name:de ChatGPT-Verlauf löschen 🕶️
// @name:dv ChatGPT ހިސްތުކުރުން ފުހެވޭނީ 🕶️
// @name:dz ChatGPT སྐད་ཆ་འབད་བཅུ་ 🕶️
// @name:el Διαγραφή Ιστορικού ChatGPT 🕶️
// @name:eo Forviŝi ChatGPT Historion 🕶️
// @name:es Borrar Historial de ChatGPT 🕶️
// @name:et Kustuta ChatGPT Ajalugu 🕶️
// @name:eu Ezabatu ChatGPT Historia 🕶️
// @name:fa پاک کردن تاریخچه ChatGPT 🕶️
// @name:fi Poista ChatGPT:n historia 🕶️
// @name:fo Strika ChatGPT Søgu 🕶️
// @name:fr Effacer l'historique de ChatGPT 🕶️
// @name:fr-CA Effacer l'historique de ChatGPT 🕶️
// @name:gd Lùghdaich Eachdraidh ChatGPT 🕶️
// @name:gl Limpar Historial de ChatGPT 🕶️
// @name:gu આપો ChatGPT ઇતિહાસ 🕶️
// @name:haw Kāpaki Kākoʻo i ka Moʻolelo o ChatGPT 🕶️
// @name:he מחק את היסטוריית ChatGPT 🕶️
// @name:hi ChatGPT इतिहास को हटाएं 🕶️
// @name:hr Izbriši ChatGPT Povijest 🕶️
// @name:ht Efase ChatGPT Istwa 🕶️
// @name:hu ChatGPT Előzmények Törlése 🕶️
// @name:hy Ջնջել ChatGPT-ի Պատմությունը 🕶️
// @name:id Hapus Riwayat ChatGPT 🕶️
// @name:is Eyða ChatGPT Saga 🕶️
// @name:it Cancella Cronologia ChatGPT 🕶️
// @name:ja ChatGPT の履歴を削除する 🕶️
// @name:jv Hapus Riwayat ChatGPT 🕶️
// @name:ka ChatGPT-ის ისტორიის გასუფთავება 🕶️
// @name:kab Sken ChatGPT Tamɣarit 🕶️
// @name:kk ChatGPT Тарихын Жою 🕶️
// @name:km លុបប្រវត្តិសាស្រ្ត ChatGPT 🕶️
// @name:kn ChatGPT ಇತಿಹಾಸವನ್ನು ಅಳಿಸಿ 🕶️
// @name:ko ChatGPT 기록 지우기 🕶️
// @name:ku Çavkaniya ChatGPTê Paqij bike 🕶️
// @name:ky ChatGPT Тарыхын Жок Кыл 🕶️
// @name:la Eximitte Historiam ChatGPT 🕶️
// @name:lb Läschen ChatGPT Geschicht 🕶️
// @name:lo ລຶບບັນດາຕິນມັກສະບັບ ChatGPT 🕶️
// @name:lt Išvalyti ChatGPT Istoriją 🕶️
// @name:lv Notīrīt ChatGPT Vēsturi 🕶️
// @name:mg Mamafa ny ChatGPT Historique 🕶️
// @name:mi Muku ChatGPT Hītori 🕶️
// @name:mk Избриши го Историјата на ChatGPT 🕶️
// @name:ml ചാറ്റ്ജിപിടി ചരിത്രം മായ്ക്കുക 🕶️
// @name:mn ChatGPT Түүхийг устгах 🕶️
// @name:ms Padam Sejarah ChatGPT 🕶️
// @name:mt Ħassar It-Twaħħil ChatGPT 🕶️
// @name:my ဆက်လက် ChatGPT သမိုင်းကို ဖျက်ပစ်နေပါသည် 🕶️
// @name:ne Autoclear ChatGPT इतिहास 🕶️
// @name:nl Wis ChatGPT Geschiedenis 🕶️
// @name:no Autoclear ChatGPT Historie 🕶️
// @name:ny Tikalonso ChatGPT Chisulo 🕶️
// @name:pa ਚੈਟਜੀਪੀਟੀ ਇਤਿਹਾਸ ਮਿਟਾਓ 🕶️
// @name:pap Bula Historia di ChatGPT 🕶️
// @name:pl Wyczyść Historię ChatGPT 🕶️
// @name:ps د ChatGPT د تاریخ پاکول 🕶️
// @name:pt Limpar Histórico do ChatGPT 🕶️
// @name:pt-BR Limpar Histórico do ChatGPT 🕶️
// @name:rn Kwihesha ChatGPT Byinshi 🕶️
// @name:ro Ștergeți Istoricul ChatGPT 🕶️
// @name:ru Очистить Историю ChatGPT 🕶️
// @name:rw Fata ChatGPT Itangazo 🕶️
// @name:sg Mbama ChatGPT Makumbe 🕶️
// @name:si නැවත සංවේදී ChatGPT ඉතිරිකිරීම 🕶️
// @name:sk Vymažte ChatGPT Históriu 🕶️
// @name:sl Počisti Zgodovino ChatGPT 🕶️
// @name:sm Masi ChatGPT Faʻaipoipoga 🕶️
// @name:sn Tirisa ChatGPT Chiremba 🕶️
// @name:so Ka Saar Tareenka ChatGPT 🕶️
// @name:sr Обриши историју ChatGPT-а 🕶️
// @name:sv Rensa ChatGPT Historik 🕶️
// @name:sw Futa Historia ya ChatGPT 🕶️
// @name:ta தானாகவே அழிக்க சேட்ஜிபிடி வரலாற்றை 🕶️
// @name:te ChatGPT చరిత్రను తొలగించు 🕶️
// @name:tg Тозаиши корҳои ChatGPT 🕶️
// @name:th ล้างประวัติศาสตร์ ChatGPT 🕶️
// @name:ti ምርግጋጽ ChatGPT ኣጸዓይ ፈጥር 🕶️
// @name:tk ChatGPT Tarixini Aýyr 🕶️
// @name:tn Futa ChatGPT Tlhahlobo 🕶️
// @name:to Fakatonu ChatGPT History 🕶️
// @name:tpi Kolim ChatGPT Stori 🕶️
// @name:tr ChatGPT Geçmişi Temizle 🕶️
// @name:uk Очистити Історію ChatGPT 🕶️
// @name:ur ChatGPT کی تاریخ صاف کریں 🕶️
// @name:uz ChatGPT Tarixini Tozalash 🕶️
// @name:vi Xóa Lịch Sử ChatGPT 🕶️
// @name:xh Qhipha ChatGPT Isaziso 🕶️
// @name:yi ויסמעקן טשאַטגפּט געשיכטע 🕶️
// @name:zh 自动清除 ChatGPT 历史记录 🕶️
// @name:zh-CN 自动清除 ChatGPT 历史记录 🕶️
// @name:zh-HK 自动清除 ChatGPT 历史记录 🕶️
// @name:zh-SG 自动清除 ChatGPT 历史记录 🕶️
// @name:zh-TW 自动清除 ChatGPT 历史记录 🕶️
// @name:zu Sula ChatGPT Isifundo 🕶️
// @description Auto-clears chat history when visiting chatgpt.com
// @description:af Skoonmaak Chat Geskiedenis wanneer jy chatgpt.com besoek
// @description:am የ chatgpt.com ጸሃይ ታክሲን በማግኘት ታከለው
// @description:ar يقوم تلقائيًا بمسح سجل المحادثات عند زيارة chatgpt.com
// @description:az chatgpt.com-a gedəndə avtomatik olaraq söhbət tarixini təmizləyir
// @description:be Аўтаматычна ачышчае гісторыю чата пры наведванні chatgpt.com
// @description:bem Chibwezache Mphindi Zochitika Pamene Kumatemba chatgpt.com
// @description:bg Автоматично изчиства чат историята при посещение на chatgpt.com
// @description:bn যখন chatgpt.com পরিদর্শন করা হলে অটোমেটিকভাবে চ্যাট ইতিহাস মুছে ফেলে
// @description:bo དཔེར་ན་ chatgpt.com འགྲེལ་བཤད་འདི་ལུ་ སྤྱོད་ཡོད་མི་འདི་བལྟ་མི་བཟོ།
// @description:bs Automatski briše istoriju chata prilikom posjete chatgpt.com
// @description:ca S'elimina automàticament l'historial de xats en visitar chatgpt.com
// @description:ceb Automatic gidut-ana sa kasaysayan sa chat sa pagbisita sa chatgpt.com
// @description:ckb دهستکاریکردنی مێژووی گفتوگۆکان خۆکارانه بۆ سەردانی chatgpt.com
// @description:cs Automaticky vymaže historii chatu při návštěvě chatgpt.com
// @description:cy Mae'n glirio hanes sgwrs yn awtomatig wrth ymweld â chatgpt.com
// @description:da Renser automatisk chatloggen ved besøg på chatgpt.com
// @description:de Löscht den Chatverlauf automatisch beim Besuch von chatgpt.com
// @description:dv chatgpt.com އެކައުންމަދުގެ ޗެކުމު ހުރިހައްދަވާނެ ޗައިންކުރޭ
// @description:dz འཛུགས་མིག་འདི་ལུ་ chatgpt.com འགྲེལ་བཤད་རོགས་བསྐྱེད་ཡོད།
// @description:el Αυτόματη διαγραφή ιστορικού συνομιλίας κατά την επίσκεψη στο chatgpt.com
// @description:eo Memorigo de la babilado aŭtomate malaperas dum vizito ĉe chatgpt.com
// @description:es Borra automáticamente el historial de chat al visitar chatgpt.com
// @description:et Kustutab automaatselt vestluse ajaloo, kui külastate saiti chatgpt.com
// @description:eu Berezgaitasunez ezabatzen du txataren historia chatgpt.com bisitatzen denean
// @description:fa پاک کردن خودکار تاریخچه چت در هنگام بازدید از chatgpt.com
// @description:fi Poistaa keskusteluhistorian automaattisesti käydessä chatgpt.comissa
// @description:fo Auto-rensar spjall søguna tá tú vitjar chatgpt.com
// @description:fr Efface automatiquement l'historique des discussions lors de la visite de chatgpt.com
// @description:fr-CA Efface automatiquement l'historique des discussions lors de la visite de chatgpt.com
// @description:gd Thoir aire bheagachaidh air eachdraidh na còmhraidh nuair a tha thu a' tadhal air chatgpt.com
// @description:gl Limpa automáticamente o historial do chat ao visitar chatgpt.com
// @description:gu chatgpt.com મુકે છે નાતીજે જ્ઞાનવર્ધક ચેટ નો ઇતિહાસ
// @description:haw Mālama haku ʻinoʻino pāhana hoʻohānau mai ana i chatgpt.com
// @description:he מנקה באופן אוטומטי את היסטוריית הצ'אט בעת ביקור ב-chatgpt.com
// @description:hi chatgpt.com पर आवर्तित होने पर चैट इतिहास को स्वचालित रूप से साफ करता है
// @description:hr Automatski briše povijest razgovora prilikom posjeta chatgpt.com
// @description:ht Auto-efase istwa chat la lè vizite chatgpt.com
// @description:hu Automatikusan törli a csevegés előzményeit a chatgpt.com látogatásakor
// @description:hy Պատմությունը ինքնաշխատանքային մաքրում է chatgpt.com այցելելուն դեպի
// @description:id Menghapus otomatis riwayat obrolan saat mengunjungi chatgpt.com
// @description:is Hreinsar sjálfvirkt spjallshönnun þegar heimsókn er gerð á chatgpt.com
// @description:it Cancella automaticamente la cronologia della chat durante la visita a chatgpt.com
// @description:ja chatgpt.com を访れる际に自动的にチャット履歴を消去します
// @description:jv Otomatis ngapus riwayat obrolan nalika ngunjungi chatgpt.com
// @description:ka თავისითად წაშლის ჩათის ისტორიას chatgpt.com-ზე მომსახურების დროს
// @description:kab Ireɣsan Ayren Tisstrir ChatGPT I yur-s achatgpt.com
// @description:kk chatgpt.com-ды көруге бастап автоматты түрде чат тарихын жою
// @description:km លុបសេវាកម្មប្រវត្តិការណ៍ជជែកពីព័ត៌មានមនុស្សកន្លងទៅកាន់ chatgpt.com
// @description:kn chatgpt.com ಸಂದರ್ಶಿಸಿದಾಗ ಸ್ವಯಂಚಾಲಿತವಾಗಿ ಚಾಟ್ ಇತಿಹಾಸವನ್ನು ಅಳಿಸುತ್ತದೆ
// @description:ko chatgpt.com 방문 시 채팅 기록을 자동으로 지웁니다
// @description:ku Çêkirina historyaya chatê di hembêzkirina chatgpt.com de
// @description:ky chatgpt.com барысында түр кат жазмаларын автоматтык түрде жок этиш
// @description:la Automate chat historia clearum cum chatgpt.com adibvisam
// @description:lb Läscht automatesch d'Chat-Geschicht wéini een chatgpt.com besicht
// @description:lo ລຶບປະວັດການສົນທະນາໃນຖ້າເຂົ້າຊົມ chatgpt.com
// @description:lt Automatiškai išvalo pokalbių istoriją apsilankius chatgpt.com
// @description:lv Automātiski notīra čata vēsturi, apmeklējot chatgpt.com
// @description:mg Mamafa tsy ampy lalao ny tetikasa vaovao rehefa mitovy amin'ny chatgpt.com
// @description:mi Auto-kōmata e whakakore i te hītori whakawhiti kōrero i te toro ki te chatgpt.com
// @description:mk Автоматски брише историја на разговорот при посета на chatgpt.com
// @description:ml chatgpt.com സന്ദർശിക്കുമ്പോൾ ചാറ്റ് ചരിത്രം ഓട്ടോ-പിന്തുണച്ച് അഴിച്ചുവയ്ക്കുന്നു
// @description:mn chatgpt.com-оос орж ирэх үед чатын түүхийг автоматаар цэвэрлэнэ
// @description:ms Membersihkan sejarah perbualan secara automatik apabila melawat chatgpt.com
// @description:mt Jħassar awtomatikament il-kronoloġija tal-chat meta żżur chatgpt.com
// @description:my chatgpt.com ကိုသွားဖို့အတွက် စကားဝှက်မှတ်တမ်းကို အလိုလိုရွေးချယ်ရန် အလွယ်တကူပြန်ဖွင့်ထားသည်
// @description:ne chatgpt.com मा आएकोमा च्याट इतिहास स्वचालित रूपमा हटाउँछ
// @description:nl Wis automatisch de chatgeschiedenis bij een bezoek aan chatgpt.com
// @description:no Sletter automatisk samtalehistorikk ved besøk på chatgpt.com
// @description:ny Kwatsa makina m'chipatala cha chat pamene kuwona chatgpt.com
// @description:pa chatgpt.com ਦੇ ਦੌਰਾਨ ਚੈਟ ਇਤਿਹਾਸ ਆਟੋਮੈਟਿਕ ਕਲੀਅਰ ਹੋ ਜਾਂਦਾ ਹੈ
// @description:pap Limpieza automático di historial di chat na bishita chatgpt.com
// @description:pl Automatycznie czyści historię czatu podczas odwiedzania chatgpt.com
// @description:ps خودکار تاریخچه چت پاک کول په chatgpt.com کښې
// @description:pt Limpa automaticamente o histórico de bate-papo ao visitar chatgpt.com
// @description:pt-BR Limpa automaticamente o histórico de bate-papo ao visitar chatgpt.com
// @description:rn Guteza inkuru y'ikarita y'imibare igihe utanga chatgpt.com
// @description:ro Șterge automat istoricul chat-ului la vizitarea chatgpt.com
// @description:ru Автоматически очищает историю чата при посещении chatgpt.com
// @description:rw Inyunganira amakuru ya chat inyuma yuko umaze guhamagara chatgpt.com
// @description:sg Auto-kura kolibatalu ya misamba wakari karika kutɔbɔ chatgpt.com
// @description:si chatgpt.com වෙත පැය සඳහා ස්වයංක්රීයතාවයින් චැට් ඉතිරිකිරීම මකා දැමුම
// @description:sk Automaticky vymazáva históriu chatu pri návšteve chatgpt.com
// @description:sl Samodejno izbriše zgodovino klepeta ob obisku spletnega mesta chatgpt.com
// @description:sm Automa i le malologa o le sootaga i luma i le chatgpt.com
// @description:sn Inongorora chat history chinayo ipapo uchi chiri kunochinja chatgpt.com
// @description:so Wax ka qaad chatka markii la booqdo chatgpt.com
// @description:sr Аутоматски брише историју чата при посети chatgpt.com
// @description:sv Rensar automatiskt chattens historik vid besök på chatgpt.com
// @description:sw Inaondoa historia ya mazungumzo moja kwa moja wakati wa kutembelea chatgpt.com
// @description:ta chatgpt.com அடுத்தடுத்தப்படும் நேரத்தில் உரையாடல் வரலாறை தானியங்கே நீக்குகிறது
// @description:te chatgpt.com సందర్శించినప్పుడు స్వయంచాలకంగా చాట్ చరిత్రను తొలగిస్తుంది
// @description:tg Тарихи чатро худкор ҷоба кунед, ки chatgpt.com равед
// @description:th ล้างประวัติการสนทนาโดยอัตโนมัติเมื่อเข้าชม chatgpt.com
// @description:ti ኮምፕዩተር እየጠበቀ ጊዜ chatgpt.com የተሰኘውን ችግርን አስፈልጋል
// @description:tk chatgpt.com-a ugradykda çat görnüşini awtomatiki biçimde boşaltýar
// @description:tn Emucisha chat chinyakanyaka nga uwonawo chatgpt.com
// @description:to 'Oku fa'a kovi 'e he ngaahi sipoti fakamatala 'i he falelotu ki he chatgpt.com
// @description:tpi Autometik klia chat histeri long bisitim long chatgpt.com
// @description:tr chatgpt.com'u ziyaret ettiğinizde sohbet geçmişini otomatik olarak temizler
// @description:uk Автоматично очищає історію чату при відвідуванні chatgpt.com
// @description:ur chatgpt.com کے دورے پر چیٹ تاریخ خودکار طور پر صاف کرتا ہے
// @description:uz chatgpt.com saytini tashrif buyurganda chat tarixini avtomatik ravishda o'chiradi
// @description:vi Xóa lịch sử trò chuyện tự động khi ghé thăm chatgpt.com
// @description:xh Ingqalasela ihisitela le-chat uma uzithola chatgpt.com
// @description:yi רומט די פֿונען דער שאַט היסטאָריע ביי בעזוך בקוקן chatgpt.com
// @description:zh 访问 chatgpt.com 时自动清除聊天记录
// @description:zh-CN 访问 chatgpt.com 时自动清除聊天记录
// @description:zh-HK 访问 chatgpt.com 时自动清除聊天记录
// @description:zh-SG 访问 chatgpt.com 时自动清除聊天记录
// @description:zh-TW 访问 chatgpt.com 时自动清除聊天记录
// @description:zu Ziba itshala lokucabanga okuzoshintshwa ngokuzenzakalelayo uma ukubuka chatgpt.com
// @author Adam Lui
// @namespace https://codeberg.org/adamlui
// @version 2026.9.5
// @license MIT
// @icon data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%22180%22%20height=%22180%22%20fill=%22none%22%3E%3Cstyle%3E:root%7B--primary-fill:%23000;--secondary-fill:%23fff;%7D@media%20(prefers-color-scheme:dark)%7B:root%7B--primary-fill:%23fff;--secondary-fill:%23000;%7D%7D%3C/style%3E%3Cg%20clip-path=%22url(%23a)%22%3E%3Crect%20width=%22180%22%20height=%22180%22%20fill=%22var(--primary-fill)%22%20rx=%2290%22/%3E%3Cg%20clip-path=%22url(%23b)%22%3E%3Cpath%20fill=%22var(--secondary-fill)%22%20d=%22M75.91%2073.628V62.232c0-.96.36-1.68%201.199-2.16l22.912-13.194c3.119-1.8%206.838-2.639%2010.676-2.639%2014.394%200%2023.511%2011.157%2023.511%2023.032%200%20.839%200%201.799-.12%202.758l-23.752-13.914c-1.439-.84-2.879-.84-4.318%200L75.91%2073.627Zm53.499%2044.383v-27.23c0-1.68-.72-2.88-2.159-3.719L97.142%2069.55l9.836-5.638c.839-.48%201.559-.48%202.399%200l22.912%2013.195c6.598%203.839%2011.035%2011.995%2011.035%2019.912%200%209.116-5.397%2017.513-13.915%2020.992v.001Zm-60.577-23.99-9.836-5.758c-.84-.48-1.2-1.2-1.2-2.16v-26.39c0-12.834%209.837-22.55%2023.152-22.55%205.039%200%209.716%201.679%2013.676%204.678L70.993%2055.516c-1.44.84-2.16%202.039-2.16%203.719v34.787-.002Zm21.173%2012.234L75.91%2098.339V81.546l14.095-7.917%2014.094%207.917v16.793l-14.094%207.916Zm9.056%2036.467c-5.038%200-9.716-1.68-13.675-4.678l23.631-13.676c1.439-.839%202.159-2.038%202.159-3.718V85.863l9.956%205.757c.84.48%201.2%201.2%201.2%202.16v26.389c0%2012.835-9.957%2022.552-23.27%2022.552v.001Zm-28.43-26.75L47.72%20102.778c-6.599-3.84-11.036-11.996-11.036-19.913%200-9.236%205.518-17.513%2014.034-20.992v27.35c0%201.68.72%202.879%202.16%203.718l29.989%2017.393-9.837%205.638c-.84.48-1.56.48-2.399%200Zm-1.318%2019.673c-13.555%200-23.512-10.196-23.512-22.792%200-.959.12-1.919.24-2.879l23.63%2013.675c1.44.84%202.88.84%204.32%200l30.108-17.392v11.395c0%20.96-.361%201.68-1.2%202.16l-22.912%2013.194c-3.119%201.8-6.837%202.639-10.675%202.639Zm29.748%2014.274c14.515%200%2026.63-10.316%2029.39-23.991%2013.434-3.479%2022.071-16.074%2022.071-28.91%200-8.396-3.598-16.553-10.076-22.43.6-2.52.96-5.039.96-7.557%200-17.153-13.915-29.99-29.989-29.99-3.239%200-6.358.48-9.477%201.56-5.398-5.278-12.835-8.637-20.992-8.637-14.515%200-26.63%2010.316-29.39%2023.991-13.434%203.48-22.07%2016.074-22.07%2028.91%200%208.396%203.598%2016.553%2010.075%2022.431-.6%202.519-.96%205.038-.96%207.556%200%2017.154%2013.915%2029.989%2029.99%2029.989%203.238%200%206.357-.479%209.476-1.559%205.397%205.278%2012.835%208.637%2020.992%208.637Z%22/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath%20id=%22a%22%3E%3Cpath%20d=%22M0%200h180v180H0z%22/%3E%3C/clipPath%3E%3CclipPath%20id=%22b%22%3E%3Cpath%20d=%22M29.487%2029.964h121.035v119.954H29.487z%22/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E
// @icon64 data:image/svg+xml,%3Csvg%20xmlns=%22http://www.w3.org/2000/svg%22%20width=%22180%22%20height=%22180%22%20fill=%22none%22%3E%3Cstyle%3E:root%7B--primary-fill:%23000;--secondary-fill:%23fff;%7D@media%20(prefers-color-scheme:dark)%7B:root%7B--primary-fill:%23fff;--secondary-fill:%23000;%7D%7D%3C/style%3E%3Cg%20clip-path=%22url(%23a)%22%3E%3Crect%20width=%22180%22%20height=%22180%22%20fill=%22var(--primary-fill)%22%20rx=%2290%22/%3E%3Cg%20clip-path=%22url(%23b)%22%3E%3Cpath%20fill=%22var(--secondary-fill)%22%20d=%22M75.91%2073.628V62.232c0-.96.36-1.68%201.199-2.16l22.912-13.194c3.119-1.8%206.838-2.639%2010.676-2.639%2014.394%200%2023.511%2011.157%2023.511%2023.032%200%20.839%200%201.799-.12%202.758l-23.752-13.914c-1.439-.84-2.879-.84-4.318%200L75.91%2073.627Zm53.499%2044.383v-27.23c0-1.68-.72-2.88-2.159-3.719L97.142%2069.55l9.836-5.638c.839-.48%201.559-.48%202.399%200l22.912%2013.195c6.598%203.839%2011.035%2011.995%2011.035%2019.912%200%209.116-5.397%2017.513-13.915%2020.992v.001Zm-60.577-23.99-9.836-5.758c-.84-.48-1.2-1.2-1.2-2.16v-26.39c0-12.834%209.837-22.55%2023.152-22.55%205.039%200%209.716%201.679%2013.676%204.678L70.993%2055.516c-1.44.84-2.16%202.039-2.16%203.719v34.787-.002Zm21.173%2012.234L75.91%2098.339V81.546l14.095-7.917%2014.094%207.917v16.793l-14.094%207.916Zm9.056%2036.467c-5.038%200-9.716-1.68-13.675-4.678l23.631-13.676c1.439-.839%202.159-2.038%202.159-3.718V85.863l9.956%205.757c.84.48%201.2%201.2%201.2%202.16v26.389c0%2012.835-9.957%2022.552-23.27%2022.552v.001Zm-28.43-26.75L47.72%20102.778c-6.599-3.84-11.036-11.996-11.036-19.913%200-9.236%205.518-17.513%2014.034-20.992v27.35c0%201.68.72%202.879%202.16%203.718l29.989%2017.393-9.837%205.638c-.84.48-1.56.48-2.399%200Zm-1.318%2019.673c-13.555%200-23.512-10.196-23.512-22.792%200-.959.12-1.919.24-2.879l23.63%2013.675c1.44.84%202.88.84%204.32%200l30.108-17.392v11.395c0%20.96-.361%201.68-1.2%202.16l-22.912%2013.194c-3.119%201.8-6.837%202.639-10.675%202.639Zm29.748%2014.274c14.515%200%2026.63-10.316%2029.39-23.991%2013.434-3.479%2022.071-16.074%2022.071-28.91%200-8.396-3.598-16.553-10.076-22.43.6-2.52.96-5.039.96-7.557%200-17.153-13.915-29.99-29.989-29.99-3.239%200-6.358.48-9.477%201.56-5.398-5.278-12.835-8.637-20.992-8.637-14.515%200-26.63%2010.316-29.39%2023.991-13.434%203.48-22.07%2016.074-22.07%2028.91%200%208.396%203.598%2016.553%2010.075%2022.431-.6%202.519-.96%205.038-.96%207.556%200%2017.154%2013.915%2029.989%2029.99%2029.989%203.238%200%206.357-.479%209.476-1.559%205.397%205.278%2012.835%208.637%2020.992%208.637Z%22/%3E%3C/g%3E%3C/g%3E%3Cdefs%3E%3CclipPath%20id=%22a%22%3E%3Cpath%20d=%22M0%200h180v180H0z%22/%3E%3C/clipPath%3E%3CclipPath%20id=%22b%22%3E%3Cpath%20d=%22M29.487%2029.964h121.035v119.954H29.487z%22/%3E%3C/clipPath%3E%3C/defs%3E%3C/svg%3E
// @compatible brave
// @compatible chrome
// @compatible edge
// @compatible firefox
// @compatible ghost
// @compatible librewolf
// @compatible opera
// @compatible qq
// @compatible vivaldi
// @match *://chatgpt.com/*
// @connect cdn.staticdelivr.com
// @connect scriptcat.org
// @require https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@4.15.12/dist/chatgpt.min.js#sha256-ZUAiLBu0Ifhqk99jMKSWl/AaOnK+kEwYQK+cCyNpCEg=
// @require https://cdn.staticdelivr.com/gl/adamlui/userscripts/8292d8b/assets/js/lib/css.js/dist/css.min.js#sha256-zf9s8C0cZ/i+gnaTIUxa0+RpDYpsJVlyuV5L2q4KUdA=
// @require https://cdn.staticdelivr.com/gl/adamlui/userscripts/8292d8b/assets/js/lib/dom.js/dist/dom.min.js#sha256-nTc2by3ZAz6AR7B8fOqjloJNETvjAepe15t2qlghMDo=
// @resource rpgCSS https://cdn.staticdelivr.com/gl/adamlui/ai-web-extensions/727feff/assets/styles/rising-particles/dist/gray.min.css#sha256-48sEWzNUGUOP04ur52G5VOfGZPSnZQfrF3szUr4VaRs=
// @resource rpwCSS https://cdn.staticdelivr.com/gl/adamlui/ai-web-extensions/727feff/assets/styles/rising-particles/dist/white.min.css#sha256-6xBXczm7yM1MZ/v0o1KVFfJGehHk47KJjq8oTktH4KE=
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// @grant GM_unregisterMenuCommand
// @grant GM_getResourceText
// @grant GM_xmlhttpRequest
// @grant GM.xmlHttpRequest
// @noframes
// @homepageURL https://apps.adamlui.com/autoclear-chatgpt-history
// @supportURL https://codeberg.org/adamlui/autoclear-chatgpt-history/issues
// @contributionURL https://ko-fi.com/adamlui
// ==/UserScript==
// Documentation: https://codeberg.org/adamlui/autoclear-chatgpt-history/src/branch/main/docs
// NOTE: This script relies on the powerful chatgpt.js library @ https://chatgptjs.org
// © 2023–2026 KudoAI & contributors under the MIT license.
(async () => {
'use strict'
// Init DATA
window.env = {
browser: { language: chatgpt.getUserLanguage(), isMobile: chatgpt.browser.isMobile() },
scriptManager: {
name: (() => { try { return GM_info.scriptHandler } catch { return 'unknown' }})(),
version: (() => { try { return GM_info.version } catch { return 'unknown' }})()
},
ui: { scheme: getScheme() },
xhr: typeof GM != 'undefined' && GM.xmlHttpRequest || GM_xmlhttpRequest
}
Object.assign(env.browser, { get isCompact() { return innerWidth <= 480 }})
env.scriptManager.supportsTooltips = env.scriptManager.name == 'Tampermonkey'
&& Number.parseInt(env.scriptManager.version.split('.')[0]) >= 5
window.app = {
version: GM_info.script.version, chatgptjsVer: /chatgpt\.js@([\d.]+)/.exec(GM_info.scriptMetaStr)?.[1],
commitHashes: {
data: '0f803af2e0', // for cached app.json
images: '2eeee946bc', // for navicon in toggles.sidebar.insert()
locales: 'e24bf8dd0a' // for cached messages.json
},
urls: { staticdelivr: 'https://cdn.staticdelivr.com/gl/adamlui/autoclear-chatgpt-history' }
}
app.urls.assets = {
data: `${app.urls.staticdelivr}/${app.commitHashes.data}/assets/data`,
images: `${app.urls.staticdelivr}/${app.commitHashes.images}/assets/images`,
locales: `${app.urls.staticdelivr}/${app.commitHashes.locales}/greasemonkey/_locales`
}
const remoteData = {
app: await new Promise(resolve => env.xhr({
method: 'GET', url: `${app.urls.assets.data}/app.json`,
onload: ({ responseText }) => resolve(JSON.parse(responseText))
})),
msgs: await new Promise(resolve => {
const msgBaseURL = app.urls.assets.locales,
localeDir = env.browser.language ? env.browser.language.replace('-', '_') : 'en'
let msgsURL = `${msgBaseURL}/${localeDir}/messages.json`, msgFetchesTried = 0
function fetchMsgs() { env.xhr({ method: 'GET', url: msgsURL, onload: handleMsgs })}
function handleMsgs(resp) {
try { // to return localized messages.json
const msgs = JSON.parse(resp.responseText), flatMsgs = {}
for (const key in msgs) // remove need to ref nested keys
if (typeof msgs[key] == 'object' && 'message' in msgs[key])
flatMsgs[key] = msgs[key].message
resolve(flatMsgs)
} catch { // if bad response
msgFetchesTried++ ; if (msgFetchesTried == 3) return resolve({}) // try original/region-stripped/EN
msgsURL = env.browser.language.includes('-') && msgFetchesTried == 1 ? // if regional lang on 1st try
msgsURL.replace(/(_locales\/[^_]+)_[^_]+(\/)/, '$1$2') // ...strip region before retrying
: `${msgBaseURL}/en/messages.json` // else use default English messages
fetchMsgs()
}
}
fetchMsgs()
})
}
Object.assign(app, { ...remoteData.app, urls: { ...app.urls, ...remoteData.app.urls }, msgs: remoteData.msgs })
// Init SETTINGS
app.config ??= {}
const settings = {
controls: { // displays top-to-bottom in toolbar menu
autoclear: { type: 'toggle', defaultVal: false,
label: app.msgs.menuLabel_autoclear, helptip: app.msgs.appDesc },
toggleHidden: { type: 'toggle', defaultVal: false,
label: app.msgs.menuLabel_toggleVis, helptip: app.msgs.helptip_toggleVis },
notifDisabled: { type: 'toggle', defaultVal: false,
label: app.msgs.menuLabel_modeNotifs, helptip: app.msgs.helptip_modeNotifs },
clearNow: { type: 'action', symbol: '🧹',
label: app.msgs.menuLabel_clearNow, helptip: app.msgs.helptip_clearNow }
},
load(...keys) {
keys.flat().forEach(key =>
app.config[key] = settings.processKey(key, GM_getValue(`${app.configKeyPrefix}_${key}`, undefined)))
},
processKey(key, val) {
const ctrl = settings.controls?.[key]
if (val != undefined && ( // validate stored val
(ctrl?.type == 'toggle' && typeof val != 'boolean')
|| (ctrl?.type == 'slider' && Number.isNaN(Number.parseFloat(val)))
)) val = undefined
return val ?? (ctrl?.defaultVal ?? (ctrl?.type == 'slider' ? 100 : false))
},
save(key, val) { GM_setValue(`${app.configKeyPrefix}_${key}`, val) ; app.config[key] = val },
typeIsEnabled(key) { // for menu labels + notifs to return ON/OFF
const reInvertSuffixes = /disabled|hidden/i
return reInvertSuffixes.test(key) // flag in control key name
&& !reInvertSuffixes.test(this.controls[key]?.label || '') // but not in label msg key name
? !app.config[key] : app.config[key] // so invert since flag reps opposite type state, else don't
}
}
settings.load(Object.keys(settings.controls))
// Define MENU functions
window.gmToolbarMenu = {
state: {
symbols: ['❌', '✔️'], separator: env.scriptManager.name == 'Tampermonkey' ? ' — ' : ': ',
words: [app.msgs.state_off.toUpperCase(), app.msgs.state_on.toUpperCase()]
},
refresh() {
if (typeof GM_unregisterMenuCommand == 'undefined') return
this.entryIDs.forEach(GM_unregisterMenuCommand)
this.register()
},
register() {
// Add toggles
this.entryIDs = Object.keys(settings.controls).map(key => {
const entryData = settings.controls[key]
const menuLabel = `${
entryData.symbol || this.state.symbols[+settings.typeIsEnabled(key)] } ${entryData.label} ${
entryData.type == 'toggle' ? this.state.separator
+ this.state.words[+settings.typeIsEnabled(key)]
: entryData.type == 'slider' ? ': ' + app.config[key] + entryData.labelSuffix || ''
: entryData.status ? ` — ${entryData.status}` : '' }`
return GM_registerMenuCommand(menuLabel, () => {
if (entryData.type == 'toggle') {
settings.save(key, !app.config[key]) ; syncConfigToUI({ key })
notify(`${entryData.label}: ${this.state.words[+settings.typeIsEnabled(key)]}`)
} else // Clear Now action
clearChatsAndGoHome()
}, env.scriptManager.supportsTooltips ? { title: entryData.helptip || ' ' } : undefined)
})
// Add About entry
this.entryIDs.push(GM_registerMenuCommand(
`💡 ${app.msgs.menuLabel_about} ${app.msgs.appName}`,
() => modals.open('about'),
env.scriptManager.supportsTooltips ? { title: ' ' } : undefined
))
}
}
window.updateCheck = () => env.xhr({
method: 'GET', url: `${app.urls.update.gm}?t=${Date.now()}`,
headers: { 'Cache-Control': 'no-cache' },
onload: ({ responseText }) => {
app.latestVer = /@version +(.*)/.exec(responseText)?.[1]
if (app.latestVer)
for (let i = 0 ; i < 4 ; i++) { // loop thru subver's
const currentSubVer = Number.parseInt(app.version.split('.')[i], 10) || 0,
latestSubVer = Number.parseInt(app.latestVer.split('.')[i], 10) || 0
if (currentSubVer > latestSubVer) break // out of comparison since not outdated
else if (latestSubVer > currentSubVer) // outdated
return modals.open('update', 'available')
}
modals.open('update', 'unavailable') ; modals.open('about')
}
})
// Define FEEDBACK functions
function notify(msg, pos = '', notifDuration = '', shadow = '') {
if (app.config.notifDisabled && !msg.includes(app.msgs.menuLabel_modeNotifs)) return
// Strip state word to append colored one later
const foundState = gmToolbarMenu.state.words.find(word => msg.includes(word))
if (foundState) msg = msg.replace(foundState, '')
// Show notification
chatgpt.notify(`${app.symbol} ${msg}`, pos, notifDuration, shadow || env.ui.scheme == 'light')
const notif = document.querySelector('.chatgpt-notif:last-child')
// Append styled state word
if (foundState) {
const stateStyles = {
on: {
light: 'color: #5cef48 ; text-shadow: rgba(255,250,169,0.38) 2px 1px 5px',
dark: 'color: #5cef48 ; text-shadow: rgb(55,255,0) 3px 0 10px'
},
off: {
light: 'color: #ef4848 ; text-shadow: rgba(255,169,225,0.44) 2px 1px 5px',
dark: 'color: #ef4848 ; text-shadow: rgba(255, 116, 116, 0.87) 3px 0 9px'
}
}
const styledStateSpan = dom.create.elem('span')
styledStateSpan.style.cssText = stateStyles[
foundState == gmToolbarMenu.state.words[0] ? 'off' : 'on'][env.ui.scheme]
styledStateSpan.append(foundState) ; notif.append(styledStateSpan)
}
}
// Define MODAL functions
const modals = {
stack: [], // of types of undismissed modals
class: `${app.slug}-modal`,
about() {
// Show modal
const labelStyles = 'text-transform: uppercase ; font-size: 17px ; font-weight: bold ;'
+ `color: ${ env.ui.scheme == 'dark' ? 'white' : '#494141' }`
const aboutModal = modals.alert(
`${app.symbol} ${app.msgs.appName}`, // title
`🧠 ${app.msgs.about_author}: `
+ `${app.msgs.appAuthor} `
+ `${app.msgs.about_and} `
+ `${app.msgs.about_contributors}\n`
+ `🏷️ ${app.msgs.about_version}: `
+ `${app.version}\n`
+ `📜 ${app.msgs.about_openSourceCode}: `
+ ``
+ `${app.urls.codeberg}\n`
+ `🚀 ${app.msgs.about_latestChanges}: `
+ ``
+ `${app.urls.codeberg}/commits\n`
+ `⚡ ${app.msgs.about_poweredBy}: `
+ `chatgpt.js`
+ ` v${app.chatgptjsVer}`,
[ // buttons
function checkForUpdates() { updateCheck() },
function getSupport(){},
function moreAIextensions(){}
], '', 747 // set width
)
// Format text
aboutModal.querySelector('h2').style.cssText = `
text-align: center ; font-size: 51px ; line-height: 46px ; padding: 15px 0`
aboutModal.querySelector('p').style.cssText = `
text-align: center ; overflow-wrap: anywhere ; white-space: pre-wrap ;
margin: ${ env.browser.isCompact ? '6px 0 -16px' : '3px 0 29px' }`
// Hack buttons
aboutModal.querySelectorAll('button').forEach(btn => {
btn.style.cssText = 'height: 58px ; min-width: 136px ; text-align: center'
// Replace link buttons w/ clones that don't dismiss modal
if (/support|extensions/i.test(btn.textContent)) {
btn.replaceWith(btn = btn.cloneNode(true))
btn.onclick = () => modals.safeWinOpen(app.urls[
btn.textContent.includes(app.msgs.btnLabel_getSupport) ? 'support'
: 'relatedExtensions'
])
}
// Prepend emoji + localize labels
if (/updates/i.test(btn.textContent))
btn.textContent = `🚀 ${app.msgs.btnLabel_checkForUpdates}`
else if (/support/i.test(btn.textContent))
btn.textContent = `🧠 ${app.msgs.btnLabel_getSupport}`
else if (/extensions/i.test(btn.textContent))
btn.textContent = `🤖 ${app.msgs.btnLabel_moreAIextensions}`
else // hide Dismiss button
btn.style.display = 'none'
})
return aboutModal
},
alert(title = '', msg = '', btns = '', checkbox = '', width = '') { // generic one from chatgpt.alert()
const alertID = chatgpt.alert(title, msg, btns, checkbox, width),
alert = document.getElementById(alertID).firstChild
this.init(alert) // add classes + rising particles bg
return alert
},
init(modal) { // requires lib/css.js
this.stylize()
modal.classList.add(this.class)
modal.parentNode.classList.add(`${this.class}-bg`)
css.addRisingParticles(modal)
},
observeRemoval(modal, modalType, modalSubType) { // to maintain stack for proper nav
const modalBG = modal.parentNode
new MutationObserver(([mutation], obs) =>
mutation.removedNodes.forEach(removedNode => {
if (removedNode !== modalBG) return
if (modals.stack[0].includes(modalSubType || modalType)) { // new modal not launched so nav back
modals.stack.shift() // remove this modal type from stack 1st
const prevModalType = modals.stack[0]
if (prevModalType) { // open it
modals.stack.shift() // remove type from stack since re-added on open
modals.open(prevModalType)
}
}
obs.disconnect()
})
).observe(modalBG.parentNode, { childList: true, subtree: true })
},
open(modalType, modalSubType) {
const modal = modalSubType ? this[modalType][modalSubType]() : this[modalType]() // show modal
if (!modal) return // since no div returned
this.stack.unshift(modalSubType ? `${modalType}_${modalSubType}` : modalType) // add to stack
this.init(modal) // add classes + rising particles bg
this.observeRemoval(modal, modalType, modalSubType) // to maintain stack for proper nav
},
safeWinOpen(url) { open(url, '_blank', 'noopener') }, // to prevent backdoor vulnerabilities
stylize() {
const { scheme } = env.ui
if (!this.styles?.isConnected) document.head.append(this.styles ??= dom.create.style())
this.styles.textContent = `
.${this.class} { /* modals */
user-select: none ; -webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ;
font-family: -apple-system, system-ui, BlinkMacSystemFont, Segoe UI, Roboto,
Oxygen-Sans, Ubuntu, Cantarell, Helvetica Neue, sans-serif ;
padding: 20px 25px 24px 25px !important ; font-size: 20px ;
color: ${ scheme == 'dark' ? 'white' : 'black' } !important ;
background-image: linear-gradient(180deg, ${
scheme == 'dark' ? '#99a8a6 -200px, black 200px' : '#b6ebff -296px, white 171px' }) }
.${this.class} [class*=modal-close-btn] {
position: absolute !important ; float: right ; top: 14px !important ; right: 16px !important ;
cursor: pointer ; width: 33px ; height: 33px ; border-radius: 20px
}
.${this.class} [class*=modal-close-btn] svg { height: 10px }
.${this.class} [class*=modal-close-btn] path {
${ scheme == 'dark' ? 'stroke: white ; fill: white' : 'stroke: #9f9f9f ; fill: #9f9f9f' }}
${ scheme == 'dark' ? // invert dark mode hover paths
`.${this.class} [class*=modal-close-btn]:hover path { stroke: black ; fill: black }` : '' }
.${this.class} [class*=modal-close-btn]:hover { background-color: #f2f2f2 } /* hover underlay */
.${this.class} [class*=modal-close-btn] svg { margin: 11.5px } /* center SVG for hover underlay */
.${this.class} a { color: #${ scheme == 'dark' ? '00cfff' : '1e9ebb' } !important }
.${this.class} h2 { font-weight: bold }
.${this.class} button {
--btn-transition: transform 0.1s ease-in-out, box-shadow 0.1s ease-in-out ;
font-size: 14px ; text-transform: uppercase ; /* shrink/uppercase labels */
border-radius: 0 !important ; /* square borders */
transition: var(--btn-transition) ; /* smoothen hover fx */
-webkit-transition: var(--btn-transition) ; -moz-transition: var(--btn-transition) ;
-o-transition: var(--btn-transition) ; -ms-transition: var(--btn-transition) ;
cursor: pointer !important ; /* add finger cursor */
border: 1px solid ${ scheme == 'dark' ? 'white' : 'black' } !important ;
padding: 8px !important ; min-width: 102px /* resize */
}
.${this.class} button:not([class*=primary]) { background: none }
.${this.class} button:hover {
${ scheme == 'light' ? // reduce intensity of light scheme hover glow
'--btn-shadow: 2px 1px 43px #00cfff70 ;' : '' }
color: inherit !important ; /* remove color hack */
background-color: rgb(${ scheme == 'light' ? '192 223 227 / 5%' : '43 156 171 / 43%' })
}
${ env.browser.isMobile ? '' : `.${this.class} .modal-buttons { margin-left: -13px !important }` }
.about-em { color: ${ scheme == 'dark' ? 'white' : 'green' } !important }`
},
update: {
width: 377,
available() {
// Show modal
const updateAvailModal = modals.alert(`🚀 ${app.msgs.alert_updateAvail}!`, // title
`${app.msgs.alert_newerVer} ${app.msgs.appName} ` // msg
+ `(v${app.latestVer}) ${app.msgs.alert_isAvail}! `
+ '${app.msgs.link_viewChanges}`,
function update() { // button
modals.safeWinOpen(`${app.urls.update.gm}?t=${Date.now()}`)
}, '', modals.update.width
)
// Localize button labels if needed
if (!env.browser.language.startsWith('en')) {
const updateBtns = updateAvailModal.querySelectorAll('button')
updateBtns[1].textContent = app.msgs.btnLabel_update
updateBtns[0].textContent = app.msgs.btnLabel_dismiss
}
return updateAvailModal
},
unavailable() {
return modals.alert(`${app.msgs.alert_upToDate}!`, // title
`${app.msgs.appName} (v${app.version}) ${app.msgs.alert_isUpToDate}!`, // msg
'', '', modals.update.width
)
}
}
}
// Define UI functions
function clearChatsAndGoHome() {
chatgpt.clearChats()
// Hide history from DOM since chatgpt.clearChats() works back-end only (front-end updates on reload otherwise)
const historyDivSel = 'div#history'
new Promise(resolve => setTimeout(resolve, 1000)).then(() => {
document.querySelectorAll(`${historyDivSel} li`).forEach(chatEntry => chatEntry.style.display = 'none')
document.querySelector(historyDivSel).previousElementSibling.style.display = 'none'
if (!clearChatsAndGoHome.historyObserver) { // monitor sidebar to restore chats heading on new chat
clearChatsAndGoHome.historyObserver = new MutationObserver(mutations =>
mutations.forEach(mutation => {
if (mutation.type == 'childList')
mutation.addedNodes.forEach(node => {
if (node.tagName == 'LI')
document.querySelector(historyDivSel).previousElementSibling.style.display = ''
})
})
)
clearChatsAndGoHome.historyObserver.observe(
document.querySelector(historyDivSel), { childList: true, subtree: true })
}
})
if (location.pathname != '/') chatgpt.startNewChat() // return home from potential ghost chat
notify(app.msgs.notif_chatsCleared, 'bottom-right', 2.5)
}
function getScheme() {
return /\b(light|dark)\b/.exec(document.documentElement.className)?.[1]
|| ( window.matchMedia?.('(prefers-color-scheme: dark)').matches ? 'dark' : 'light' )
}
function syncConfigToUI({ key } = {}) {
if (key == 'autoclear' && app.config.autoclear) clearChatsAndGoHome()
if (/autoclear|toggleHidden/.test(key)) toggles.sidebar.update.state()
gmToolbarMenu.refresh() // prefixes/suffixes
}
const toggles = {
sidebar: {
class: `${app.slug}-sidebar-toggle`,
create() {
// Init toggle elems
this.div = dom.create.elem('div', { class: this.class })
this.navicon = dom.create.elem('img')
this.toggleLabel = dom.create.elem('label')
this.switchSpan = dom.create.elem('span')
this.knobSpan = dom.create.elem('span')
// Assemble elems into parent div
this.switchSpan.append(this.knobSpan)
this.div.append(this.navicon, this.toggleLabel, this.switchSpan)
// Stylize elems
this.stylize() // create/append stylesheet
// Update scheme/state
this.update.scheme() ; this.update.state()
// Add hover/click listeners
this.div.onmouseover = this.div.onmouseout = ({ type }) => // trigger OpenAI hover overlay
this.div.style.setProperty('--item-background-color',
`var(--sidebar-surface-${ type == 'mouseover' ? 'secondary' : 'primary' })`)
this.div.onclick = () => {
settings.save('autoclear', !app.config.autoclear) ; syncConfigToUI({ key: 'autoclear' })
notify(`${app.msgs.mode_autoclear}: ${gmToolbarMenu.state.words[+app.config.autoclear]}`)
}
},
insert() { // requires lib/.min.js
const sidebar = document.querySelector(chatgpt.selectors.sidebar)
if (!sidebar || this.status?.startsWith('insert') || document.querySelector(`.${this.class}`)) return
this.status = 'inserting' ; if (!this.div) this.create()
const sidebarHeader = sidebar.querySelector('div#sidebar-header, header[class*=sidebar-header]')
if (sidebarHeader) { sidebarHeader.after(this.div) ; this.status = 'inserted' }
else {
this.status = 'waitingForSidebar'
dom.get.loadedElem('div#sidebar-header, header[class*=sidebar-header]').then(header => {
header.after(this.div) ; this.stylize() ; this.status = 'inserted'
}).catch((err) => { this.status = 'failed' ; console.error('toggles.sidebar.insert()', err) })
}
},
stylize() { // requires lib/.js + env
const firstNavLink = document.querySelector('button.wm-sidebar-navigationLink') || chatgpt.getNewChatLink()
if (firstNavLink && !this.classesBorrowed) { // borrow/assign classes from sidebar elems
const firstIcon = firstNavLink.querySelector('div:first-child'),
firstLabel = firstNavLink.querySelector('div:nth-child(2)')
this.div.classList.add(...firstNavLink.classList, ...(firstLabel?.classList || []))
this.div.querySelector('img')?.classList.add(...(firstIcon?.classList || []))
this.classesBorrowed = true
}
this.styles ??= dom.create.style(
`:root { /* vars */
--switch-enabled-bg-color: #ad68ff ; --switch-disabled-bg-color: #ccc ;
--switch-enabled-box-shadow: 1px 2px 8px #d8a9ff ;
--switch-enabled-hover-box-shadow: 0 1px 10px #9b5ad1 ;
--knob-box-shadow: rgba(0,0,0,0.3) 0 1px 2px 0 ;
--knob-box-shadow-dark: rgba(0,0,0,0.3) 0 1px 2px 0, rgba(0,0,0,0.15) 0 3px 6px 2px }`
// Element styles
+ `.${this.class} { /* parent div */
width: auto ; max-height: 37px ; padding: 0 5px ; margin: 3px 6px ;
user-select: none ; cursor: pointer
}
.${this.class} > img { /* navicon */
width: 1.25rem ; height: 1.25rem ; margin-left: 2px ; margin-right: 4px }
.${this.class} > span { /* switch span */
position: relative ; width: 30px ; height: 15px ; border-radius: 28px ;
background-color: var(--switch-disabled-bg-color) ;
bottom: ${ firstNavLink ? '0.5px' : '-0.15em' } ;
transition: 0.4s ; -webkit-transition: 0.4s ; -moz-transition: 0.4s ;
-o-transition: 0.4s ; -ms-transition: 0.4s }
.${this.class} > span.enabled { /* switch on */
background-color: var(--switch-enabled-bg-color) ;
box-shadow: var(--switch-enabled-box-shadow) ;
-webkit-box-shadow: var(--switch-enabled-box-shadow) ;
-moz-box-shadow: var(--switch-enabled-box-shadow) ;
transition: 0.15s ; -webkit-transition: 0.15s ; -moz-transition: 0.15s ;
-o-transition: 0.15s ; -ms-transition: 0.15s }
.${this.class}:hover > span.enabled { /* switch on when hover on parent div */
box-shadow: var(--switch-enabled-hover-box-shadow) ;
-webkit-box-shadow: var(--switch-enabled-hover-box-shadow) ;
-moz-box-shadow: var(--switch-enabled-hover-box-shadow) }
.${this.class} > span.disabled { /* switch off */
background-color: var(--switch-disabled-bg-color) ; box-shadow: none }
.${this.class} > span > span { /* knob span */
position: absolute ; width: 12px ; height: 12px ; content: "" ; border-radius: 28px ;
background-color: white ; left: 2px ; bottom: 1.25px ;
box-shadow: var(--knob-box-shadow) ;
-webkit-box-shadow: var(--knob-box-shadow) ; -moz-box-shadow: var(--knob-box-shadow) ;
transition: 0.4s ; -webkit-transition: 0.4s ; -moz-transition: 0.4s ;
-o-transition: 0.4s ; -ms-transition: 0.4s }
.${this.class} > label { /* toggle label */
cursor: pointer ; overflow: hidden ; text-overflow: ellipsis ; white-space: nowrap ;
color: black ; padding: 0 3px ; flex-grow: 1 ; font-size: var(--font-size-14)
}`
// Dark scheme mods
+ `.${this.class}.dark > span.enabled { /* switch on */
background-color: var(--switch-enabled-bg-color) ;
box-shadow: var(--switch-enabled-hover-box-shadow) ; /* use hover style instead */
-webkit-box-shadow: var(--switch-enabled-hover-box-shadow) ;
-moz-box-shadow: var(--switch-enabled-hover-box-shadow) }
.${this.class}.dark:hover > span.enabled { /* switch on when hover on parent div */
box-shadow: var(--switch-enabled-box-shadow) ; /* use regular style instead */
-webkit-box-shadow: var(--switch-enabled-box-shadow) ;
-moz-box-shadow: var(--switch-enabled-box-shadow) }
.${this.class}.dark > span > span { /* knob span */
box-shadow: var(--knob-box-shadow-dark) ; /* make 3D-er */
-webkit-box-shadow: var(--knob-box-shadow-dark) ;
-moz-box-shadow: var(--knob-box-shadow-dark) }
.${this.class}.dark > label { color: white } /* toggle label */`
)
if (!this.styles.isConnected) document.head.append(this.styles)
},
update: {
navicon({ preload } = {}) {
const baseURL = `${app.urls.assets.images}/icons/incognito`,
schemeMap = { light: 'black', dark: 'white' }, fileName = 'icon32.png'
if (preload)
Object.keys(schemeMap).forEach(scheme =>
new Image().src = `${baseURL}/${schemeMap[scheme]}/${fileName}`)
else toggles.sidebar.navicon.src = `${baseURL}/${schemeMap[env.ui.scheme]}/${fileName}`
},
scheme() { // to match UI scheme
toggles.sidebar.div.classList.remove('dark', 'light')
toggles.sidebar.div.classList.add(env.ui.scheme)
toggles.sidebar.update.navicon()
},
state() {
if (!toggles.sidebar.div) return // since toggle never created = sidebar missing
toggles.sidebar.div.style.display = app.config.toggleHidden ? 'none' : 'flex'
const isOn = app.config.autoclear
toggles.sidebar.toggleLabel.textContent = `${app.msgs.mode_autoclear} `
+ app.msgs[`state_${ isOn ? 'enabled' : 'disabled' }`]
requestAnimationFrame(() => {
toggles.sidebar.switchSpan.className = isOn ? 'enabled' : 'disabled'
toggles.sidebar.knobSpan.style.transform = `translateX(${ isOn ? 13 : 0 }px)`
}) // to trigger 1st transition fx
}
}
}
}
// Run MAIN routine
gmToolbarMenu.register() // create browser toolbar menu
toggles.sidebar.update.navicon({ preload: true }) // preload sidebar NAVICON variants
await Promise.race([chatgpt.isLoaded(), new Promise(resolve => setTimeout(resolve, 5000))]) // initial UI loaded
;['rpg', 'rpw'].forEach(cssType => // add Rising Particles styles
document.head.append(dom.create.style(GM_getResourceText(`${cssType}CSS`))))
toggles.sidebar.insert()
// AUTO-CLEAR on first visit if enabled
if (app.config.autoclear) clearChatsAndGoHome()
// Monitor NODE CHANGES to maintain sidebar toggle visibility
new MutationObserver(() => {
if (!app.config.toggleHidden && document.querySelector(chatgpt.selectors.sidebar)
&& !document.querySelector(`.${toggles.sidebar.class}`)
&& toggles.sidebar.status != 'inserting'
) { toggles.sidebar.status = 'missing' ; toggles.sidebar.insert() }
}).observe(document.body, { attributes: true, subtree: true })
// Monitor SCHEME PREF changes to update sidebar toggle + modal colors
new MutationObserver(handleSchemePrefChange).observe( // for site scheme pref changes
document.documentElement, { attributes: true, attributeFilter: ['class'] })
window.matchMedia('(prefers-color-scheme: dark)').addEventListener( // for browser/system scheme pref changes
'change', () => requestAnimationFrame(handleSchemePrefChange))
function handleSchemePrefChange() {
const displayedScheme = getScheme()
if (env.ui.scheme != displayedScheme) {
env.ui.scheme = displayedScheme ; toggles.sidebar.update.scheme() ; modals.stylize() }
}
})()