ChatGPT Widescreen Mode 🖥️

Adds Widescreen + Fullscreen modes to ChatGPT for enhanced viewing + reduced scrolling

Install this script?
Author's suggested script

You may also like Autoclear ChatGPT History.

Install this script
// ==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.4.5
// @license             MIT
// @compatible          chrome
// @compatible          firefox
// @compatible          edge
// @compatible          opera
// @compatible          brave
// @compatible          vivaldi
// @compatible          librewolf
// @compatible          ghost
// @compatible          qq
// @match               *://chat.openai.com/*
// @match               *://poe.com/*
// @icon                https://media.chatgptwidescreen.com/images/icons/widescreen-robot-emoji/icon48.png
// @icon64              https://media.chatgptwidescreen.com/images/icons/widescreen-robot-emoji/icon64.png
// @require             https://cdn.jsdelivr.net/npm/@kudoai/chatgpt.js@2.6.8/dist/chatgpt.min.js#sha256-veeaKpmLF7CZ6kxNfn8SMjD36ijLffGCzel9G4ykpPI=
// @connect             raw.githubusercontent.com
// @connect             greasyfork.org
// @grant               GM_setValue
// @grant               GM_getValue
// @grant               GM_registerMenuCommand
// @grant               GM_unregisterMenuCommand
// @grant               GM_openInTab
// @grant               GM.xmlHttpRequest
// @noframes
// @homepageURL         https://chatgptwidescreen.com
// @supportURL          https://chatgptwidescreen.com/support
// ==/UserScript==

// NOTE: This script relies on the powerful chatgpt.js library @ https://chatgpt.js.org (c) 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', userLanguage: chatgpt.getUserLanguage(),
        gitHubURL: 'https://github.com/adamlui/chatgpt-widescreen',
        greasyForkURL: 'https://greasyfork.org/scripts/461473-chatgpt-widescreen-mode' }
    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', 'raw.githubusercontent.com') + '/main/'
    loadSetting('fullerWindows', 'fullWindow', 'hiddenFooter', 'hiddenHeader', 'notifDisabled',
                'ncbDisabled', 'tcbDisabled', 'widerChatbox', 'wideScreen')

    // 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
        GM.xmlHttpRequest({ method: 'GET', url: msgHref, onload: onLoad })
        function onLoad(response) {
            try { // to return localized messages.json
                const messages = new Proxy(JSON.parse(response.responseText), {
                    get(target, prop) { // remove need to ref nested keys
                        if (typeof target[prop] == 'object' && target[prop] !== null && 'message' in target[prop]) {
                            return target[prop].message
                }}}) ; resolve(messages)
            } catch (err) { // if 404
                msgXHRtries++ ; if (msgXHRtries === 3) return // 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
                GM.xmlHttpRequest({ method: 'GET', url: msgHref, onload: onLoad })
            }
        }
    }) ; const messages = 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() {
        const menuIDs = [] // empty to store newly registered cmds for removal while preserving order

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

        // Add command to toggle taller chatbox when typing
        const tcbLabel = '↕️ ' + ( messages.menuLabel_tallerChatbox || 'Taller Chatbox' )
                       + state.separator + state.word[+config.tcbDisabled]
        menuIDs.push(GM_registerMenuCommand(tcbLabel, () => {
            saveSetting('tcbDisabled', !config.tcbDisabled)
            updateTweaksStyle()
            if (!config.notifDisabled)
                notify(`${ messages.menuLabel_tallerChatbox || 'Taller Chatbox' }: ${ state.word[+config.tcbDisabled] }`)
            for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu() // refresh menu
        }))

        // Add command to toggle wider chatbox with widescreen mode
        const wcbLabel = '↔️ ' + ( messages.menuLabel_widerChatbox || 'Wider Chatbox' )
                       + state.separator + state.word[+!config.widerChatbox]
        menuIDs.push(GM_registerMenuCommand(wcbLabel, () => {
            saveSetting('widerChatbox', !config.widerChatbox)
            updateWidescreenStyle()
            if (!config.notifDisabled)
                notify(`${ messages.menuLabel_widerChatbox || 'Wider Chatbox' }: ${ state.word[+!config.widerChatbox] }`)
            for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu() // refresh menu
        }))

        // Add command to hide New Chat button
        const hncLabel = state.symbol[+config.ncbDisabled] + ' '
                       + ( messages.menuLabel_newChatBtn || 'New Chat Button' )
                       + state.separator + state.word[+config.ncbDisabled]
        menuIDs.push(GM_registerMenuCommand(hncLabel, () => {
            saveSetting('ncbDisabled', !config.ncbDisabled)
            updateTweaksStyle()
            notify(`${ messages.menuLabel_newChatBtn || 'New Chat Button' }: ${ state.word[+config.ncbDisabled] }`)
            for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu() // refresh menu
        }))

        if (site == 'openai') {

            // Add command to toggle hidden header
            const hhLabel = state.symbol[+!config.hiddenHeader] + ' '
                          + ( messages.menuLabel_hiddenHeader || 'Hidden Header' )
                          + state.separator + state.word[+!config.hiddenHeader]
            menuIDs.push(GM_registerMenuCommand(hhLabel, () => {
                saveSetting('hiddenHeader', !config.hiddenHeader)
                updateTweaksStyle()
                if (!config.notifDisabled)
                    notify(`${ messages.menuLabel_hiddenHeader || 'Hidden Header' }: ${ state.word[+!config.hiddenHeader] }`)
                for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu() // refresh menu
            }))

            // Add command to toggle hidden footer
            const hfLabel = state.symbol[+!config.hiddenFooter] + ' '
                          + ( messages.menuLabel_hiddenFooter || 'Hidden Footer' )
                          + state.separator + state.word[+!config.hiddenFooter]
            menuIDs.push(GM_registerMenuCommand(hfLabel, () => {
                saveSetting('hiddenFooter', !config.hiddenFooter)
                updateTweaksStyle()
                if (!config.notifDisabled)
                    notify(`${ messages.menuLabel_hiddenFooter || 'Hidden Footer' }: ${ state.word[+!config.hiddenFooter] }`)
                for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu() // refresh menu
            }))
        }

        // Add command to show notifications when switching modes
        const mnLabel = state.symbol[+config.notifDisabled] + ' '
                      + ( messages.menuLabel_modeNotifs || 'Mode Notifications' )
                      + state.separator + state.word[+config.notifDisabled]
        menuIDs.push(GM_registerMenuCommand(mnLabel, () => {
            saveSetting('notifDisabled', !config.notifDisabled)
            notify(`${ messages.menuLabel_modeNotifs || 'Mode Notifications' }: ${ state.word[+config.notifDisabled] }`)
            for (const id of menuIDs) { GM_unregisterMenuCommand(id) } registerMenu() // refresh menu
        }))

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

    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 aboutAlertID = alert(
            messages.appName || config.appName, // title
            `<span style="${ headingStyle }"><b>🏷️ <i>${ messages.about_version || 'Version' }</i></b>: </span>`
                + `<span style="${ pStyle }">${ GM_info.script.version }</span>\n`
            + `<span style="${ headingStyle }"><b>⚡ <i>${ messages.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>${ messages.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 reviewAlertID = chatgpt.alert(( messages.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') },
                          function futurepedia() { safeWindowOpen(
                              'https://www.futurepedia.io/tool/chatgpt-widescreen-mode#chatgpt-widescreen-mode-review') }])
                    document.getElementById(reviewAlertID).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(aboutAlertID).querySelectorAll('button')) {
            if (/updates/i.test(button.textContent)) button.textContent = (
                '🚀 ' + ( messages.buttonLabel_updateCheck || 'Check for Updates' ))
            else if (/support/i.test(button.textContent)) button.textContent = (
                '🧠 ' + ( messages.buttonLabel_getSupport || 'Get Support' ))
            else if (/review/i.test(button.textContent)) button.textContent = (
                '⭐ ' + ( messages.buttonLabel_leaveReview || 'Leave a Review' ))
            else if (/apps/i.test(button.textContent)) button.textContent = (
                '🤖 ' + ( messages.buttonLabel_moreApps || 'More ChatGPT Apps' ))
            else button.style.display = 'none' // hide Dismiss button
        }
    }

    function updateCheck() {

        // Fetch latest meta
        const currentVer = GM_info.script.version
        GM.xmlHttpRequest({
            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 updateAlertID = alert(( messages.alert_updateAvail || 'Update available' ) + '! 🚀', // title
                            ( messages.alert_newerVer || 'An update to' ) + ' ' // msg
                                + ( messages.appName || config.appName ) + ' '
                                + `(v ${ latestVer }) ${ messages.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') + '" '
                                    + `> ${ messages.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
                                ).onclose = () => location.reload() },
                            '', updateAlertWidth
                        )

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

                        return
                }}

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

    // Define FEEDBACK functions

    function notify(msg, position = '', notifDuration = '', shadow = '') {
        chatgpt.notify(`${ config.appSymbol } ${ msg }`, position, notifDuration,
            shadow || chatgpt.isDarkMode() ? '' : 'shadow')
    }

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

    // Define BUTTON functions

    function setBtnColor() { return (
        site == 'openai' ? ( chatgpt.isDarkMode() || chatgpt.history.isOff() ? 'white' : '#202123' ) : 'currentColor' )}

    function insertBtns() {
        const chatbar = document.querySelector(chatbarSelector)
        if (chatbar.contains(wideScreenBtn)) return // if buttons aren't missing, exit
        const elemsToInsert = [newChatBtn, wideScreenBtn, fullWindowBtn, fullScreenBtn, tooltipDiv],
              leftMostBtn = chatbar.querySelector('button' + ( site != 'poe' ? '[class*="right"]' : ''))
        if (site == 'openai') // allow tooltips to overflow
            chatbar.classList.remove('overflow-hidden')
        else if (site == 'poe') // elevate nested non-send button to chatbar
            chatbar.insertBefore(leftMostBtn, chatbar.lastChild)
        elemsToInsert.forEach(elem => chatbar.insertBefore(elem, leftMostBtn))
    }

    function updateBtnSVG(mode, state = '') {

        // Define SVG viewbox + elems
        const svgViewBox = (
            // move to XY coords to crop whitespace
            ( mode == 'newChat' ? '11 6 ' : mode == 'wideScreen' ? '8 8 ' : mode == 'fullWindow' ? '0 0 ' : '8 8 ' )
            // shrink to fit size
          + ( mode == 'newChat' ? '13 13' : mode == 'wideScreen' ? '20 20 ' : mode == 'fullWindow' ? '24 24' : '20 20' ))
        const fullScreenONelems = [
            createSVGelem('path', { fill: buttonColor, d: 'm14,14-4,0 0,2 6,0 0,-6 -2,0 0,4 0,0 z' }),
            createSVGelem('path', { fill: buttonColor, d: 'm22,14 0,-4 -2,0 0,6 6,0 0,-2 -4,0 0,0 z' }),
            createSVGelem('path', { fill: buttonColor, d: 'm20,26 2,0 0,-4 4,0 0,-2 -6,0 0,6 0,0 z' }),
            createSVGelem('path', { fill: buttonColor, d: 'm10,22 4,0 0,4 2,0 0,-6 -6,0 0,2 0,0 z' }) ]
        const fullScreenOFFelems = [
            createSVGelem('path', { fill: buttonColor, d: 'm10,16 2,0 0,-4 4,0 0,-2 L 10,10 l 0,6 0,0 z' }),
            createSVGelem('path', { fill: buttonColor, d: 'm20,10 0,2 4,0 0,4 2,0 L 26,10 l -6,0 0,0 z' }),
            createSVGelem('path', { fill: buttonColor, d: 'm24,24 -4,0 0,2 L 26,26 l 0,-6 -2,0 0,4 0,0 z' }),
            createSVGelem('path', { fill: buttonColor, 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: '18', height: '18', rx: '2', ry: '2' }),
            createSVGelem('line', { x1: '9', y1: '3', x2: '9', y2: '21' }) ]
        const wideScreenONelems = [
            createSVGelem('path', { fill: buttonColor, '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: buttonColor, '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: buttonColor, 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])

        // Initialize rem margin offset vs. OpenAI's .mr-1 for hover overlay centeredness
        const lMargin = mode == 'wideScreen' ? .11 : .12, rMargin = (.25 - lMargin)

        // 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', buttonColor)
            buttonSVG.setAttribute('fill', 'none')
            buttonSVG.setAttribute('stroke-width', '2')
            buttonSVG.setAttribute('height', site == 'poe' ? '2em' : 18)
            buttonSVG.setAttribute('width', site == 'poe' ? '2em' : 18)
        }
        buttonSVG.setAttribute('class', sendImgClasses) // assign borrowed classes
        buttonSVG.setAttribute( // center oerlay + prevent triggering tooltips twice
            'style', `margin: 0 ${ rMargin }rem 0 ${ lMargin }rem ; pointer-events: none`)
        buttonSVG.setAttribute('viewBox', svgViewBox) // set pre-tweaked viewbox

        // 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' ? '0.8' : '0'
    }

    function updateTooltip(buttonType) { // text & position
        tooltipDiv.innerText = messages['tooltip_' + buttonType + (
            !/full|wide/i.test(buttonType) ? '' : (config[buttonType] ? 'OFF' : 'ON'))]
        const ctrAddend = 25 + ( site == 'poe' ? 42 : 0 ), spreadFactor = site == 'poe' ? 42 : 30,
              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') {
            if (site == 'openai') chatgpt.sidebar.hide()
            else /* poe */ syncMode('fullWindow') // since not sidebarObserve()'d
            document.head.append(fullWindowStyle)
        } 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 (site == 'openai') chatgpt.sidebar.show()
            else /* poe */ syncMode('fullWindow') // since not sidebarObserve()'d
        } else if (mode == 'fullScreen') {
            if (config.f11)
                alert(messages.alert_pressF11 || 'Press F11 to exit full screen',
                    ( messages.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 == 'openai' ? chatgpt.sidebar.isOff()
               : /* poe */ !!document.querySelector('#fullWindow-mode') )
    }

    function syncMode(mode) { // setting + icon + tooltip
        const state = ( mode == 'wideScreen' ? !!document.querySelector('#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(`${ messages['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 = (
              site == 'openai' ? (
                  inputSelector + `{ padding-right: ${ config.ncbDisabled ? 126 : 152 }px }` // narrow input to accomodate btns
                + 'div.group > div > div > div > div:nth-child(2) { ' // move response paginator
                    + 'position: relative ; left: 66px ; top: 7px } ' // ...below avatar to avoid cropping
                + ( config.hiddenHeader ? hhStyle : '' ) // hide header
                + ( config.hiddenFooter ? hfStyle : '' )) : '' ) // hide footer
        + ( !config.tcbDisabled ? tcbStyle : '' ) // expand text input vertically
        + `#newChat-button { display: ${ config.ncbDisabled ? 'none' : 'flex' }}`
    }

    function updateWidescreenStyle() {
        wideScreenStyle.innerText = (
              site == 'openai' ? (
                  '.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^="MainColumn_column"] { width: 100% !important }' // widen outer container
                + '[class*="ChatPageMain_container"] { max-width: 97% !important }' // widen inner container
                + '[class^="Message"] { max-width: 100% !important }' // widen speech bubbles
                + '[class^="ChatMessageInputFooter"] { max-width: 618px ; margin: auto }' ) // preserve chatbar width
            : '')
        if (config.widerChatbox) wideScreenStyle.innerText += wcbStyle
    }

    // Run MAIN routine

    if (site == 'openai') await chatgpt.isLoaded()

    // Create browser toolbar menu or disable script if extension installed
    const state = {
        symbol: ['✔️', '❌'], word: ['ON', 'OFF'],
        separator: getUserscriptManager() == 'Tampermonkey' ? ' — ' : ': ' }
    if (document.documentElement.getAttribute('cwm-extension-installed')) { // if extension installed
        GM_registerMenuCommand(state.symbol[1] + ' ' + ( messages.menuLabel_disabled || 'Disabled (extension installed)' ),
            () => { return }) // disable menu
        return // exit script
    } else registerMenu() // create functional menu

    // Define UI element selectors
    const inputSelector = site == 'openai' ? 'form textarea[id*="prompt"]'
                        : /* poe */ '[class*="InputContainer_textArea"] textarea, [class*="InputContainer_textArea"]::after',
          sidebarSelector = site == 'openai' ? '#__next > div > div.dark'
                          : /* poe */ 'menu[class*="sidebar"], aside[class*="sidebar"]',
          sidepadSelector = '#__next > div > div',
          headerSelector = site == 'openai' ? 'main .sticky' : '',
          footerSelector = site == 'openai' ? 'main form ~ div' : '',
          chatbarSelector = site == 'openai' ? 'div[class*="textarea:focus"'
                          : site == 'poe' ? 'div[class*="ChatMessageInputContainer"]' : ''

    // Save full-window + full screen states
    config.fullWindow = site == 'openai' ? 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-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 button classes
    const sendBtnSelector = site == 'openai' ? 'form button[class*="bottom"]' : null,
          sendBtnClasses = document.querySelector(sendBtnSelector)?.classList || [],
          sendImgClasses = document.querySelector('form button[class*="bottom"] 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: black ; padding: 5px ; border-radius: 6px ; border: 1px solid #d9d9e3 ;' // bubble style
        + 'font-size: 0.7rem ; color: white ; ' // font style
        + 'position: absolute ; bottom: 50px ; ' // v-position
        + '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
                  + ( site == 'openai' ? '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()
        site == 'openai' ? 'main form { max-width: 96% !important }'
      : site == 'poe' ? '[class^="ChatMessageInputFooter"] { max-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: 0px }' ) // remove side padding

    // Create/insert chatbar buttons
    const buttonTypes = ['fullScreen', 'fullWindow', 'wideScreen', 'newChat'],
          bOffset = 1.77, // rem between buttons
          rOffset = ( // rem from right edge of chatbar
              site == 'openai' ? 3 : '' )
    let buttonColor = 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 = `right: ${ rOffset + i * bOffset }rem` // position left of prev button
            window[buttonName].style.cursor = 'pointer' // add finger cursor
            if (site != 'poe') // assign borrowed classes
                window[buttonName].setAttribute('class', sendBtnClasses)
            else if (site == 'poe') // lift buttons slightly
                window[buttonName].style.marginBottom = ( buttonType == 'newChat' ? '0.45' : '0.2' ) + 'rem'
            if (site == 'openai') { // style tweaks for OpenAI Gizmo UI
                window[buttonName].style.backgroundColor = 'transparent' // remove dark mode overlay
                window[buttonName].style.borderColor = 'transparent' // remove dark mode overlay
                window[buttonName].style.bottom = '0.91rem' // nudge up for flushness w/ send button
            }

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

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

    // Monitor node changes to maintain button visibility + auto-toggle once + manage send button's tooltip
    let prevSessionChecked = false
    const nodeObserver = new MutationObserver(([{ addedNodes, type }]) => {
        if (type == 'childList' && addedNodes.length) {
            insertBtns() // again or they constantly disappear

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

    }}) ; nodeObserver.observe(document.documentElement, { childList: true, subtree: true })

    // Monitor scheme changes to update button colors
    const schemeObserver = new MutationObserver(([{ type, target }]) => {
        if (target === document.documentElement && type == 'attributes' && target.getAttribute('class')) {
            buttonColor = setBtnColor()
            updateBtnSVG('fullScreen') ; updateBtnSVG('fullWindow') ; updateBtnSVG('wideScreen') ; updateBtnSVG('newChat')
    }}) ; schemeObserver.observe(document.documentElement, { attributes: true })

    // Monitor sidebar to update full-window setting
    if (site == 'openai') {
        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.addEventListener('resize', () => {
        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.addEventListener('keydown', 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()
    })

})()