Greasy Fork is available in English.

ChatGPT Λειτουργία ευρείας οθόνης 🖥️

Προσθέτει λειτουργίες Widescreen + Fullscreen στο ChatGPT για βελτιωμένη προβολή + μείωση της κύλισης

Εγκατάσταση αυτού του κώδικαΒοήθεια
Κώδικας προτεινόμενος από τον δημιιουργό

Μπορεί, επίσης, να σας αρέσει ο κώδικας Διαγραφή Ιστορικού ChatGPT 🕶️.

Εγκατάσταση αυτού του κώδικα
// ==UserScript==
// @name                ChatGPT Widescreen Mode 🖥️
// @name:af             ChatGPT Widescreen Modus 🖥️
// @name:ar             ChatGPT وضع شاشة عريضة 🖥️
// @name:az             ChatGPT Geniş Ekran Rejimi 🖥️
// @name:be             ChatGPT Рэжым шырокага экрана 🖥️
// @name:bg             ChatGPT Широкоекранен режим 🖥️
// @name:bn             ChatGPT উইডস্ক্রিন মোড 🖥️
// @name:bo             ChatGPT རྒྱུ་ཆེན་གཞི་བསྒྲུབ་ རྒྱལ་བཤད། 🖥️
// @name:bs             ChatGPT Režim širokog ekrana 🖥️
// @name:ca             ChatGPT Mode de pantalla ampla 🖥️
// @name:ckb            ChatGPT دەستکاری چوارچێوەی ڕووکاری 🖥️
// @name:cs             ChatGPT Režim širokoúhlé obrazovky 🖥️
// @name:cy             ChatGPT Mod Sgrin Lled 🖥️
// @name:da             ChatGPT Bredformat-tilstand 🖥️
// @name:de             ChatGPT Breitbildmodus 🖥️
// @name:dv             ChatGPT ވިސްނުވެއް މޯޑި 🖥️
// @name:dz             ChatGPT འབྲེལ་བ་གཞུང་ལས་སྤྱི་ཚོགས་གླེང་། 🖥️
// @name:el             ChatGPT Λειτουργία ευρείας οθόνης 🖥️
// @name:eo             ChatGPT Larĝekrana reĝimo 🖥️
// @name:es             ChatGPT Modo de pantalla panorámica 🖥️
// @name:et             ChatGPT Laiarakenduse režiim 🖥️
// @name:eu             ChatGPT Pantaila Zabalaren Modua 🖥️
// @name:fa             ChatGPT حالت تلویزیونی 🖥️
// @name:fi             ChatGPT Laajakuva-tila 🖥️
// @name:fo             ChatGPT Búðarformaður háttur 🖥️
// @name:fr             ChatGPT Mode Écran Large 🖥️
// @name:fr-CA          ChatGPT Mode Écran Large 🖥️
// @name:gd             ChatGPT Dreach-sgrìn Leud 🖥️
// @name:gl             ChatGPT Modo de pantalla ancha 🖥️
// @name:gu             ChatGPT પ્રશાંત સ્ક્રીન મોડ 🖥️
// @name:he             ChatGPT מצב מסך רחב 🖥️
// @name:hi             ChatGPT चहचटजीपटी वाइडस्क्रीन मोड 🖥️
// @name:hr             ChatGPT Način širokog zaslona 🖥️
// @name:ht             ChatGPT Mod Fomat Gwo 🖥️
// @name:hu             ChatGPT Szélesvásznú mód 🖥️
// @name:hy             ChatGPT լայնէկրանանշանի ռեժիմ 🖥️
// @name:id             ChatGPT Mode Layar Lebar 🖥️
// @name:is             ChatGPT Breiðskjásstilling 🖥️
// @name:it             ChatGPT Modalità schermo panoramica 🖥️
// @name:ja             ChatGPT ワイドスクリーンモード 🖥️
// @name:jv             ChatGPT Mode Layar Lebar 🖥️
// @name:ka             ChatGPT ფანტსარიშის რეჟიმი 🖥️
// @name:kab            ChatGPT Amezwaru n Tikliwin Isellalen 🖥️
// @name:kk             ChatGPT Көлеңкескен түрі 🖥️
// @name:km             ChatGPT របៀបបង្អស្សមួយធ្វើដំបូង 🖥️
// @name:kn             ChatGPT ChatGPT ವೈಡ್ಸ್ಕ್ರೀನ್ ಮೋಡ್ 🖥️
// @name:ko             ChatGPT 와이드 스크린 모드 🖥️
// @name:ku             ChatGPT Moda Pêlên Dirêj 🖥️
// @name:ky             ChatGPT Жогорку Экрандык Режим 🖥️
// @name:la             ChatGPT Modus Latus Plenus 🖥️
// @name:lb             ChatGPT Breitbild-Modus 🖥️
// @name:lo             ChatGPT ຮູບແບບໂປຣຟອນໃຫຍ່ 🖥️
// @name:lt             ChatGPT Išplėstojo ekrano režimas 🖥️
// @name:lv             ChatGPT Plašā ekrāna režīms 🖥️
// @name:mg             ChatGPT Tarehimarika tamin'ny Widescreen 🖥️
// @name:mi             ChatGPT Āhua Whātika ā-Raupapa 🖥️
// @name:mk             ChatGPT Режим на широк екран 🖥️
// @name:ml             ChatGPT വൈഡ്സ്ക്രീൻ മോഡ് 🖥️
// @name:mn             ChatGPT Жинхэнэ Экранны Арга 🖥️
// @name:ms             ChatGPT Mod Skrin Lebar 🖥️
// @name:mt             ChatGPT Modalità Widescreen 🖥️
// @name:my             ChatGPT အများနှုန်းကျော်ဘုံ 🖥️
// @name:ne             ChatGPT विस्तृत प्राण्डविक पद्धति 🖥️
// @name:nl             ChatGPT Breedbeeldmodus 🖥️
// @name:no             ChatGPT Bredskjermmodus 🖥️
// @name:ny             ChatGPT Mawonekedwe Wathandiza 🖥️
// @name:pa             ChatGPT ਵਾਇਡਸਕ੍ਰੀਨ ਮੋਡ 🖥️
// @name:pl             ChatGPT Tryb szerokoekranowy 🖥️
// @name:ps             ChatGPT د ډیسپلې موډ 🖥️
// @name:pt             ChatGPT Modo Tela Larga 🖥️
// @name:pt-BR          ChatGPT Modo Tela Larga 🖥️
// @name:ro             ChatGPT Mod ecran lat 🖥️
// @name:ru             ChatGPT Режим широкоформатного экрана 🖥️
// @name:rw             ChatGPT Ibindi by'umuhondo bw'ishusho 🖥️
// @name:sg             ChatGPT Mode Écran Large 🖥️
// @name:si             ChatGPT විශේෂාංග පෙළක්ෂාව 🖥️
// @name:sk             ChatGPT Režim širokouhlého displeja 🖥️
// @name:sl             ChatGPT Način širokega zaslona 🖥️
// @name:sm             ChatGPT Laititi Fuamatala 🖥️
// @name:sn             ChatGPT Zvimwe Zvitsva Mode 🖥️
// @name:so             ChatGPT hab Widescreen Mode 🖥️
// @name:sr             ChatGPT Режим широког екрана 🖥️
// @name:sv             ChatGPT Bredbildsläge 🖥️
// @name:sw             ChatGPT Njia ya Skrini Panoramio 🖥️
// @name:ta             ChatGPT விரிவான திரை முறை 🖥️
// @name:te             ChatGPT ChatGPT వైడ్ స్క్రీన్ మోడ్ 🖥️
// @name:tg             ChatGPT Возачи Режаи Дисплеи Барнома 🖥️
// @name:th             ChatGPT โหมดหน้าจอแบบเต็มจอ 🖥️
// @name:ti             ቻትጂፒቲ ዋይድስክሪን ሞድ 🖥️
// @name:tk             ChatGPT Giň Ekran Tertibi 🖥️
// @name:tn             ChatGPT Nyemba Yemba Mzizo 🖥️
// @name:to             ChatGPT Moli ʻo e Ngaahi Fatongi ʻo Haʻaneesi 🖥️
// @name:tr             ChatGPT Geniş Ekran Modu 🖥️
// @name:uk             ChatGPT Режим широкого екрана 🖥️
// @name:ur             ChatGPT وائڈ اسکرین موڈ 🖥️
// @name:uz             ChatGPT Keng ekranga rejim 🖥️
// @name:vi             ChatGPT Chế độ Màn hình Rộng 🖥️
// @name:xh             ChatGPT Indlela ye-Widescreen Mode 🖥️
// @name:yi             ChatGPT ווידעסקרעען מאָדע 🖥️
// @name:zh             ChatGPT 宽屏模式 🖥️
// @name:zh-CN          ChatGPT 宽屏模式 🖥️
// @name:zh-HK          ChatGPT 寬螢幕模 🖥️
// @name:zh-SG          ChatGPT 宽屏模式 🖥️
// @name:zh-TW          ChatGPT 寬螢幕模 🖥️
// @name:zu             ChatGPT Isimo sokudlalwa emkhumbini 🖥️
// @description         Adds Widescreen + Fullscreen modes to ChatGPT for enhanced viewing + reduced scrolling
// @description:af      Voeg Widescreen + Volledige skermmodusse by ChatGPT vir verbeterde kyk + verminderde skrol
// @description:am      የራስ መጠን በ ChatGPT ውስጥ የተከፈተውን ወደ ትንቢት ማድረግ + ድምፅ እንዲታትና
// @description:ar      إضافة وضعي الشاشة العريضة + الشاشة الكاملة إلى ChatGPT لتحسين العرض + تقليل التمرير
// @description:az      ChatGPT üçün Geniş Ekran + Tam Ekran rejimləri əlavə etmək, görüntünü yaxşılaşdırmaq + səlahətən keçirməni azaltmaq
// @description:be      Дадае рэжымы шырокага экрана + поўнага экрана да ChatGPT для паляпшэнага прагляду + змяншэння скролінгу
// @description:bem     Ena Mode ya Widescreen + ya Fullscreen ku ChatGPT pa kuongeza kulandila + ukukonda scrolling
// @description:bg      Добавя режими на Широк екран + Пълен екран към ChatGPT за подобрено гледане + намалено плъзгане
// @description:bn      এক্স এগ্রিসিভ দেখার জন্য ChatGPT-এ ওয়াইডস্ক্রিন + ফুলস্ক্রিন মোড যুক্ত করে + স্ক্রলিং হ্রাস করে
// @description:bo      བཅའ་དྲུག་རྒྱུས་པ་+ བདེ་ཆེན་རྒྱུས་པ་འདི་ChatGPT་ནང་ལས་བསྒྲགས་བཞག་བའི་ལྷག་པ་ཞིག་ཁམས་པའི་བརྟག་ཞིབ་བཅོ་སྒྲིག་འདུག
// @description:bs      Dodaje Widescreen + Fullscreen režime ChatGPT-u za poboljšani prikaz + smanjenje klizanja
// @description:ca      Afegeix els modes de pantalla ampla + pantalla completa a ChatGPT per a una visualització millorada + reducció de desplaçament
// @description:ceb     Gidugang ang Mga Modo sa Widescreen + Fullscreen sa ChatGPT alang sa labaw nga pagtan-aw + paggamot sa pag-scroll
// @description:ckb     Mode Widescreen + Mode Fullscreen bo ChatGPT zêde bike ji bo nêzîkbûna başkirinê + kam kirina birevandinê
// @description:cs      Přidává režimy Widescreen + Fullscreen do ChatGPT pro zlepšené zobrazení + snížení posouvání
// @description:cy      Ychwanega dulliau Sgrin Llydan + Sgrin Lawn i ChatGPT ar gyfer gwylio well + lleihau sgrolio
// @description:da      Tilføjer Widescreen + Fuldscreen-tilstande til ChatGPT for forbedret visning + reduceret rulning
// @description:de      Fügt Widescreen + Vollbildmodus zu ChatGPT für verbesserte Ansicht + reduziertes Scrollen hinzu
// @description:dv      ChatGPT ބޮޓޮބޭސީ + ފަންވައިދާ މޯޑިއުލްއަޤް ހެދުމަށް ކަނޑުގެންނަވަން + ރުސަބް ބެންކުރުމަށްވެއްޖެ
// @description:dz      ChatGPT སྒྲུབ་ཐབས་ + མིང་དཔྱད་ཆེས་དང་པ་ཡོངས་ཀྱི་ལྷག་པ་ཁྱབ་ལ་ཉོ་གར་བཞག་པ་དང་བདེན་པས་ཆེད་པ་དང་ཚིག་རྐྱང་ལ་མགོ་ནི་
// @description:el      Προσθέτει λειτουργίες Widescreen + Fullscreen στο ChatGPT για βελτιωμένη προβολή + μείωση της κύλισης
// @description:eo      Aldonas larĝekranajn + plenekranajn reĝimojn al ChatGPT por plibonigita vidado + malpliigo de ŝovado
// @description:es      Agrega modos de pantalla panorámica + pantalla completa a ChatGPT para una visualización mejorada + desplazamiento reducido
// @description:et      Lisab ChatGPT-le Widescreen + Täisekraani režiimid parema vaatamise ja vähenenud kerimisega
// @description:eu      Gehitu Widescreen + Pantaila oso moduak ChatGPT-ra ikusmen hobetu + mugimendua gutxituz
// @description:fa      اضافه کردن حالت های واید اسکرین + تمام صفحه به ChatGPT برای مشاهده بهتر + کاهش اسکرول
// @description:fi      Lisää Widescreen + Koko näytön tilat ChatGPT:hen paremman katselun + vähentyneen vierityksen ansiosta
// @description:fo      Leggur til Widescreen + Fullscreen hamrar á ChatGPT fyri bætta sýning + minnka skúvsetan
// @description:fr      Ajoute les modes écran large + plein écran à ChatGPT pour une meilleure visualisation + un défilement réduit
// @description:fr-CA   Ajoute les modes écran large + plein écran à ChatGPT pour une meilleure visualisation + un défilement réduit
// @description:gd      Cuir gnèithean Leudachaidh + Faidhlichean sàbhaladh sgrùdair gu ChatGPT airson sealladh air àrdachadh + sgroladh lag
// @description:gl      Engade os modos de pantalla panorámica + pantalla completa a ChatGPT para unha visualización mellorada + desprazamento reducido
// @description:gu      એન્હાનો જોડાણ કરો વાઈડસ્ક્રીન + ફુલસ્ક્રીન મોડ તેમજ ChatGPT માટે સુધારેલ જોઈન્ટ પ્રદર્શન + કમેલી સ્ક્રોલિંગ માટે
// @description:haw     Kālele i nā Ao Waiwai + Ao Piha i loko o ChatGPT no ka ho'ohana'ana ikaika + ka'u hā'ule 'ana
// @description:he      מוסיף מצבי מסך מורחב + מסך מלא ל־ChatGPT לשיפור תצוגה + הפחתת גלילה
// @description:hi      बढ़िया दृश्य + कम स्क्रोलिंग के लिए ChatGPT में वाइडस्क्रीन + फुलस्क्रीन मोड जोड़ता है
// @description:hr      Dodaje Widescreen + Fullscreen načine na ChatGPT za poboljšani prikaz + smanjenje klizanja
// @description:ht      Ajoute modes Widescreen + Fullscreen nan ChatGPT pou amelyore gade + redui defileman
// @description:hu      Hozzáadja a Widescreen + Teljes képernyő módokat a ChatGPT-hoz jobb megtekintés + görgetés csökkentése érdekében
// @description:hy      Ավելացնում է Widescreen + Fullscreen ռեժիմները ChatGPT-ում բարելավված դիտումի համար + սահմանափակումը
// @description:id      Menambahkan mode Widescreen + Layar Penuh ke ChatGPT untuk penampilan yang lebih baik + pengurangan pengguliran
// @description:is      Bætir við Widescreen + Fullskjárshamra í ChatGPT til að bæta sýn + draga úr skruni
// @description:it      Aggiunge le modalità Widescreen + Schermo intero a ChatGPT per una visualizzazione migliorata + riduzione dello scorrimento
// @description:ja      ChatGPTの表示を向上するために、ワイドスクリーン+フルスクリーンモードを追加します
// @description:jv      Nambahi Modus Widescreen + Fullscreen menyang ChatGPT kanggo tampilan sing ditingkatake + ngecilake scrolling
// @description:ka      დაამატებს Widescreen + Fullscreen რეჟიმებს ChatGPT-ში გაუმჯობესებული ნახვა + გამოკლებული გადადების მიზნით
// @description:kk      ChatGPT-ге Widescreen + Fullscreen режимдерін қосу арқылы көрініс үздіктемелеу + скроллауды кеміту
// @description:km      បន្ថែមរបៀប Widescreen + Fullscreen ទៅក្នុង ChatGPT សម្រាប់ការមើលឡើងប្រហែលជាក្រុមហ៊ុន + ការរងចាំខ្លួនឯងប៉ុណ្ណោះ
// @description:kn      ಅಭ್ಯಾಸಕ್ರಮದ ದೃಷ್ಟಿಗೆ ಅನುಗುಣವಾಗಿ Widescreen + Fullscreen ಮೋಡ್ಗಳನ್ನು ChatGPTಗೆ ಸೇರಿಸುತ್ತದೆ
// @description:ko      ChatGPT에 와이드스크린 + 전체화면 모드를 추가하여 개선된 화면 표시 + 스크롤 감소
// @description:ku      Dihênîne modên Widescreen + Fullscreenê bo ChatGPTê ji bo pêşînkirina nîşandina + kêmkirina kayanînê
// @description:ky      ChatGPT-го Widescreen + Fullscreen режимдору кошо аркылуу жакшы көрүнүштү + скроллоо жоготуу
// @description:la      TV + Fullscreen modos addit ChatGPT ad augendam viewing + reducta scrolling
// @description:lb      Füügt Widescreen + Fullscreen Modi dem ChatGPT bäi fir verbessert Visioun + reduzéiert Scrollen
// @description:lo      ເພີ່ມຮູບແບບ Widescreen + Fullscreen ໃຫ້ໄດ້ຮຽນ ChatGPT ສຳເລັດຂອງການສະແດງອ້າງວ່າງ + ການປະເມີດໃຫ້ນ້ໍາຂາຍຫນ້າ
// @description:lt      Prideda Widescreen + Fullscreen režimus į ChatGPT, siekiant pagerinti peržiūrą ir sumažinti slinkimą
// @description:lv      Pievieno Widescreen + Fullscreen režīmus ChatGPT, lai uzlabotu skatīšanos + samazinātu ritināšanu
// @description:mg      Mametraka ny fomba Widescreen + Fullscreen amin'ny ChatGPT mba handresy ny fandefasana + famindrampoana ny firotsahana
// @description:mi      Ka whakarite ana i ngā āhuatanga Widescreen + Fullscreen ki te ChatGPT mō te whakaatu whakapai ake + te whakaiti i te whakararuraru
// @description:mk      Додава режими на Widescreen + Fullscreen во ChatGPT за подобрување на приказот + намалување на скролирањето
// @description:ml      വായ്ഡ്സ്ക്രീൻ + പൂർണ്ണസ്ക്രീൻ മോഡുകൾ ചേർക്കുന്നു ChatGPT-യിൽ മിക്കവാറും കാഴ്ചപ്പാട് + കുറവ് തിരിക്കൽ ചെയ്യുന്നതിന്
// @description:mn      Widescreen + Fullscreen горимуудыг ChatGPT рүү нэмэх нь дэлгэцэнд хувцаслан + гүйлгээний эвдрэлдээ хянахыг оруулна
// @description:mr      व्हाइडस्क्रीन + फुलस्क्रीन मोड्स चे ChatGPTमध्ये जोडले जातात व पाहणी वाढविण्यासाठी स्क्रोलिंग कमी करतात
// @description:ms      Menambah mod Widescreen + Layar Penuh ke ChatGPT untuk tampilan yang lebih baik + pengurangan penatalan
// @description:mt      Jiddoqq modes Widescreen + Fullscreen għal ChatGPT għall-viżwalizzazzjoni miġġielda + qasam it-telf
// @description:my      ပိုမိုကောင်းမွန်သောကြည့်ရှုရန်အတွက် ChatGPT တွင် Widescreen + မျက်နှာပြင်အပြည့်မုဒ်များကို ထည့်သွင်းထားသည်။
// @description:ne      सुधारिएको दृश्य + कम स्क्रोलिंगका लागि वाइडस्क्रिन + फुलस्क्रिन मोडहरूलाई ChatGPTमा थप्दछ
// @description:nl      Voegt Widescreen + Fullscreen modi toe aan ChatGPT voor verbeterde weergave + verminderd scrollen
// @description:no      Legger til Widescreen + Fullskjerm-moduser i ChatGPT for forbedret visning + redusert rulling
// @description:ny      Ndiwonetsa zolengedwa + Zomwe zosowa ndi ChatGPT kwa kudzaza kuona + kuchotsa kupinda
// @description:pa      ਚਾਟਜੀਪੀਟੀ ਵਿੱਚ Widescreen + Fullscreen ਮੋਡਾਂ ਸ਼ਾਮਿਲ ਕਰਦਾ ਹੈ ਜਿਹਨਾਂ ਨਾਲ ਵੇਖਾਉਣ ਵਧੀਆ ਹੋ ਜਾਂਦਾ ਹੈ + ਘੂਮਣ ਦੀ ਘਟ-ਪੁਟ ਹੋ ਜਾਂਦੀ ਹੈ
// @description:pl      Dodaje tryby Widescreen + Fullscreen do ChatGPT dla lepszej widoczności + zmniejszonego przewijania
// @description:ps      ځانګړې + ټولې پرده په ChatGPT کې اضافه کړي لپاره لاسرسی ترلاسه کړي + کوښښول په کمته
// @description:pt      Adiciona modos Widescreen + Tela cheia ao ChatGPT para visualização aprimorada + rolagem reduzida
// @description:pt-BR   Adiciona modos Widescreen + Tela cheia ao ChatGPT para visualização aprimorada + rolagem reduzida
// @description:ro      Adaugă modurile Widescreen + Ecran complet la ChatGPT pentru vizualizare îmbunătățită + defilare redusă
// @description:ru      Добавляет режимы Widescreen + Fullscreen в ChatGPT для улучшенного просмотра + сокращенной прокрутки
// @description:rw      Ongeraho Mugari Mugari + Byuzuye Mugaragaza kuri ChatGPT kugirango ubone kureba neza + kugabanya umuzingo
// @description:sg      Ajoute les modes écran large + plein écran à ChatGPT pour une meilleure visualisation + un défilement réduit
// @description:si      ChatGPT සඳහා Widescreen + Fullscreen ප්‍රදර්ශනයක් එක් කරයි තවත් පෙන්වන්නේ දැන් සිතූවාසිකාරක්ෂක විසින් සහ කෙසේදැයි ඇති අගයන් හුස්ම ක්‍රියාවලියකි
// @description:sk      Pridáva Widescreen + Fullscreen režimy do ChatGPT pre vylepšené zobrazenie + zníženie posúvania
// @description:sl      Dodaja Widescreen + Fullscreen načine v ChatGPT za izboljšan prikaz + zmanjšano premikanje
// @description:sm      Faamauina Widescreen + Fullscreen faavae i le ChatGPT mo le faailoga faapea le mafaufau + le auina faavaivai
// @description:sn      Ongorora Mutaundi wa Widescreen + Fullscreen muChatGPT kutora dzokubvumidzwa + kusha kusweropfuura
// @description:so      Wax ka beddelaa Hababka Widescreen + Fullscreen ee ChatGPT si aad u fiirsato + si aad u yaraato garaabinta
// @description:sq      Shton modalitete Widescreen + Fullscreen në ChatGPT për pamje të përmirësuar + ulje të skrollimit
// @description:sr      Додаје Widescreen + Fullscreen режиме у ЧатГПТ за унапређен приказ + смањено листање
// @description:st      E ngolisitsoe mokhabo Widescreen + Fullscreen ho ChatGPT bakeng sa mofuta ona le ho fokotsa liphalantere
// @description:sv      Lägger till Widescreen + Fullscreen-lägen i ChatGPT för förbättrad visning + minskad scrollning
// @description:sw      Inaongeza modes Widescreen + Fullscreen kwenye ChatGPT kwa kuimarisha kuonyesha + kupunguza kusokota
// @description:ta      மேலதிரும் பார்வை + சுழற்சியை மோதித்து சாட்ஜிபிடியில் Widescreen + Fullscreen முறைகளைச் சேர்க்கிறது
// @description:te      Widescreen + Fullscreen మోడ్లను ChatGPTకి చేర్చుకోవడంతో చంపుకునే వీక్షణ కనిపించడానికి స్క్రోల్ తక్కువగా మార్చండి
// @description:tg      Маводиҳои Widescreen + Fullscreen-ро ба ChatGPT илова мекунад, барои кӯҳнашудаи намоиш + кам кардани скроллаш
// @description:th      เพิ่มโหมดจอแบบ Widescreen + Fullscreen ใน ChatGPT สำหรับการดูที่ดีขึ้น + การเลื่อนลดลง
// @description:ti      ንዝለዓለ ምርኣይ + ምጉዳል ምዝዋር ኣብ ChatGPT Widescreen + Fullscreen modes ይውስኽ
// @description:tk      Giňeldilen görmek + peseltmek üçin ChatGPT-ä giň ekran + Doly ekran reesimlerini goşýar
// @description:tl      Nagdaragdag ng mga mode ng Widescreen + Fullscreen sa ChatGPT para sa pinabuting pagtingin + pinaikling pag-scroll
// @description:tn      Inowedzera Widescreen + Fullscreen modes kuChatGPT yekuwedzera kutarisa + kuderedzwa kupuruzira
// @description:to      Hoʻohui i ka Widescreen + Fullscreen modes i ChatGPT no ka hoʻonui ʻia ʻana o ka nānā ʻana + hoʻemi i ka ʻōwili ʻana
// @description:tr      Geliştirilmiş görüntüleme + azaltılmış kaydırma için ChatGPT'ye Widescreen + Fullscreen modları ekler
// @description:tt      ChatGPT-га Widescreen + Fullscreen рәжимләрен өстәәдегезгә, күрүләштеңләри үткәргә + скроллауны буянтартарга
// @description:ug      ChatGPTغا Widescreen + Fullscreen كۆرۈنمە پەسەند قىلىش+يىتىپ ئاجرا قىلىش مۆچىيى
// @description:uk      Додає режими Widescreen + Fullscreen до ChatGPT для поліпшеного перегляду + зменшення прокрутки
// @description:ur      ترقی یافتہ دیکھائی کے لئے ChatGPT میں وائڈ اسکرین + پوری اسکرین موڈز شامل کرتا ہے
// @description:uz      ChatGPT-ga Widescreen + Fullscreen rejimlarini qo'shadi, ko'rishni yaxshilash + aylantirishni kamaytirish uchun
// @description:vi      Thêm chế độ Widescreen + Fullscreen vào ChatGPT để cải thiện hiển thị + giảm cuộn trang
// @description:wo      Bëgg na ñiis-ñiis Widescreen + Fullscreen ci ChatGPT buñ leen waxtu dib lu jàmm + ñaari jëm
// @description:xh      Enza izimvo Widescreen + Fullscreen kwakuhlanganiselelwa kwiChatGPT ukuze kube khona okukhulu + kokucima kwesithombe
// @description:yi      מוסיף Widescreen + Fullscreen מאָדעס צו ChatGPT פֿאַר ימפּרוווד וויוינג + רידוסט סקראָללינג
// @description:yo      Fi àwọn ohun ọrọ Widescreen + Fullscreen si ChatGPT fun iwọle aiyewo + iwọle ififunwọle
// @description:zh      向 ChatGPT 添加宽屏 + 全屏模式以增强查看效果 + 减少滚动
// @description:zh-CN   向 ChatGPT 添加宽屏 + 全屏模式以增强查看效果 + 减少滚动
// @description:zh-HK   向 ChatGPT 添加寬屏 + 全屏模式以增強查看效果 + 減少滾動
// @description:zh-SG   向 ChatGPT 添加宽屏 + 全屏模式以增强查看效果 + 减少滚动
// @description:zh-TW   向 ChatGPT 添加寬屏 + 全屏模式以增強查看效果 + 減少滾動
// @description:zu      Engeza izinhlobo zezimodi ze-Widescreen + Fullscreen ku-ChatGPT ukuze kube nokubonakala + ukuncitsha ukusukela
// @author              Adam Lui
// @namespace           https://github.com/adamlui
// @version             2024.7.19.5
// @license             MIT
// @compatible          chrome
// @compatible          firefox
// @compatible          edge
// @compatible          opera
// @compatible          brave
// @compatible          vivaldi
// @compatible          librewolf
// @compatible          ghost
// @compatible          qq
// @match               *://chatgpt.com/*
// @match               *://chat.openai.com/*
// @match               *://poe.com/*
// @icon                https://media.chatgptwidescreen.com/images/icons/widescreen-robot-emoji/icon48.png?9a393be
// @icon64              https://media.chatgptwidescreen.com/images/icons/widescreen-robot-emoji/icon64.png?9a393be
// @require             https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@3.0.1/dist/chatgpt.min.js#sha256-jCJMPu044aK37jtC2wMMKnNgHbXJ5Pm9ZdIqDERob7k=
// @connect             cdn.jsdelivr.net
// @connect             greasyfork.org
// @grant               GM_setValue
// @grant               GM_getValue
// @grant               GM_registerMenuCommand
// @grant               GM_unregisterMenuCommand
// @grant               GM_openInTab
// @grant               GM_xmlhttpRequest
// @grant               GM.xmlHttpRequest
// @noframes
// @homepageURL         https://www.chatgptwidescreen.com
// @supportURL          https://support.chatgptwidescreen.com
// @contributionURL     https://github.com/sponsors/adamlui
// ==/UserScript==

// Documentation: https://docs.chatgptwidescreen.com
// NOTE: This script relies on the powerful chatgpt.js library @ https://chatgpt.js.org © 2023–2024 KudoAI & contributors under the MIT license.

(async () => { /* global newChatBtn, wideScreenBtn, fullWindowBtn, fullScreenBtn */

    const site = new URL(document.location.href).hostname.split('.').slice(-2, -1)[0]

    // Init CONFIG
    const config = {
        appName: 'ChatGPT Widescreen Mode', appSymbol: '🖥️', keyPrefix: site + 'Widescreen',
        gitHubURL: 'https://github.com/adamlui/chatgpt-widescreen',
        greasyForkURL: 'https://greasyfork.org/scripts/461473-chatgpt-widescreen-mode',
        latestAssetCommitHash: '6f22742' } // for messages.json
    config.updateURL = config.greasyForkURL.replace('https://', 'https://update.')
        .replace(/(\d+)-?([a-zA-Z-]*)$/, (_, id, name) => `${ id }/${ !name ? 'script' : name }.meta.js`)
    config.supportURL = config.gitHubURL + '/issues/new'
    config.assetHostURL = config.gitHubURL.replace('github.com', 'cdn.jsdelivr.net/gh') + `@${config.latestAssetCommitHash}/`
    config.userLanguage = chatgpt.getUserLanguage()
    loadSetting('autoFocusChatbarDisabled', 'fullerWindows', 'fullWindow', 'hiddenFooter', 'hiddenHeader',
                'notifDisabled', 'ncbDisabled', 'tcbDisabled', 'widerChatbox', 'wideScreen')

    // Init FETCHER
    const xhr = getUserscriptManager() == 'OrangeMonkey' ? GM_xmlhttpRequest : GM.xmlHttpRequest

    // Define MESSAGES
    const msgsLoaded = new Promise(resolve => {
        const msgHostDir = config.assetHostURL + 'greasemonkey/_locales/',
              msgLocaleDir = ( config.userLanguage ? config.userLanguage.replace('-', '_') : 'en' ) + '/'
        let msgHref = msgHostDir + msgLocaleDir + 'messages.json', msgXHRtries = 0
        xhr({ method: 'GET', url: msgHref, onload: onLoad })
        function onLoad(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 (err) { // if bad response
                msgXHRtries++ ; if (msgXHRtries == 3) return resolve({}) // try up to 3X (original/region-stripped/EN) only
                msgHref = config.userLanguage.includes('-') && msgXHRtries == 1 ? // if regional lang on 1st try...
                    msgHref.replace(/([^_]+_[^_]+)_[^/]*(\/.*)/, '$1$2') // ...strip region before retrying
                        : ( msgHostDir + 'en/messages.json' ) // else use default English messages
                xhr({ method: 'GET', url: msgHref, onload: onLoad })
            }
        }
    }) ; const msgs = await msgsLoaded

    // Define SCRIPT functions

    function loadSetting(...keys) { keys.forEach(key => config[key] = GM_getValue(`${ config.keyPrefix }_${ site }_${ key }`, false)) }
    function saveSetting(key, value) { GM_setValue(`${ config.keyPrefix }_${ site }_${ key }`, value) ; config[key] = value }
    function safeWindowOpen(url) { window.open(url, '_blank', 'noopener') } // to prevent backdoor vulnerabilities
    function getUserscriptManager() { try { return GM_info.scriptHandler } catch (err) { return 'other' }}

    // Define MENU functions

    function registerMenu() {

        // Add command to also activate wide screen in full-window
        const fwLabel = menuState.symbol[+config.fullerWindows] + ' '
                      + ( msgs.menuLabel_fullerWins || 'Fuller Windows' )
                      + menuState.separator + menuState.word[+config.fullerWindows]
        menuIDs.push(GM_registerMenuCommand(fwLabel, () => {
            saveSetting('fullerWindows', !config.fullerWindows)
            syncFullerWindows(config.fullerWindows) // live update on click
            if (!config.notifDisabled) notify(
                `${ ( msgs.menuLabel_fullerWins || 'Fuller Windows' ) }: ${ menuState.word[+config.fullerWindows] }`)
            refreshMenu()
        }))

        // Add command to toggle taller chatbox when typing
        const tcbLabel = '↕️ ' + ( msgs.menuLabel_tallerChatbox || 'Taller Chatbox' )
                       + menuState.separator + menuState.word[+!config.tcbDisabled]
        menuIDs.push(GM_registerMenuCommand(tcbLabel, () => {
            saveSetting('tcbDisabled', !config.tcbDisabled)
            updateTweaksStyle()
            if (!config.notifDisabled) notify(
                `${ msgs.menuLabel_tallerChatbox || 'Taller Chatbox' }: ${ menuState.word[+!config.tcbDisabled] }`)
            refreshMenu()
        }))

        // Add command to toggle wider chatbox w/ widescreen mode in Poe
        if (site == 'poe') {
            const wcbLabel = '↔️ ' + ( msgs.menuLabel_widerChatbox || 'Wider Chatbox' )
                           + menuState.separator + menuState.word[+config.widerChatbox]
            menuIDs.push(GM_registerMenuCommand(wcbLabel, () => {
                saveSetting('widerChatbox', !config.widerChatbox)
                updateWidescreenStyle()
                if (!config.notifDisabled) notify(
                    `${ msgs.menuLabel_widerChatbox || 'Wider Chatbox' }: ${ menuState.word[+config.widerChatbox] }`)
                refreshMenu()
            }))
        }

        // Add command to hide New Chat button
        const hncLabel = menuState.symbol[+!config.ncbDisabled] + ' '
                       + ( msgs.menuLabel_newChatBtn || 'New Chat Button' )
                       + menuState.separator + menuState.word[+!config.ncbDisabled]
        menuIDs.push(GM_registerMenuCommand(hncLabel, () => {
            saveSetting('ncbDisabled', !config.ncbDisabled)
            updateTweaksStyle()
            notify(`${ msgs.menuLabel_newChatBtn || 'New Chat Button' }: ${ menuState.word[+!config.ncbDisabled] }`)
            refreshMenu()
        }))

        if (/chatgpt|openai/.test(site)) {

            // Add command to toggle Auto-Focus Chatbar
            const afcLabel = menuState.symbol[+!config.autoFocusChatbarDisabled] + ' '
                           + ( msgs.menuLabel_autoFocusChatbar || 'Auto-Focus Chatbar' ) + ' '
                           + menuState.separator + menuState.word[+!config.autoFocusChatbarDisabled]
            menuIDs.push(GM_registerMenuCommand(afcLabel, () => {
                saveSetting('autoFocusChatbarDisabled', !config.autoFocusChatbarDisabled)
                notify(( msgs.menuLabel_autoFocusChatbar || 'Auto-Focus Chatbar' ) + ' '
                             + menuState.word[+!config.autoFocusChatbarDisabled])
                if (!config.autoFocusChatbarDisabled) document.querySelector(inputSelector)?.focus()
                refreshMenu()
            }))

            // Add command to toggle hidden header
            const hhLabel = menuState.symbol[+config.hiddenHeader] + ' '
                          + ( msgs.menuLabel_hiddenHeader || 'Hidden Header' )
                          + menuState.separator + menuState.word[+config.hiddenHeader]
            menuIDs.push(GM_registerMenuCommand(hhLabel, () => {
                saveSetting('hiddenHeader', !config.hiddenHeader)
                updateTweaksStyle()
                if (!config.notifDisabled) notify(
                    `${ msgs.menuLabel_hiddenHeader || 'Hidden Header' }: ${ menuState.word[+config.hiddenHeader] }`)
                refreshMenu()
            }))

            // Add command to toggle hidden footer
            const hfLabel = menuState.symbol[+config.hiddenFooter] + ' '
                          + ( msgs.menuLabel_hiddenFooter || 'Hidden Footer' )
                          + menuState.separator + menuState.word[+config.hiddenFooter]
            menuIDs.push(GM_registerMenuCommand(hfLabel, () => {
                saveSetting('hiddenFooter', !config.hiddenFooter)
                updateTweaksStyle()
                if (!config.notifDisabled) notify(
                    `${ msgs.menuLabel_hiddenFooter || 'Hidden Footer' }: ${ menuState.word[+config.hiddenFooter] }`)
                refreshMenu()
            }))
        }

        // Add command to show notifications when switching modes
        const mnLabel = menuState.symbol[+!config.notifDisabled] + ' '
                      + ( msgs.menuLabel_modeNotifs || 'Mode Notifications' )
                      + menuState.separator + menuState.word[+!config.notifDisabled]
        menuIDs.push(GM_registerMenuCommand(mnLabel, () => {
            saveSetting('notifDisabled', !config.notifDisabled)
            notify(`${ msgs.menuLabel_modeNotifs || 'Mode Notifications' }: ${ menuState.word[+!config.notifDisabled] }`)
            refreshMenu()
        }))

        // Add command to launch About modal
        const amLabel = `💡 ${ msgs.menuLabel_about || 'About' } ${ msgs.appName || config.appName }`
        menuIDs.push(GM_registerMenuCommand(amLabel, launchAboutModal))
    }

    function refreshMenu() {
        if (getUserscriptManager() == 'OrangeMonkey') return
        for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu()
    }

    function launchAboutModal() {

        // Show alert
        const chatgptJSver = (/chatgpt-([\d.]+)\.min/.exec(GM_info.script.header) || [null, ''])[1],
              headingStyle = 'font-size: 1.15rem',
              pStyle = 'position: relative ; left: 3px',
              pBrStyle = 'position: relative ; left: 4px ',
              aStyle = 'color: ' + ( chatgpt.isDarkMode() ? '#c67afb' : '#8325c4' ) // purple
        const aboutModalID = siteAlert(
            msgs.appName || config.appName, // title
            `<span style="${ headingStyle }"><b>🏷️ <i>${ msgs.about_version || 'Version' }</i></b>: </span>`
                + `<span style="${ pStyle }">${ GM_info.script.version }</span>\n`
            + `<span style="${ headingStyle }"><b>⚡ <i>${ msgs.about_poweredBy || 'Powered by' }</i></b>: </span>`
                + `<span style="${ pStyle }"><a style="${ aStyle }" href="https://chatgpt.js.org" target="_blank" rel="noopener">`
                + 'chatgpt.js</a>' + ( chatgptJSver ? ( ' v' + chatgptJSver ) : '' ) + '</span>\n'
            + `<span style="${ headingStyle }"><b>📜 <i>${ msgs.about_sourceCode || 'Source code' }</i></b>:</span>\n`
                + `<span style="${ pBrStyle }"><a href="${ config.gitHubURL }" target="_blank" rel="nopener">`
                + config.gitHubURL + '</a></span>',
            [ // buttons
                function checkForUpdates() { updateCheck() },
                function getSupport() { safeWindowOpen(config.supportURL) },
                function leaveAReview() { // show new modal
                    const reviewModalID = chatgpt.alert(( msgs.alert_choosePlatform || 'Choose a Platform' ) + ':', '',
                        [ function greasyFork() { safeWindowOpen(config.greasyForkURL + '/feedback#post-discussion') },
                          function productHunt() { safeWindowOpen(
                              'https://www.producthunt.com/products/chatgpt-widescreen-mode/reviews/new') }])
                    document.getElementById(reviewModalID).querySelector('button')
                        .style.display = 'none' }, // hide dismiss button
                function moreChatGPTapps() { safeWindowOpen('https://github.com/adamlui/chatgpt-apps') }
            ], '', 478 // set width
        )

        // Re-format buttons to include emoji + localized label + hide Dismiss button
        for (const button of document.getElementById(aboutModalID).querySelectorAll('button')) {
            if (/updates/i.test(button.textContent)) button.textContent = (
                '🚀 ' + ( msgs.buttonLabel_updateCheck || 'Check for Updates' ))
            else if (/support/i.test(button.textContent)) button.textContent = (
                '🧠 ' + ( msgs.buttonLabel_getSupport || 'Get Support' ))
            else if (/review/i.test(button.textContent)) button.textContent = (
                '⭐ ' + ( msgs.buttonLabel_leaveReview || 'Leave a Review' ))
            else if (/apps/i.test(button.textContent)) button.textContent = (
                '🤖 ' + ( msgs.buttonLabel_moreApps || 'More ChatGPT Apps' ))
            else button.style.display = 'none' // hide Dismiss button
        }
    }

    function updateCheck() {

        // Fetch latest meta
        const currentVer = GM_info.script.version
        xhr({
            method: 'GET', url: config.updateURL + '?t=' + Date.now(),
            headers: { 'Cache-Control': 'no-cache' },
            onload: response => { const updateAlertWidth = 377

                // Compare versions
                const latestVer = /@version +(.*)/.exec(response.responseText)[1]
                for (let i = 0 ; i < 4 ; i++) { // loop thru subver's
                    const currentSubVer = parseInt(currentVer.split('.')[i], 10) || 0,
                          latestSubVer = parseInt(latestVer.split('.')[i], 10) || 0
                    if (currentSubVer > latestSubVer) break // out of comparison since not outdated
                    else if (latestSubVer > currentSubVer) { // if outdated

                        // Alert to update
                        const updateModalID = siteAlert(( msgs.alert_updateAvail || 'Update available' ) + '! 🚀', // title
                            ( msgs.alert_newerVer || 'An update to' ) + ' ' // msg
                                + ( msgs.appName || config.appName ) + ' '
                                + `(v${ latestVer }) ${ msgs.alert_isAvail || 'is available' }!  `
                                + '<a target="_blank" rel="noopener" style="font-size: 0.7rem" '
                                    + 'href="' + config.gitHubURL + '/commits/main/greasemonkey/'
                                    + config.updateURL.replace(/.*\/(.*)meta\.js/, '$1user.js') + '"'
                                    + `> ${ msgs.link_viewChanges || 'View changes' }</a>`,
                            function update() { // button
                                GM_openInTab(config.updateURL.replace('meta.js', 'user.js') + '?t=' + Date.now(),
                                    { active: true, insert: true })}, // focus, make adjacent                            
                            '', updateAlertWidth
                        )

                        // Localize button labels if needed
                        if (!config.userLanguage.startsWith('en')) {
                            const updateAlert = document.querySelector(`[id="${ updateModalID }"]`),
                                  updateBtns = updateAlert.querySelectorAll('button')
                            updateBtns[1].textContent = msgs.buttonLabel_update || 'Update'
                            updateBtns[0].textContent = msgs.buttonLabel_dismiss || 'Dismiss'
                        }

                        return
                }}

                // Alert to no update, return to About modal
                siteAlert(( msgs.alert_upToDate || 'Up-to-date' ) + '!', // title
                    `${ msgs.appName || config.appName } (v${ currentVer }) ` // msg
                        + ( msgs.alert_isUpToDate || 'is up-to-date' ) + '!',
                    '', '', updateAlertWidth
                )
                launchAboutModal()
    }})}

    // Define FEEDBACK functions

    function notify(msg, position = '', notifDuration = '', shadow = '') {

        // Strip state word to append colored one later
        const foundState = menuState.word.find(word => msg.includes(word))
        if (foundState) msg = msg.replace(foundState, '')

        // Show notification
        chatgpt.notify(`${ config.appSymbol } ${ msg }`, position, notifDuration, shadow || chatgpt.isDarkMode() ? '' : 'shadow')
        const notifs = document.querySelectorAll('.chatgpt-notif'),
              notif = notifs[notifs.length -1]

        // Append colored state word
        if (foundState) {
            const coloredState = document.createElement('span')
            coloredState.style.color = foundState == menuState.word[0] ? 'rgb(239, 72, 72)' : '#5cef48'
            coloredState.append(foundState) ; notif.append(coloredState)
        }
    }

    function siteAlert(title = '', msg = '', btns = '', checkbox = '', width = '') {
        return chatgpt.alert(`${ config.appSymbol } ${ title }`, msg, btns, checkbox, width )}

    // Define BUTTON functions

    function setBtnColor() { return (
        /chatgpt|openai/.test(site) ? (
            document.querySelector('.dark.bg-black, [class*="dark:bg-gray"]') // temp chat post-GPT4-o, pre-GPT-4o
         || chatgpt.isDarkMode() ? 'white' : '#202123' )
      : site == 'poe' ? 'currentColor' : ''
    )}

    function insertBtns() {

        // ID chatbar
        let chatbar
        if (/chatgpt|openai/.test(site)) {
            chatbar = document.querySelector('div[class*="textarea:focus"]') // pre-5/2024
                   || document.getElementById('prompt-textarea').parentNode.parentNode // post-5/2024
        } else if (site == 'poe') chatbar = document.querySelector('div[class*="ChatMessageInputContainer"]')

        if (chatbar.contains(wideScreenBtn)) return // if buttons aren't missing, exit

        // Tweak chatbar
        if (/chatgpt|openai/.test(site)) // allow tooltips to overflow
            chatbar.classList.remove('overflow-hidden')
        else if (site == 'poe') { // left-align attach file button
            const attachFileBtn = chatbar.querySelector('button[class*="File"]')
            attachFileBtn.style.cssText = 'position: absolute ; left: 1rem ; bottom: 0.35rem'
            document.querySelector(inputSelector).style.padding = '0 13px 0 40px' // accommodate new btn pos
        }

        // Insert buttons
        const elemsToInsert = [newChatBtn, wideScreenBtn, fullWindowBtn, fullScreenBtn, tooltipDiv]
        const elemToInsertBefore = (
            /chatgpt|openai/.test(site) ? chatbar.querySelector('button[class*="right"]') // ChatGPT pre-5/2024
                                       || chatbar.lastChild // ChatGPT post-5/2024 + Poe
                                        : chatbar.children[1] ) // Poe
        elemsToInsert.forEach(elem => chatbar.insertBefore(elem, elemToInsertBefore))
    }

    function updateBtnSVG(mode, state = '') {

        // Define SVG viewbox + elems
        const svgViewBox = ( mode == 'newChat' ? '11 6 ' : mode == 'fullWindow' ? '-2 -0.5 ' : '8 8 ' ) // move to XY coords to crop whitespace
                         + ( mode == 'newChat' ? '13 13' : mode == 'fullWindow' ? '24 24' : '20 20' ) // shrink to fit size
        const fullScreenONelems = [
            createSVGelem('path', { fill: btnColor, d: 'm14,14-4,0 0,2 6,0 0,-6 -2,0 0,4 0,0 z' }),
            createSVGelem('path', { fill: btnColor, d: 'm22,14 0,-4 -2,0 0,6 6,0 0,-2 -4,0 0,0 z' }),
            createSVGelem('path', { fill: btnColor, d: 'm20,26 2,0 0,-4 4,0 0,-2 -6,0 0,6 0,0 z' }),
            createSVGelem('path', { fill: btnColor, d: 'm10,22 4,0 0,4 2,0 0,-6 -6,0 0,2 0,0 z' }) ]
        const fullScreenOFFelems = [
            createSVGelem('path', { fill: btnColor, d: 'm10,16 2,0 0,-4 4,0 0,-2 L 10,10 l 0,6 0,0 z' }),
            createSVGelem('path', { fill: btnColor, d: 'm20,10 0,2 4,0 0,4 2,0 L 26,10 l -6,0 0,0 z' }),
            createSVGelem('path', { fill: btnColor, d: 'm24,24 -4,0 0,2 L 26,26 l 0,-6 -2,0 0,4 0,0 z' }),
            createSVGelem('path', { fill: btnColor, d: 'M 12,20 10,20 10,26 l 6,0 0,-2 -4,0 0,-4 0,0 z' }) ]
        const fullWindowElems = [
            createSVGelem('rect', { x: '3', y: '3', width: '17', height: '17', rx: '2', ry: '2' }),
            createSVGelem('line', { x1: '9', y1: '3', x2: '9', y2: '21' }) ]
        const wideScreenONelems = [
            createSVGelem('path', { fill: btnColor, 'fill-rule': 'evenodd',
                d: 'm26,13 0,10 -16,0 0,-10 z m-14,2 12,0 0,6 -12,0 0,-6 z' }) ]
        const wideScreenOFFelems = [
            createSVGelem('path', { fill: btnColor, 'fill-rule': 'evenodd',
                d: 'm28,11 0,14 -20,0 0,-14 z m-18,2 16,0 0,10 -16,0 0,-10 z' }) ]
        const newChatElems = [ createSVGelem('path', { fill: btnColor, d: 'M22,13h-4v4h-2v-4h-4v-2h4V7h2v4h4V13z' }) ]

        // Pick appropriate button/elements
        const [button, ONelems, OFFelems] = (
            mode == 'fullScreen' ? [fullScreenBtn, fullScreenONelems, fullScreenOFFelems]
          : mode == 'fullWindow' ? [fullWindowBtn, fullWindowElems, fullWindowElems]
          : mode == 'wideScreen' ? [wideScreenBtn, wideScreenONelems, wideScreenOFFelems]
                                 : [newChatBtn, newChatElems, newChatElems])

        // Set SVG attributes
        const buttonSVG = button.querySelector('svg') || document.createElementNS('http://www.w3.org/2000/svg', 'svg')
        buttonSVG.setAttribute('height', 18) // prevent shrinking
        if (mode == 'fullWindow') { // stylize full-window button
            buttonSVG.setAttribute('stroke', btnColor)
            buttonSVG.setAttribute('fill', 'none')
            buttonSVG.setAttribute('stroke-width', '2')
            buttonSVG.setAttribute('height', site == 'poe' ? '2em' : 17)
            buttonSVG.setAttribute('width', site == 'poe' ? '2em' : 17)
        }
        buttonSVG.setAttribute('class', sendSVGclasses) // assign borrowed classes
        buttonSVG.setAttribute('viewBox', svgViewBox) // set pre-tweaked viewbox
        buttonSVG.style.pointerEvents = 'none' // prevent triggering tooltips twice
        if (/chatgpt|openai/.test(site)) // override button resizing
            buttonSVG.style.height = buttonSVG.style.width = `${ isGPT4oUI ? 1.25 : 1.3 }rem`

        // Update SVG elements
        while (buttonSVG.firstChild) { buttonSVG.removeChild(buttonSVG.firstChild) }
        const svgElems = config[mode] || state.toLowerCase() == 'on' ? ONelems : OFFelems
        svgElems.forEach(elem => buttonSVG.append(elem))

        // Update SVG
        if (!button.contains(buttonSVG)) button.append(buttonSVG)
    }

    function createSVGelem(tagName, attributes) {
        const elem = document.createElementNS('http://www.w3.org/2000/svg', tagName)
        for (const attr in attributes) elem.setAttributeNS(null, attr, attributes[attr])
        return elem
    }

    // Define TOOLTIP functions

    function toggleTooltip(event) {
        updateTooltip(event.currentTarget.id.replace(/-button$/, ''))
        tooltipDiv.style.opacity = event.type == 'mouseover' ? '1' : '0'
    }

    function updateTooltip(buttonType) { // text & position
        tooltipDiv.innerText = msgs['tooltip_' + buttonType + (
            !/full|wide/i.test(buttonType) ? '' : (config[buttonType] ? 'OFF' : 'ON'))]
        const ctrAddend = 25 + ( site == 'poe' ? 45 : 12 ),
              spreadFactor = site == 'poe' ? 35 : 30.5,
              iniRoffset = spreadFactor * ( buttonType.includes('fullScreen') ? 1
                                          : buttonType.includes('fullWindow') ? 2
                                          : buttonType.includes('wide') ? 3 : 4 ) + ctrAddend
        tooltipDiv.style.right = `${ // horizontal position
            iniRoffset - tooltipDiv.getBoundingClientRect().width /2 }px`
    }

    // Define TOGGLE functions

    function activateMode(mode) {
        if (mode == 'wideScreen') { document.head.append(wideScreenStyle) ; syncMode('wideScreen') }
        else if (mode == 'fullWindow') {
            document.head.append(fullWindowStyle)
            if (site == 'poe') syncMode('fullWindow') ; else chatgpt.sidebar.hide()
        } else if (mode == 'fullScreen') document.documentElement.requestFullscreen()
    }

    function deactivateMode(mode) {
        if (mode == 'wideScreen')
            try { document.head.removeChild(wideScreenStyle) ; syncMode('wideScreen') } catch (err) {}
        else if (mode == 'fullWindow') {
            try { document.head.removeChild(fullWindowStyle) } catch (err) {}
            if (/chatgpt|openai/.test(site)) chatgpt.sidebar.show()
            else if (site == 'poe') syncMode('fullWindow') // since not sidebarObserve()'d
        } else if (mode == 'fullScreen') {
            if (config.f11)
                siteAlert(msgs.alert_pressF11 || 'Press F11 to exit full screen',
                    ( msgs.alert_f11reason || 'F11 was used to enter full screen, and due to browser security reasons,'
                        + 'the same key must be used to exit it' ) + '.')
            document.exitFullscreen().catch(err => console.error(config.appSymbol + ' » Failed to exit fullscreen', err))
        }
    }

    function toggleMode(mode, state = '') {
        switch (state.toUpperCase()) {
            case 'ON' : activateMode(mode) ; break
            case 'OFF' : deactivateMode(mode) ; break
            default : config[mode] ? deactivateMode(mode) : activateMode(mode)
        }
    }

    // Define SYNC functions

    function isFullWindow() {
        return site == 'poe' ? !!document.getElementById('fullWindow-mode')
                             : chatgpt.sidebar.isOff()
    }

    function syncMode(mode) { // setting + icon + tooltip
        const state = ( mode == 'wideScreen' ? !!document.getElementById('wideScreen-mode')
                      : mode == 'fullWindow' ? isFullWindow()
                                             : chatgpt.isFullScreen() )
        saveSetting(mode, state) ; updateBtnSVG(mode) ; updateTooltip(mode)
        if (mode == 'fullWindow') syncFullerWindows(state)
        if (!config.notifDisabled) // notify synced state
            notify(`${ msgs['mode_' + mode] } ${ state ? 'ON' : 'OFF' }`)
        config.modeSynced = true ; setTimeout(() => config.modeSynced = false, 100) // prevent repetition
    }

    function syncFullerWindows(fullWindowState) {
        if (fullWindowState && config.fullerWindows && !config.wideScreen) { // activate fuller windows
            document.head.append(wideScreenStyle) ; updateBtnSVG('wideScreen', 'on')
        } else if (!fullWindowState) { // de-activate fuller windows
            try { document.head.removeChild(fullWindowStyle) } catch (err) {} // to remove style too so sidebar shows
            if (!config.wideScreen) { // disable widescreen if result of fuller window
                try { document.head.removeChild(wideScreenStyle) } catch (err) {}
                updateBtnSVG('wideScreen', 'off')
    }}}

    function updateTweaksStyle() {
        tweaksStyle.innerText = (
            /chatgpt|openai/.test(site) ? (
                  ( inputSelector + '{ margin-right: -43px }' ) // widen/narrow input to be flush w/ btns
                + ( '[id$="-button"]:hover { opacity: 80% !important }' ) // dim chatbor btns on hover
                + ( config.hiddenHeader ? hhStyle : '' ) // hide header
                + ( config.hiddenFooter ? hfStyle : '' )) // hide footer
          : site == 'poe' ? 'button[class*="Voice"] { margin: 0 -3px 0 -8px }' // h-pad mic btn for even spread
          : '' )
        + ( !config.tcbDisabled ? tcbStyle : '' ) // expand text input vertically
        + `#newChat-button { display: ${ config.ncbDisabled ? 'none' : 'flex' }}`
    }

    function updateWidescreenStyle() {
        wideScreenStyle.innerText = (
              /chatgpt|openai/.test(site) ? (
                  '.text-base { max-width: 100% !important }' // widen outer container
                + '.text-base:nth-of-type(2) { max-width: 97% !important }' // widen inner container
                + '#__next > div > div.flex { width: 100px }' ) // prevent sidebar shrinking when zoomed
            : site == 'poe' ? (
                  '[class*="ChatMessagesView"] { width: 100% !important }' // widen outer container
                + '[class^="Message"] { max-width: 100% !important }' ) // widen speech bubbles
            : '' )
        if (config.widerChatbox) wideScreenStyle.innerText += wcbStyle
    }

    // Run MAIN routine

    // Init MENU objs
    const menuIDs = [] // to store registered cmds for removal while preserving order
    const menuState = {
        symbol: ['❌', '✔️'], word: ['OFF', 'ON'],
        separator: getUserscriptManager() == 'Tampermonkey' ? ' — ' : ': '
    }

    // Define UI element SELECTORS
    const inputSelector = /chatgpt|openai/.test(site) ? 'form textarea[id*="prompt"]'
                        : site == 'poe' ? '[class*="InputContainer_textArea"] textarea, [class*="InputContainer_textArea"]::after' : '',
          sidebarSelector = /chatgpt|openai/.test(site) ? '#__next > div > div.dark'
                          : site == 'poe' ? 'menu[class*="sidebar"], aside[class*="sidebar"]' : '',
          sidepadSelector = '#__next > div > div',
          headerSelector = /chatgpt|openai/.test(site) ? 'main .sticky' : ''
    let footerSelector = 'footer'
    try { footerSelector = /chatgpt|openai/.test(site) ?
              chatgpt.getFooterDiv()?.classList.toString().replace(/([:[\]\\])/g, '\\$1').replace(/^| /g, '.') : ''
    } catch (err) {}

    // AUTO-FOCUS ChatGPT chatbar if enabled
    if (/chatgpt|openai/.test(site) && !config.autoFocusChatbarDisabled) {
        await Promise.race([
            new Promise(resolve => {
                (function checkSecondChatbarBtn() { // since it causes de-focus
                    const chatbarBtns = document.querySelector(inputSelector)?.parentNode.parentNode.getElementsByTagName('button')
                    chatbarBtns?.length >= 2 ? resolve(true) : setTimeout(checkSecondChatbarBtn, 200)
                })();
            }), new Promise(resolve => setTimeout(resolve, 3000)) // timeout after 3s
        ])
        document.querySelector(inputSelector)?.focus()
    }

    // Create browser TOOLBAR MENU or DISABLE SCRIPT if extension installed
    const extensionInstalled = await Promise.race([
        new Promise(resolve => {
            (function checkExtensionInstalled() {
                if (document.querySelector('[cwm-extension-installed]')) resolve(true)
                else setTimeout(checkExtensionInstalled, 200)
            })()
        }), new Promise(resolve => setTimeout(() => resolve(false), 1500))])
    if (extensionInstalled) { // disable script/menu
        GM_registerMenuCommand(menuState.symbol[0] + ' ' + ( msgs.menuLabel_disabled || 'Disabled (extension installed)' ),
            () => { return }) // disable menu
        return // exit script
    } else registerMenu() // create functional menu

    // Init UI flag
    const isGPT4oUI = document.documentElement.className.includes(' ')

    // Save FULL-WINDOW + FULL SCREEN states
    config.fullWindow = /chatgpt|openai/.test(site) ? isFullWindow() : config.fullWindow
    config.fullScreen = chatgpt.isFullScreen()

    // Stylize ALERTS
    if (!document.getElementById('chatgpt-alert-override-style')) {
        const chatgptAlertStyle = document.createElement('style')
        chatgptAlertStyle.id = 'chatgpt-alert-override-style'
        chatgptAlertStyle.innerText = (
            ( chatgpt.isDarkMode() ? '.chatgpt-modal > div { border: 1px solid white }' : '' )
          + '.chatgpt-modal button {'
              + 'font-size: 0.77rem ; text-transform: uppercase ;'
              + 'border-radius: 0 !important ; padding: 5px !important ; min-width: 102px }'
          + '.modal-buttons { margin-left: -13px !important }'
        )
        document.head.append(chatgptAlertStyle)
    }

    // Collect SEND BUTTON classes
    const sendBtn = document.querySelector('[data-testid="send-button"]') // pre-GPT-4o
                 || document.querySelector('path[d*="M15.192 8.906a1.143"]')?.parentNode.parentNode; // post-GPT-4o
    const sendBtnClasses = sendBtn?.classList || [],
          sendSVGclasses = sendBtn?.querySelector('svg')?.classList || []

    // Create/stylize TOOLTIP div
    const tooltipDiv = document.createElement('div')
    tooltipDiv.classList.add('toggle-tooltip')
    const tooltipStyle = document.createElement('style')
    tooltipStyle.innerText = '.toggle-tooltip {'
        + 'background-color: rgba(0, 0, 0, 0.71) ; padding: 5px ; border-radius: 6px ; border: 1px solid #d9d9e3 ;' // bubble style
        + 'font-size: 0.85rem ; color: white ;' // font style
        + 'position: absolute ; bottom: 50px ;' // v-position
        + 'box-shadow: 4px 6px 16px 0px rgb(0 0 0 / 38%) ;' // drop shadow
        + 'opacity: 0 ; transition: opacity 0.1s ; z-index: 9999 ;' // visibility
        + '-webkit-user-select: none ; -moz-user-select: none ; -ms-user-select: none ; user-select: none }' // disable select
    document.head.append(tooltipStyle)

    // Create/apply general style TWEAKS
    const tweaksStyle = document.createElement('style'),
          tcbStyle = inputSelector + '{ max-height: 68vh !important }', // heighten chatbox
          hhStyle = headerSelector + '{ display: none !important }' // hide header
                  + ( /chatgpt|openai/.test(site) ? 'main { padding-top: 12px }' : '' ), // increase top-padding
          hfStyle = footerSelector + '{ color: transparent !important ;' // hide footer text
                                   + '  padding: .1rem 0 0 !important }' // reduce v-padding
    updateTweaksStyle() ; document.head.append(tweaksStyle)

    // Create WIDESCREEN style
    const wideScreenStyle = document.createElement('style')
    wideScreenStyle.id = 'wideScreen-mode' // for syncMode()
    const wcbStyle = ( // Wider Chatbox for updateWidescreenStyle()
        /chatgpt|openai/.test(site) ? 'main form { max-width: 96% !important }'
      : site == 'poe' ? '[class*=footerInner] { width: 100% }' : '' )
    updateWidescreenStyle()

    // Create FULL-WINDOW style
    const fullWindowStyle = document.createElement('style')
    fullWindowStyle.id = 'fullWindow-mode' // for syncMode()
    fullWindowStyle.innerText = (
          sidebarSelector + '{ display: none }' // hide sidebar
        + sidepadSelector + '{ padding-left: 0 }' ) // remove side padding

    // Create/insert chatbar BUTTONS
    const buttonTypes = ['fullScreen', 'fullWindow', 'wideScreen', 'newChat'],
          bOffset = site == 'poe' ? -1.5 : -13, rOffset = site == 'poe' ? -6 : -4
    let btnColor = setBtnColor()
    for (let i = 0 ; i < buttonTypes.length ; i++) {
        (buttonType => { // enclose in IIFE to separately capture button type for async listeners
            const buttonName = buttonType + 'Btn'
            window[buttonName] = document.createElement('div') // create button
            window[buttonName].id = buttonType + '-button' // for toggleTooltip()
            updateBtnSVG(buttonType) // insert icon
            window[buttonName].style.cssText = 'position: relative ; top: 0 ;'
                                             + `right: ${ rOffset + i * bOffset }px` // position left of prev button
            window[buttonName].style.cursor = 'pointer' // add finger cursor
            if (isGPT4oUI || site == 'poe') window[buttonName].style.position = 'relative' // override static pos
            if (/chatgpt|openai/.test(site)) { // assign classes + tweak styles
                window[buttonName].setAttribute('class', sendBtnClasses)
                window[buttonName].style.backgroundColor = 'transparent' // remove dark mode overlay
                window[buttonName].style.borderColor = 'transparent' // remove dark mode overlay
            } else if (site == 'poe') // lift buttons slightly
                window[buttonName].style.marginBottom = ( buttonType == 'newChat' ? '0.45' : '0.2' ) + 'rem'

            // Add click/hover listeners
            window[buttonName].onclick = () => {
                if (buttonType == 'newChat') {
                    if (/chatgpt|openai/.test(site)) chatgpt.startNewChat()
                    else if (site == 'poe') document.querySelector('header a[class*="button"]')?.click()
                } else toggleMode(buttonType)
            }
            window[buttonName].onmouseover = toggleTooltip
            window[buttonName].onmouseout = toggleTooltip

        })(buttonTypes[i])
    } insertBtns()

    // Monitor NODE CHANGES to auto-toggle once + maintain button visibility + update colors
    let isTempChat = false, prevSessionChecked = false
    const nodeObserver = new MutationObserver(([mutation]) => {

        // Check loaded keys to restore previous session's state
        if (!prevSessionChecked) {
            if (config.wideScreen) toggleMode('wideScreen', 'ON')
            if (config.fullWindow) { toggleMode('fullWindow', 'ON')
                if (/chatgpt|openai/.test(site)) { // sidebar observer doesn't trigger
                    syncFullerWindows(true) // so sync Fuller Windows...
                    if (!config.notifDisabled) // ... + notify
                        notify(( msgs.mode_fullWindow || 'Full-window' ) + ' ON')
            }}
            if (config.tcbDisabled) updateTweaksStyle() ; prevSessionChecked = true
        }

        insertBtns() // again or they constantly disappear

        // Update button colors on ChatGPT scheme or temp chat toggle
        if (/chatgpt|openai/.test(site)) {
            let chatbarBGdiv = document.querySelector('textarea')
            for (let i = 0 ; i < ( isGPT4oUI ? 3 : 1 ) ; i++) { chatbarBGdiv = chatbarBGdiv.parentNode }
            if (chatbarBGdiv) {
                const chatbarBGisBlack = chatbarBGdiv.classList.contains('bg-black');
                if ((mutation.type === 'attributes' && mutation.attributeName === 'class') // potential scheme toggled
                     || (chatbarBGisBlack && !isTempChat) || (!chatbarBGisBlack && isTempChat)) { // temp chat toggled
                            btnColor = setBtnColor() // init new color
                            chatbarBGdiv.style.overflow = 'visible' // allow tooltips to overflow pre-GPT4o UI
                            const buttons = ['fullScreen', 'fullWindow', 'wideScreen', 'newChat']
                            buttons.forEach(btn => updateBtnSVG(btn)) ; isTempChat = !isTempChat
        }}}
    })
    nodeObserver.observe(document.documentElement, { attributes: true }) // <html> for page scheme toggles
    nodeObserver.observe(document.querySelector('main'), { attributes: true, subtree: true }); // <main> for chatbar changes

    // Monitor SIDEBAR to update full-window setting
    if (/chatgpt|openai/.test(site)) {
        const sidebarObserver = new MutationObserver(() => {
            const fullWindowState = isFullWindow()
            if ((config.fullWindow && !fullWindowState) || (!config.fullWindow && fullWindowState))
                if (!config.modeSynced) syncMode('fullWindow')
        })
        setTimeout(() => // delay half-sec before observing to avoid repeated toggles from nodeObserver
            sidebarObserver.observe(document.body, {
                subtree: true, childList: false, attributes: true }), 500)
    }

    // Add RESIZE LISTENER to update full screen setting/button + disable F11 flag
    window.onresize = () => {
        const fullScreenState = chatgpt.isFullScreen()
        if (config.fullScreen && !fullScreenState) { syncMode('fullScreen') ; config.f11 = false } // exiting full screen
        else if (!config.fullScreen && fullScreenState) syncMode('fullScreen') // entering full screen
    }

    // Add KEY LISTENER to enable flag on F11 + stop generating text on ESC
    window.onkeydown = event => {
        if ((event.key == 'F11' || event.keyCode == 122) && !config.fullScreen) config.f11 = true
        else if ((event.key == 'Escape' || event.keyCode == 27) && !chatgpt.isIdle()) chatgpt.stop()
    }

})()