Генератор надежных и случайных паролей, легкий и доступный на любом сайте. Расширенная настройка (длина, символы, цифры, заглавные буквы). Мгновенное копирование. Интеллектуальный многоязычный интерфейс.
// ==UserScript==
// @name 🔐 SecurePass — Robust & Random Password Generator
// @name:fr 🔐 SecurePass — Générateur de mots de passe robustes et aléatoires
// @name:es 🔐 SecurePass — Generador de contraseñas robustas y aleatorias
// @name:de 🔐 SecurePass — Generator für robuste und zufällige Passwörter
// @name:pt 🔐 SecurePass — Gerador de senhas robustas e aleatórias
// @name:ru 🔐 SecurePass — Генератор надежных и случайных паролей
// @name:zh-CN 🔐 SecurePass — 强大且随机的密码生成器
// @name:zh-TW 🔐 SecurePass — 強大且隨機的密碼產生器
// @name:ar 🔐 SecurePass — منشئ كلمات مرور قوية وعشوائية
// @name:ja 🔐 SecurePass — 強力でランダムなパスワード生成ツール
// @name:ko 🔐 SecurePass — 강력하고 무작위 비밀번호 생성기
// @name:it 🔐 SecurePass — Generatore di password robuste e casuali
// @name:hi 🔐 SecurePass — मजबूत और यादृच्छिक पासवर्ड जनरेटर
// @name:nl 🔐 SecurePass — Generator voor robuuste en willekeurige wachtwoorden
// @name:pl 🔐 SecurePass — Generator solidnych i losowych haseł
// @name:tr 🔐 SecurePass — Güçlü ve rastgele parola oluşturucu
// @name:sv 🔐 SecurePass — Generator för robusta och slumpmässiga lösenord
// @name:da 🔐 SecurePass — Generator til robuste og tilfældige adgangskoder
// @name:fi 🔐 SecurePass — Vahvojen ja satunnaisten salasanojen generaattori
// @name:no 🔐 SecurePass — Generator for robuste og tilfeldige passord
// @name:cs 🔐 SecurePass — Generátor robustních a náhodných hesel
// @name:sk 🔐 SecurePass — Generátor robustných a náhodných hesiel
// @name:hu 🔐 SecurePass — Erős és véletlenszerű jelszógenerátor
// @name:ro 🔐 SecurePass — Generator de parole robuste și aleatorii
// @name:bg 🔐 SecurePass — Генератор на надеждни и случайни пароли
// @name:uk 🔐 SecurePass — Генератор надійних і випадкових паролів
// @name:el 🔐 SecurePass — Δημιουργός ισχυρών και τυχαίων κωδικών
// @name:he 🔐 SecurePass — מחולל סיסמאות חזקות ואקראיות
// @name:id 🔐 SecurePass — Generator kata sandi kuat dan acak
// @name:ms 🔐 SecurePass — Penjana kata laluan kukuh dan rawak
// @name:th 🔐 SecurePass — ตัวสร้างรหัสผ่านที่แข็งแกร่งและสุ่ม
// @name:vi 🔐 SecurePass — Trình tạo mật khẩu mạnh và ngẫu nhiên
// @name:bn 🔐 SecurePass — শক্তিশালী ও এলোমেলো পাসওয়ার্ড জেনারেটর
// @name:fa 🔐 SecurePass — تولیدکننده رمز عبور قوی و تصادفی
// @name:ta 🔐 SecurePass — வலுவான மற்றும் சீரற்ற கடவுச்சொல் உருவாக்கி
// @name:ml 🔐 SecurePass — ശക്തവും യാദൃശ്ചികവുമായ പാസ്വേഡ് ജനറേറ്റർ
// @name:mr 🔐 SecurePass — मजबूत आणि यादृच्छिक पासवर्ड जनरेटर
// @name:ur 🔐 SecurePass — مضبوط اور بے ترتیب پاس ورڈ جنریٹر
// @name:pa 🔐 SecurePass — ਮਜ਼ਬੂਤ ਅਤੇ ਬੇਤਰਤੀਬ ਪਾਸਵਰਡ ਜਨਰੇਟਰ
// @name:gu 🔐 SecurePass — મજબૂત અને રેન્ડમ પાસવર્ડ જનરેટર
// @name:kn 🔐 SecurePass — ಬಲವಾದ ಮತ್ತು ಯಾದೃಚ್ಛಿಕ ಪಾಸ್ವರ್ಡ್ ಜನರೇಟರ್
// @name:te 🔐 SecurePass — బలమైన మరియు యాదృచ్ఛిక పాస్వర్డ్ జనరేటర్
// @name:si 🔐 SecurePass — ශක්තිමත් සහ අහඹු මුරපද ජනකය
// @name:km 🔐 SecurePass — ម៉ាស៊ីនបង្កើតពាក្យសម្ងាត់រឹងមាំ និងចៃដន្យ
// @name:lo 🔐 SecurePass — ຕົວສ້າງລະຫັດຜ່ານແຂງແຮງ ແລະ ສຸ່ມ
// @name:my 🔐 SecurePass — ခိုင်မာပြီး ကျပန်း စကားဝှက် ဖန်တီးသူ
// @name:ne 🔐 SecurePass — बलियो र यादृच्छिक पासवर्ड जनरेटर
// @name:am 🔐 SecurePass — ጠንካራ እና ድንገተኛ የይለፍ ቃል ፈጣሪ
// @name:sw 🔐 SecurePass — Jenereta ya nenosiri imara na nasibu
// @name:af 🔐 SecurePass — Robuuste en ewekansige wagwoordgenerator
// @name:et 🔐 SecurePass — Tugevate ja juhuslike paroolide generaator
// @name:lv 🔐 SecurePass — Izturīgu un nejaušu paroļu ģenerators
// @name:lt 🔐 SecurePass — Tvirtų ir atsitiktinių slaptažodžių generatorius
// @name:is 🔐 SecurePass — Öflugur og tilviljunarkenndur lykilorðagenerator
// @name:ga 🔐 SecurePass — Gineadóir pasfhocal láidir agus randamach
// @name:mt 🔐 SecurePass — Ġeneratur ta' passwords robusti u każwali
// @description Robust & random password generator, lightweight and available on every website. Advanced customization (length, symbols, numbers, uppercase). Instant copy. Intelligent multilingual interface.
// @description:fr Générateur de mots de passe robustes et aléatoires, léger et accessible sur tous les sites. Personnalisation avancée (longueur, symboles, chiffres, majuscules). Copie instantanée. Interface multilingue intelligente.
// @description:es Generador de contraseñas robustas y aleatorias, ligero y disponible en cualquier sitio web. Personalización avanzada (longitud, símbolos, números, mayúsculas). Copia instantánea. Interfaz multilingüe inteligente.
// @description:de Generator für robuste und zufällige Passwörter, leicht und auf jeder Website verfügbar. Erweiterte Anpassung (Länge, Symbole, Zahlen, Großbuchstaben). Sofortiges Kopieren. Intelligente mehrsprachige Oberfläche.
// @description:pt Gerador de senhas robustas e aleatórias, leve e disponível em qualquer site. Personalização avançada (tamanho, símbolos, números, maiúsculas). Cópia instantânea. Interface multilíngue inteligente.
// @description:ru Генератор надежных и случайных паролей, легкий и доступный на любом сайте. Расширенная настройка (длина, символы, цифры, заглавные буквы). Мгновенное копирование. Интеллектуальный многоязычный интерфейс.
// @description:zh-CN 强大且随机的密码生成器,轻量并适用于所有网站。高级自定义(长度、符号、数字、大写字母)。即时复制。智能多语言界面。
// @description:zh-TW 強大且隨機的密碼產生器,輕量並適用於所有網站。進階自訂(長度、符號、數字、大寫字母)。即時複製。智慧多語介面。
// @description:ar منشئ كلمات مرور قوية وعشوائية، خفيف ويعمل على جميع المواقع. تخصيص متقدم (الطول، الرموز، الأرقام، الأحرف الكبيرة). نسخ فوري. واجهة متعددة اللغات ذكية.
// @description:ja 強力でランダムなパスワード生成ツール。軽量で全サイト対応。高度なカスタマイズ(長さ、記号、数字、大文字)。即時コピー。多言語インターフェース。
// @description:ko 강력하고 무작위 비밀번호 생성기. 가볍고 모든 웹사이트에서 사용 가능. 고급 사용자 지정(길이, 기호, 숫자, 대문자). 즉시 복사. 다국어 인터페이스.
// @description:it Generatore di password robuste e casuali, leggero e disponibile su ogni sito. Personalizzazione avanzata (lunghezza, simboli, numeri, maiuscole). Copia istantanea. Interfaccia multilingue intelligente.
// @description:hi मजबूत और यादृच्छिक पासवर्ड जनरेटर, हल्का और सभी वेबसाइटों पर उपलब्ध। उन्नत अनुकूलन (लंबाई, प्रतीक, संख्याएँ, बड़े अक्षर)। त्वरित कॉपी। बहुभाषी इंटरफ़ेस।
// @description:nl Generator voor robuuste en willekeurige wachtwoorden, licht en beschikbaar op elke website. Geavanceerde aanpassing (lengte, symbolen, cijfers, hoofdletters). Direct kopiëren. Intelligente meertalige interface.
// @description:pl Generator solidnych i losowych haseł, lekki i dostępny na każdej stronie. Zaawansowana personalizacja (długość, symbole, cyfry, wielkie litery). Natychmiastowe kopiowanie. Inteligentny interfejs wielojęzyczny.
// @description:tr Güçlü ve rastgele parola oluşturucu, hafif ve tüm web sitelerinde kullanılabilir. Gelişmiş özelleştirme (uzunluk, semboller, sayılar, büyük harfler). Anında kopyalama. Akıllı çok dilli arayüz.
// @description:sv Generator för robusta och slumpmässiga lösenord, lätt och tillgänglig på alla webbplatser. Avancerad anpassning (längd, symboler, siffror, versaler). Omedelbar kopiering. Intelligent flerspråkigt gränssnitt.
// @description:da Generator til robuste og tilfældige adgangskoder, let og tilgængelig på alle websites. Avanceret tilpasning (længde, symboler, tal, store bogstaver). Øjeblikkelig kopiering. Intelligent flersproget interface.
// @description:fi Vahvojen ja satunnaisten salasanojen generaattori, kevyt ja käytettävissä kaikilla verkkosivuilla. Edistynyt mukautus (pituus, symbolit, numerot, isot kirjaimet). Välitön kopiointi. Älykäs monikielinen käyttöliittymä.
// @description:no Generator for robuste og tilfeldige passord, lett og tilgjengelig på alle nettsteder. Avansert tilpasning (lengde, symboler, tall, store bokstaver). Umiddelbar kopiering. Intelligent flerspråklig grensesnitt.
// @description:cs Generátor robustních a náhodných hesel, lehký a dostupný na všech webech. Pokročilé přizpůsobení (délka, symboly, čísla, velká písmena). Okamžité kopírování. Inteligentní vícejazyčné rozhraní.
// @description:sk Generátor robustných a náhodných hesiel, ľahký a dostupný na každej stránke. Pokročilé prispôsobenie (dĺžka, symboly, čísla, veľké písmená). Okamžité kopírovanie. Inteligentné viacjazyčné rozhranie.
// @description:hu Erős és véletlenszerű jelszógenerátor, könnyű és minden weboldalon elérhető. Fejlett testreszabás (hossz, szimbólumok, számok, nagybetűk). Azonnali másolás. Intelligens többnyelvű felület.
// @description:ro Generator de parole robuste și aleatorii, ușor și disponibil pe orice site. Personalizare avansată (lungime, simboluri, numere, majuscule). Copiere instantanee. Interfață multilingvă inteligentă.
// @description:bg Генератор на надеждни и случайни пароли, лек и достъпен за всеки сайт. Разширена персонализация (дължина, символи, цифри, главни букви). Незабавно копиране. Интелигентен многоезичен интерфейс.
// @description:uk Генератор надійних і випадкових паролів, легкий і доступний на будь-якому сайті. Розширене налаштування (довжина, символи, цифри, великі літери). Миттєве копіювання. Інтелектуальний багатомовний інтерфейс.
// @description:el Δημιουργός ισχυρών και τυχαίων κωδικών, ελαφρύς και διαθέσιμος σε κάθε ιστότοπο. Προηγμένη παραμετροποίηση (μήκος, σύμβολα, αριθμοί, κεφαλαία). Άμεση αντιγραφή. Έξυπνη πολύγλωσση διεπαφή.
// @description:he מחולל סיסמאות חזקות ואקראיות, קל וזמין בכל אתר. התאמה מתקדמת (אורך, סמלים, מספרים, אותיות גדולות). העתקה מיידית. ממשק רב-לשוני חכם.
// @description:id Generator kata sandi kuat dan acak, ringan dan tersedia di semua situs web. Kustomisasi lanjutan (panjang, simbol, angka, huruf besar). Salin instan. Antarmuka multibahasa cerdas.
// @description:ms Penjana kata laluan kukuh dan rawak, ringan dan tersedia di semua laman web. Penyesuaian lanjutan (panjang, simbol, nombor, huruf besar). Salin serta-merta. Antara muka berbilang bahasa pintar.
// @description:th ตัวสร้างรหัสผ่านที่แข็งแกร่งและสุ่ม น้ำหนักเบาและใช้งานได้กับทุกเว็บไซต์ ปรับแต่งขั้นสูง (ความยาว สัญลักษณ์ ตัวเลข ตัวพิมพ์ใหญ่) คัดลอกทันที อินเทอร์เฟซหลายภาษาอัจฉริยะ
// @description:vi Trình tạo mật khẩu mạnh và ngẫu nhiên, nhẹ và có sẵn trên mọi trang web. Tùy chỉnh nâng cao (độ dài, ký hiệu, số, chữ hoa). Sao chép tức thì. Giao diện đa ngôn ngữ thông minh.
// @description:bn শক্তিশালী ও এলোমেলো পাসওয়ার্ড জেনারেটর, হালকা এবং সব ওয়েবসাইটে উপলব্ধ। উন্নত কাস্টমাইজেশন (দৈর্ঘ্য, চিহ্ন, সংখ্যা, বড় অক্ষর)। তাত্ক্ষণিক কপি। বুদ্ধিমান বহুভাষিক ইন্টারফেস।
// @description:fa تولیدکننده رمز عبور قوی و تصادفی، سبک و در دسترس برای همه وبسایتها. سفارشیسازی پیشرفته (طول، نمادها، اعداد، حروف بزرگ). کپی فوری. رابط چندزبانه هوشمند.
// @description:ta வலுவான மற்றும் சீரற்ற கடவுச்சொல் உருவாக்கி, இலகுவானது மற்றும் அனைத்து இணையதளங்களிலும் கிடைக்கும். மேம்பட்ட தனிப்பயனாக்கம் (நீளம், குறியீடுகள், எண்கள், பெரிய எழுத்துகள்). உடனடி நகல். புத்திசாலி பலமொழி இடைமுகம்.
// @description:ml ശക്തവും യാദൃശ്ചികവുമായ പാസ്വേഡ് ജനറേറ്റർ, ലഘുവും എല്ലാ വെബ്സൈറ്റുകളിലും ലഭ്യവും. പുരോഗമിച്ച ഇഷ്ടാനുസൃതമാക്കൽ (നീളം, ചിഹ്നങ്ങൾ, അക്കങ്ങൾ, വലിയ അക്ഷരങ്ങൾ). ഉടൻ പകർത്തൽ. ബഹുഭാഷാ ബുദ്ധിമാനായ ഇന്റർഫേസ്.
// @description:mr मजबूत आणि यादृच्छिक पासवर्ड जनरेटर, हलका आणि सर्व वेबसाइटवर उपलब्ध. प्रगत सानुकूलन (लांबी, चिन्हे, संख्या, मोठी अक्षरे). त्वरित कॉपी. बुद्धिमान बहुभाषिक इंटरफेस.
// @description:ur مضبوط اور بے ترتیب پاس ورڈ جنریٹر، ہلکا اور تمام ویب سائٹس پر دستیاب۔ جدید حسب ضرورت ترتیبات (لمبائی، علامات، اعداد، بڑے حروف)۔ فوری کاپی۔ ذہین کثیر لسانی انٹرفیس۔
// @description:pa ਮਜ਼ਬੂਤ ਅਤੇ ਬੇਤਰਤੀਬ ਪਾਸਵਰਡ ਜਨਰੇਟਰ, ਹਲਕਾ ਅਤੇ ਸਭ ਵੈਬਸਾਈਟਾਂ 'ਤੇ ਉਪਲਬਧ। ਉੱਚ ਪੱਧਰੀ ਕਸਟਮਾਈਜ਼ੇਸ਼ਨ (ਲੰਬਾਈ, ਚਿੰਨ੍ਹ, ਅੰਕ, ਵੱਡੇ ਅੱਖਰ)। ਤੁਰੰਤ ਕਾਪੀ। ਸਮਝਦਾਰ ਬਹੁਭਾਸ਼ੀ ਇੰਟਰਫੇਸ।
// @description:gu મજબૂત અને રેન્ડમ પાસવર્ડ જનરેટર, હલકો અને તમામ વેબસાઇટ પર ઉપલબ્ધ. અદ્યતન કસ્ટમાઇઝેશન (લંબાઈ, ચિહ્નો, આંકડા, મોટા અક્ષરો). તરત નકલ. બુદ્ધિશાળી બહુભાષી ઇન્ટરફેસ.
// @description:kn ಬಲವಾದ ಮತ್ತು ಯಾದೃಚ್ಛಿಕ ಪಾಸ್ವರ್ಡ್ ಜನರೇಟರ್, ಲಘು ಮತ್ತು ಎಲ್ಲಾ ವೆಬ್ಸೈಟ್ಗಳಲ್ಲಿ ಲಭ್ಯ. ಉನ್ನತ ಕಸ್ಟಮೈಸ್ (ಉದ್ದ, ಚಿಹ್ನೆಗಳು, ಸಂಖ್ಯೆಗಳು, ದೊಡ್ಡ ಅಕ್ಷರಗಳು). ತಕ್ಷಣದ ನಕಲು. ಬುದ್ಧಿವಂತ ಬಹುಭಾಷಾ ಇಂಟರ್ಫೇಸ್.
// @description:te బలమైన మరియు యాదృచ్ఛిక పాస్వర్డ్ జనరేటర్, తేలికైనది మరియు అన్ని వెబ్సైట్లలో అందుబాటులో ఉంటుంది. అధునాతన అనుకూలీకరణ (పొడవు, చిహ్నాలు, సంఖ్యలు, పెద్ద అక్షరాలు). తక్షణ కాపీ. తెలివైన బహుభాషా ఇంటర్ఫేస్.
// @description:si ශක්තිමත් සහ අහඹු මුරපද ජනකය, සැහැල්ලු සහ සියලු වෙබ් අඩවි සඳහා ලබා ගත හැක. උසස් අභිරුචිකරණය (දිග, සංකේත, අංක, මහත් අක්ෂර). ක්ෂණික පිටපත් කිරීම. බුද්ධිමත් බහුභාෂා අතුරුමුහුණත.
// @description:km ម៉ាស៊ីនបង្កើតពាក្យសម្ងាត់រឹងមាំ និងចៃដន្យ ទំងន់ស្រាល និងអាចប្រើបានលើគេហទំព័រទាំងអស់។ ការកំណត់ផ្ទាល់ខ្លួនកម្រិតខ្ពស់ (ប្រវែង សញ្ញា លេខ អក្សរធំ)។ ចម្លងភ្លាមៗ។ ចំណុចប្រទាក់ពហុភាសាឆ្លាតវៃ។
// @description:lo ຕົວສ້າງລະຫັດຜ່ານແຂງແຮງ ແລະ ສຸ່ມ, ເບົາ ແລະ ພ້ອມໃຊ້ໃນທຸກເວັບໄຊ. ການປັບແຕ່ງຂັ້ນສູງ (ຄວາມຍາວ, ສັນຍາລັກ, ຕົວເລກ, ຕົວພິມໃຫຍ່). ຄັດລອກທັນທີ. ອິນເຕີເຟດຫຼາຍພາສາອັດສະຫຼາດ.
// @description:my ခိုင်မာပြီး ကျပန်း စကားဝှက် ဖန်တီးသူ, ပေါ့ပါးပြီး ဝဘ်ဆိုဒ်အားလုံးတွင် အသုံးပြုနိုင်သည်။ အဆင့်မြင့် စိတ်ကြိုက်ပြင်ဆင်မှု (အရှည်၊ သင်္ကေတများ၊ နံပါတ်များ၊ အက္ခရာအကြီးများ)။ ချက်ချင်း ကူးယူမှု။ ဘာသာစကားမျိုးစုံ အင်တာဖေ့စ်။
// @description:ne बलियो र यादृच्छिक पासवर्ड जनरेटर, हल्का र सबै वेबसाइटहरूमा उपलब्ध। उन्नत अनुकूलन (लम्बाइ, प्रतीक, संख्या, ठूला अक्षरहरू)। तुरुन्त कपी। बुद्धिमान बहुभाषिक इन्टरफेस।
// @description:am ጠንካራ እና ድንገተኛ የይለፍ ቃል ፈጣሪ, ቀላል እና በሁሉም ድህረ ገጾች ላይ የሚገኝ። ከፍተኛ ማስተካከያ (ርዝመት፣ ምልክቶች፣ ቁጥሮች፣ አቢይ ፊደላት)። ፈጣን ቅጂ። ብዙ ቋንቋ ያለው አስተዋይ በይነገጽ።
// @description:sw Jenereta ya nenosiri imara na ya nasibu, nyepesi na inapatikana kwenye tovuti zote. Ubinafsishaji wa hali ya juu (urefu, alama, nambari, herufi kubwa). Nakala ya papo hapo. Kiolesura cha lugha nyingi chenye akili.
// @description:af Robuuste en ewekansige wagwoordgenerator, liggewig en beskikbaar op alle webwerwe. Gevorderde aanpassing (lengte, simbole, syfers, hoofletters). Onmiddellike kopie. Intelligente meertalige koppelvlak.
// @description:et Tugevate ja juhuslike paroolide generaator, kerge ja saadaval kõigil veebisaitidel. Täiustatud kohandamine (pikkus, sümbolid, numbrid, suurtähed). Kohene kopeerimine. Nutikas mitmekeelne liides.
// @description:lv Izturīgu un nejaušu paroļu ģenerators, viegls un pieejams jebkurā vietnē. Paplašināta pielāgošana (garums, simboli, cipari, lielie burti). Tūlītēja kopēšana. Vieda daudzvalodu saskarne.
// @description:lt Tvirtų ir atsitiktinių slaptažodžių generatorius, lengvas ir prieinamas visose svetainėse. Išplėstinis pritaikymas (ilgis, simboliai, skaičiai, didžiosios raidės). Momentinis kopijavimas. Išmani daugiakalbė sąsaja.
// @description:is Öflugur og tilviljunarkenndur lykilorðagenerator, léttur og aðgengilegur á öllum vefsíðum. Ítarleg sérsnið (lengd, tákn, tölur, hástafir). Tafarlaus afritun. Snjallt fjöltyngt viðmót.
// @description:ga Gineadóir pasfhocal láidir agus randamach, éadrom agus ar fáil ar gach suíomh gréasáin. Saincheapadh chun cinn (fad, siombailí, uimhreacha, ceannlitreacha). Cóipeáil láithreach. Comhéadan ilteangach cliste.
// @description:mt Ġeneratur ta' passwords robusti u każwali, ħafif u disponibbli fuq kull sit. Personalizzazzjoni avvanzata (tul, simboli, numri, ittri kbar). Kopja immedjata. Interfaċċa multilingwi intelliġenti.
// @namespace http://tampermonkey.net/
// @version 1.0.2
// @author Dℝ∃wX
// @copyright 2025-2026 DℝᴇwX
// @license Apache-2.0
// @match *://*/*
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @grant GM_setClipboard
// @noframes
// @tag password
// @tag secure password
// @tag generator
// @tag security
// @tag clipboard
// @tag productivity
// @tag customization
// @tag privacy
// ==/UserScript==
/*
Copyright 2025-2026 Dℝ∃wX
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
https://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
(function () {
'use strict';
const translations = {
en: {
generate: 'Generate',
generateFull: 'Generate password',
settingsMenu: 'Password Generator Settings',
settingsTitle: 'Settings',
sectionPassword: 'Password',
sectionDisplay: 'Display',
showBubble: 'Show bubble',
hidePasswordSetting: 'Hide password',
length: 'Length',
uppercase: 'Uppercase',
numbers: 'Numbers',
symbols: 'Symbols',
copied: 'Password copied',
reveal: 'Show password',
hide: 'Hide password',
fill: 'Generate & fill password',
close: '✕'
},
fr: {
generate: 'Générer',
generateFull: 'Générer un mot de passe',
settingsMenu: 'Paramètres du générateur de mot de passe',
settingsTitle: 'Paramètres',
sectionPassword: 'Mot de passe',
sectionDisplay: 'Affichage',
showBubble: 'Afficher la bulle',
hidePasswordSetting: 'Cacher le mot de passe',
length: 'Longueur',
uppercase: 'Majuscules',
numbers: 'Chiffres',
symbols: 'Symboles',
copied: 'Mot de passe copié',
reveal: 'Afficher le mot de passe',
hide: 'Masquer le mot de passe',
fill: 'Générer et remplir le mot de passe',
close: '✕'
},
es: {
generate: 'Generar',
generateFull: 'Generar contraseña',
settingsMenu: 'Configuración del generador de contraseñas',
settingsTitle: 'Configuración',
sectionPassword: 'Contraseña',
sectionDisplay: 'Visualización',
showBubble: 'Mostrar burbuja',
hidePasswordSetting: 'Ocultar contraseña',
length: 'Longitud',
uppercase: 'Mayúsculas',
numbers: 'Números',
symbols: 'Símbolos',
copied: 'Contraseña copiada',
reveal: 'Mostrar contraseña',
hide: 'Ocultar contraseña',
fill: 'Generar y completar la contraseña',
close: '✕'
},
de: {
generate: 'Generieren',
generateFull: 'Passwort generieren',
settingsMenu: 'Einstellungen des Passwortgenerators',
settingsTitle: 'Einstellungen',
sectionPassword: 'Passwort',
sectionDisplay: 'Anzeige',
showBubble: 'Blase anzeigen',
hidePasswordSetting: 'Passwort verbergen',
length: 'Länge',
uppercase: 'Großbuchstaben',
numbers: 'Zahlen',
symbols: 'Symbole',
copied: 'Passwort kopiert',
reveal: 'Passwort anzeigen',
hide: 'Passwort verbergen',
fill: 'Passwort generieren und ausfüllen',
close: '✕'
},
it: {
generate: 'Genera',
generateFull: 'Genera password',
settingsMenu: 'Impostazioni del generatore di password',
settingsTitle: 'Impostazioni',
sectionPassword: 'Password',
sectionDisplay: 'Visualizzazione',
showBubble: 'Mostra bolla',
hidePasswordSetting: 'Nascondi password',
length: 'Lunghezza',
uppercase: 'Maiuscole',
numbers: 'Numeri',
symbols: 'Simboli',
copied: 'Password copiata:',
reveal: 'Mostra password',
hide: 'Nascondi password',
fill: 'Genera e compila la password',
close: '✕'
},
pt: {
generate: 'Gerar',
generateFull: 'Gerar senha',
settingsMenu: 'Configurações do gerador de senha',
settingsTitle: 'Configurações',
sectionPassword: 'Senha',
sectionDisplay: 'Exibição',
showBubble: 'Mostrar bolha',
hidePasswordSetting: 'Ocultar senha',
length: 'Comprimento',
uppercase: 'Maiúsculas',
numbers: 'Números',
symbols: 'Símbolos',
copied: 'Senha copiada:',
reveal: 'Mostrar senha',
hide: 'Ocultar senha',
fill: 'Gerar e preencher senha',
close: '✕'
},
nl: {
generate: 'Genereren',
generateFull: 'Wachtwoord genereren',
settingsMenu: 'Instellingen van wachtwoordgenerator',
settingsTitle: 'Instellingen',
sectionPassword: 'Wachtwoord',
sectionDisplay: 'Weergave',
showBubble: 'Ballon tonen',
hidePasswordSetting: 'Wachtwoord verbergen',
length: 'Lengte',
uppercase: 'Hoofdletters',
numbers: 'Cijfers',
symbols: 'Symbolen',
copied: 'Wachtwoord gekopieerd:',
reveal: 'Wachtwoord tonen',
hide: 'Wachtwoord verbergen',
fill: 'Wachtwoord genereren en invullen',
close: '✕'
},
ru: {
generate: 'Сгенерировать',
generateFull: 'Создать пароль',
settingsMenu: 'Настройки генератора паролей',
settingsTitle: 'Настройки',
sectionPassword: 'Пароль',
sectionDisplay: 'Отображение',
showBubble: 'Показать пузырь',
hidePasswordSetting: 'Скрыть пароль',
length: 'Длина',
uppercase: 'Заглавные буквы',
numbers: 'Цифры',
symbols: 'Символы',
copied: 'Пароль скопирован:',
reveal: 'Показать пароль',
hide: 'Скрыть пароль',
fill: 'Создать и заполнить пароль',
close: '✕'
},
ja: {
generate: '生成',
generateFull: 'パスワードを生成',
settingsMenu: 'パスワード生成設定',
settingsTitle: '設定',
sectionPassword: 'パスワード',
sectionDisplay: '表示',
showBubble: 'バブルを表示',
hidePasswordSetting: 'パスワードを非表示',
length: '長さ',
uppercase: '大文字',
numbers: '数字',
symbols: '記号',
copied: 'パスワードをコピーしました:',
reveal: 'パスワードを表示',
hide: 'パスワードを非表示',
fill: 'パスワードを生成して入力',
close: '✕'
},
ko: {
generate: '생성',
generateFull: '비밀번호 생성',
settingsMenu: '비밀번호 생성기 설정',
settingsTitle: '설정',
sectionPassword: '비밀번호',
sectionDisplay: '표시',
showBubble: '버블 표시',
hidePasswordSetting: '비밀번호 숨기기',
length: '길이',
uppercase: '대문자',
numbers: '숫자',
symbols: '기호',
copied: '비밀번호가 복사되었습니다:',
reveal: '비밀번호 표시',
hide: '비밀번호 숨기기',
fill: '비밀번호 생성 및 입력',
close: '✕'
},
zh: {
generate: '生成',
generateFull: '生成密码',
settingsMenu: '密码生成器设置',
settingsTitle: '设置',
sectionPassword: '密码',
sectionDisplay: '显示',
showBubble: '显示气泡',
hidePasswordSetting: '隐藏密码',
length: '长度',
uppercase: '大写字母',
numbers: '数字',
symbols: '符号',
copied: '密码已复制:',
reveal: '显示密码',
hide: '隐藏密码',
fill: '生成并填写密码',
close: '✕'
},
ar: {
generate: 'توليد',
generateFull: 'توليد كلمة المرور',
settingsMenu: 'إعدادات مولد كلمة المرور',
settingsTitle: 'الإعدادات',
sectionPassword: 'كلمة المرور',
sectionDisplay: 'العرض',
showBubble: 'إظهار الفقاعة',
hidePasswordSetting: 'إخفاء كلمة المرور',
length: 'الطول',
uppercase: 'أحرف كبيرة',
numbers: 'أرقام',
symbols: 'رموز',
copied: 'تم نسخ كلمة المرور:',
reveal: 'إظهار كلمة المرور',
hide: 'إخفاء كلمة المرور',
fill: 'إنشاء وملء كلمة المرور',
close: '✕'
},
pl: {
generate: 'Generuj',
generateFull: 'Wygeneruj hasło',
settingsMenu: 'Ustawienia generatora haseł',
settingsTitle: 'Ustawienia',
sectionPassword: 'Hasło',
sectionDisplay: 'Wyświetlanie',
showBubble: 'Pokaż dymek',
hidePasswordSetting: 'Ukryj hasło',
length: 'Długość',
uppercase: 'Wielkie litery',
numbers: 'Cyfry',
symbols: 'Symbole',
copied: 'Hasło skopiowane:',
reveal: 'Pokaż hasło',
hide: 'Ukryj hasło',
fill: 'Wygeneruj i wypełnij hasło',
close: '✕'
},
tr: {
generate: 'Oluştur',
generateFull: 'Şifre oluştur',
settingsMenu: 'Şifre oluşturucu ayarları',
settingsTitle: 'Ayarlar',
sectionPassword: 'Şifre',
sectionDisplay: 'Görünüm',
showBubble: 'Balonu göster',
hidePasswordSetting: 'Şifreyi gizle',
length: 'Uzunluk',
uppercase: 'Büyük harfler',
numbers: 'Rakamlar',
symbols: 'Semboller',
copied: 'Şifre kopyalandı:',
reveal: 'Şifreyi göster',
hide: 'Şifreyi gizle',
fill: 'Şifre oluştur ve doldur',
close: '✕'
},
sv: {
generate: 'Generera',
generateFull: 'Generera lösenord',
settingsMenu: 'Inställningar för lösenordsgenerator',
settingsTitle: 'Inställningar',
sectionPassword: 'Lösenord',
sectionDisplay: 'Visning',
showBubble: 'Visa bubbla',
hidePasswordSetting: 'Dölj lösenord',
length: 'Längd',
uppercase: 'Versaler',
numbers: 'Siffror',
symbols: 'Symboler',
copied: 'Lösenord kopierat:',
reveal: 'Visa lösenord',
hide: 'Dölj lösenord',
fill: 'Generera och fyll i lösenord',
close: '✕'
},
ro: {
generate: 'Generează',
generateFull: 'Generează parolă',
settingsMenu: 'Setări generator parolă',
settingsTitle: 'Setări',
sectionPassword: 'Parolă',
sectionDisplay: 'Afișare',
showBubble: 'Afișează bula',
hidePasswordSetting: 'Ascunde parola',
length: 'Lungime',
uppercase: 'Majuscule',
numbers: 'Cifre',
symbols: 'Simboluri',
copied: 'Parola a fost copiată:',
reveal: 'Afișează parola',
hide: 'Ascunde parola',
fill: 'Generează și completează parola',
close: '✕'
},
vi: {
generate: 'Tạo',
generateFull: 'Tạo mật khẩu',
settingsMenu: 'Cài đặt trình tạo mật khẩu',
settingsTitle: 'Cài đặt',
sectionPassword: 'Mật khẩu',
sectionDisplay: 'Hiển thị',
showBubble: 'Hiển thị bong bóng',
hidePasswordSetting: 'Ẩn mật khẩu',
length: 'Độ dài',
uppercase: 'Chữ hoa',
numbers: 'Số',
symbols: 'Ký hiệu',
copied: 'Mật khẩu đã được sao chép:',
reveal: 'Hiển thị mật khẩu',
hide: 'Ẩn mật khẩu',
fill: 'Tạo và điền mật khẩu',
close: '✕'
},
hi: {
generate: 'जनरेट करें',
generateFull: 'पासवर्ड जनरेट करें',
settingsMenu: 'पासवर्ड जनरेटर सेटिंग्स',
settingsTitle: 'सेटिंग्स',
sectionPassword: 'पासवर्ड',
sectionDisplay: 'प्रदर्शन',
showBubble: 'बबल दिखाएँ',
hidePasswordSetting: 'पासवर्ड छिपाएँ',
length: 'लंबाई',
uppercase: 'बड़े अक्षर',
numbers: 'संख्याएँ',
symbols: 'प्रतीक',
copied: 'पासवर्ड कॉपी किया गया:',
reveal: 'पासवर्ड दिखाएँ',
hide: 'पासवर्ड छिपाएँ',
fill: 'पासवर्ड जनरेट करें और भरें',
close: '✕'
},
th: {
generate: 'สร้าง',
generateFull: 'สร้างรหัสผ่าน',
settingsMenu: 'การตั้งค่าตัวสร้างรหัสผ่าน',
settingsTitle: 'การตั้งค่า',
showBubble: 'แสดงบับเบิล',
hidePasswordSetting: 'ซ่อนรหัสผ่าน',
length: 'ความยาว',
uppercase: 'ตัวพิมพ์ใหญ่',
numbers: 'ตัวเลข',
symbols: 'สัญลักษณ์',
copied: 'คัดลอกรหัสผ่านแล้ว:',
reveal: 'แสดงรหัสผ่าน',
hide: 'ซ่อนรหัสผ่าน',
fill: 'สร้างและกรอกรหัสผ่าน',
close: '✕'
},
zh_HK: {
generate: '產生',
generateFull: '產生密碼',
settingsMenu: '密碼產生器設定',
settingsTitle: '設定',
sectionPassword: '密碼',
sectionDisplay: '顯示',
showBubble: '顯示氣泡',
hidePasswordSetting: '隱藏密碼',
length: '長度',
uppercase: '大寫字母',
numbers: '數字',
symbols: '符號',
copied: '密碼已複製:',
reveal: '顯示密碼',
hide: '隱藏密碼',
fill: '產生並填入密碼',
close: '✕'
},
zh_TW: {
generate: '產生',
generateFull: '產生密碼',
settingsMenu: '密碼產生器設定',
settingsTitle: '設定',
sectionPassword: '密碼',
sectionDisplay: '顯示',
showBubble: '顯示氣泡',
hidePasswordSetting: '隱藏密碼',
length: '長度',
uppercase: '大寫字母',
numbers: '數字',
symbols: '符號',
copied: '密碼已複製:',
reveal: '顯示密碼',
hide: '隱藏密碼',
fill: '產生並填入密碼',
close: '✕'
},
ms: {
generate: 'Jana',
generateFull: 'Jana kata laluan',
settingsMenu: 'Tetapan penjana kata laluan',
settingsTitle: 'Tetapan',
sectionPassword: 'Kata laluan',
sectionDisplay: 'Paparan',
showBubble: 'Tunjukkan gelembung',
hidePasswordSetting: 'Sembunyikan kata laluan',
length: 'Panjang',
uppercase: 'Huruf besar',
numbers: 'Nombor',
symbols: 'Simbol',
copied: 'Kata laluan disalin:',
reveal: 'Tunjukkan kata laluan',
hide: 'Sembunyikan kata laluan',
fill: 'Jana dan isi kata laluan',
close: '✕'
},
id: {
generate: 'Buat',
generateFull: 'Buat kata sandi',
settingsMenu: 'Pengaturan pembuat kata sandi',
settingsTitle: 'Pengaturan',
sectionPassword: 'Kata sandi',
sectionDisplay: 'Tampilan',
showBubble: 'Tampilkan gelembung',
hidePasswordSetting: 'Sembunyikan kata sandi',
length: 'Panjang',
uppercase: 'Huruf besar',
numbers: 'Angka',
symbols: 'Simbol',
copied: 'Kata sandi disalin:',
reveal: 'Tampilkan kata sandi',
hide: 'Sembunyikan kata sandi',
fill: 'Buat dan isi kata sandi',
close: '✕'
},
bn: {
generate: 'তৈরি করুন',
generateFull: 'পাসওয়ার্ড তৈরি করুন',
settingsMenu: 'পাসওয়ার্ড জেনারেটর সেটিংস',
settingsTitle: 'সেটিংস',
sectionPassword: 'পাসওয়ার্ড',
sectionDisplay: 'প্রদর্শন',
showBubble: 'বাবল দেখান',
hidePasswordSetting: 'পাসওয়ার্ড লুকান',
length: 'দৈর্ঘ্য',
uppercase: 'বড় হাতের অক্ষর',
numbers: 'সংখ্যা',
symbols: 'প্রতীক',
copied: 'পাসওয়ার্ড কপি হয়েছে:',
reveal: 'পাসওয়ার্ড দেখান',
hide: 'পাসওয়ার্ড লুকান',
fill: 'পাসওয়ার্ড তৈরি ও পূরণ করুন',
close: '✕'
},
ta: {
generate: 'உருவாக்கு',
generateFull: 'கடவுச்சொல்லை உருவாக்கு',
settingsMenu: 'கடவுச்சொல் உருவாக்கி அமைப்புகள்',
settingsTitle: 'அமைப்புகள்',
sectionPassword: 'கடவுச்சொல்',
sectionDisplay: 'காட்சி',
showBubble: 'பபிள் காண்பி',
hidePasswordSetting: 'கடவுச்சொல்லை மறை',
length: 'நீளம்',
uppercase: 'பெரிய எழுத்துக்கள்',
numbers: 'எண்கள்',
symbols: 'சின்னங்கள்',
copied: 'கடவுச்சொல் நகலெடுக்கப்பட்டது:',
reveal: 'கடவுச்சொல்லைக் காண்பி',
hide: 'கடவுச்சொல்லை மறை',
fill: 'கடவுச்சொல்லை உருவாக்கி நிரப்புக',
close: '✕'
},
ur: {
generate: 'پیدا کریں',
generateFull: 'پاس ورڈ بنائیں',
settingsMenu: 'پاس ورڈ جنریٹر کی ترتیبات',
settingsTitle: 'ترتیبات',
sectionPassword: 'پاس ورڈ',
sectionDisplay: 'ڈسپلے',
showBubble: 'بلبلہ دکھائیں',
hidePasswordSetting: 'پاس ورڈ چھپائیں',
length: 'لمبائی',
uppercase: 'بڑے حروف',
numbers: 'اعداد',
symbols: 'علامات',
copied: 'پاس ورڈ کاپی ہو گیا:',
reveal: 'پاس ورڈ دکھائیں',
hide: 'پاس ورڈ چھپائیں',
fill: 'پاس ورڈ بنائیں اور بھریں',
close: '✕'
},
ml: {
generate: 'സൃഷ്ടിക്കുക',
generateFull: 'പാസ്വേഡ് സൃഷ്ടിക്കുക',
settingsMenu: 'പാസ്വേഡ് ജനറേറ്റർ ക്രമീകരണങ്ങൾ',
settingsTitle: 'ക്രമീകരണങ്ങൾ',
sectionPassword: 'പാസ്വേഡ്',
sectionDisplay: 'പ്രദർശനം',
showBubble: 'ബബിൾ കാണിക്കുക',
hidePasswordSetting: 'പാസ്വേഡ് മറയ്ക്കുക',
length: 'നീളം',
uppercase: 'വലിയ അക്ഷരങ്ങൾ',
numbers: 'അക്കങ്ങൾ',
symbols: 'ചിഹ്നങ്ങൾ',
copied: 'പാസ്വേഡ് പകർത്തി:',
reveal: 'പാസ്വേഡ് കാണിക്കുക',
hide: 'പാസ്വേഡ് മറയ്ക്കുക',
fill: 'പാസ്വേഡ് സൃഷ്ടിച്ച് പൂരിപ്പിക്കുക',
close: '✕'
},
mr: {
generate: 'तयार करा',
generateFull: 'पासवर्ड तयार करा',
settingsMenu: 'पासवर्ड जनरेटर सेटिंग्ज',
settingsTitle: 'सेटिंग्ज',
sectionPassword: 'पासवर्ड',
sectionDisplay: 'प्रदर्शन',
showBubble: 'बबल दाखवा',
hidePasswordSetting: 'पासवर्ड लपवा',
length: 'लांबी',
uppercase: 'मोठी अक्षरे',
numbers: 'संख्या',
symbols: 'चिन्हे',
copied: 'पासवर्ड कॉपी झाला:',
reveal: 'पासवर्ड दाखवा',
hide: 'पासवर्ड लपवा',
fill: 'पासवर्ड तयार करा आणि भरा',
close: '✕'
},
pa: {
generate: 'ਤਿਆਰ ਕਰੋ',
generateFull: 'ਪਾਸਵਰਡ ਤਿਆਰ ਕਰੋ',
settingsMenu: 'ਪਾਸਵਰਡ ਜਨਰੇਟਰ ਸੈਟਿੰਗਜ਼',
settingsTitle: 'ਸੈਟਿੰਗਜ਼',
sectionPassword: 'ਪਾਸਵਰਡ',
sectionDisplay: 'ਦਿਖਾਵਟ',
showBubble: 'ਬਬਲ ਦਿਖਾਓ',
hidePasswordSetting: 'ਪਾਸਵਰਡ ਲੁਕਾਓ',
length: 'ਲੰਬਾਈ',
uppercase: 'ਵੱਡੇ ਅੱਖਰ',
numbers: 'ਅੰਕ',
symbols: 'ਚਿੰਨ੍ਹ',
copied: 'ਪਾਸਵਰਡ ਕਾਪੀ ਹੋ ਗਿਆ:',
reveal: 'ਪਾਸਵਰਡ ਦਿਖਾਓ',
hide: 'ਪਾਸਵਰਡ ਲੁਕਾਓ',
fill: 'ਪਾਸਵਰਡ ਤਿਆਰ ਕਰੋ ਅਤੇ ਭਰੋ',
close: '✕'
},
gu: {
generate: 'બનાવો',
generateFull: 'પાસવર્ડ બનાવો',
settingsMenu: 'પાસવર્ડ જનરેટર સેટિંગ્સ',
settingsTitle: 'સેટિંગ્સ',
sectionPassword: 'પાસવર્ડ',
sectionDisplay: 'પ્રદર્શન',
showBubble: 'બબલ બતાવો',
hidePasswordSetting: 'પાસવર્ડ છુપાવો',
length: 'લંબાઈ',
uppercase: 'મોટા અક્ષરો',
numbers: 'આંકડા',
symbols: 'ચિહ્નો',
copied: 'પાસવર્ડ નકલ થયો:',
reveal: 'પાસવર્ડ બતાવો',
hide: 'પાસવર્ડ છુપાવો',
fill: 'પાસવર્ડ બનાવો અને ભરો',
close: '✕'
},
kn: {
generate: 'ರಚಿಸಿ',
generateFull: 'ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ',
settingsMenu: 'ಪಾಸ್ವರ್ಡ್ ಜನರೇಟರ್ ಸೆಟ್ಟಿಂಗ್ಗಳು',
settingsTitle: 'ಸೆಟ್ಟಿಂಗ್ಗಳು',
sectionPassword: 'ಪಾಸ್ವರ್ಡ್',
sectionDisplay: 'ಪ್ರದರ್ಶನ',
showBubble: 'ಬಬಲ್ ತೋರಿಸಿ',
hidePasswordSetting: 'ಪಾಸ್ವರ್ಡ್ ಮರೆಮಾಡಿ',
length: 'ಉದ್ದ',
uppercase: 'ದೊಡ್ಡ ಅಕ್ಷರಗಳು',
numbers: 'ಸಂಖ್ಯೆಗಳು',
symbols: 'ಚಿಹ್ನೆಗಳು',
copied: 'ಪಾಸ್ವರ್ಡ್ ನಕಲಾಯಿತು:',
reveal: 'ಪಾಸ್ವರ್ಡ್ ತೋರಿಸಿ',
hide: 'ಪಾಸ್ವರ್ಡ್ ಮರೆಮಾಡಿ',
fill: 'ಪಾಸ್ವರ್ಡ್ ರಚಿಸಿ ಮತ್ತು ಭರ್ತಿ ಮಾಡಿ',
close: '✕'
},
te: {
generate: 'సృష్టించు',
generateFull: 'పాస్వర్డ్ సృష్టించు',
settingsMenu: 'పాస్వర్డ్ జనరేటర్ సెట్టింగ్స్',
settingsTitle: 'సెట్టింగ్స్',
sectionPassword: 'పాస్వర్డ్',
sectionDisplay: 'ప్రదర్శన',
showBubble: 'బబుల్ చూపించు',
hidePasswordSetting: 'పాస్వర్డ్ దాచు',
length: 'పొడవు',
uppercase: 'పెద్ద అక్షరాలు',
numbers: 'సంఖ్యలు',
symbols: 'చిహ్నాలు',
copied: 'పాస్వర్డ్ కాపీ అయ్యింది:',
reveal: 'పాస్వర్డ్ చూపించు',
hide: 'పాస్వర్డ్ దాచు',
fill: 'పాస్వర్డ్ సృష్టించి నింపండి',
close: '✕'
},
si: {
generate: 'සාදන්න',
generateFull: 'මුරපදය සාදන්න',
settingsMenu: 'මුරපද ජනක සැකසුම්',
settingsTitle: 'සැකසුම්',
sectionPassword: 'මුරපදය',
sectionDisplay: 'ප්රදර්ශනය',
showBubble: 'බබල් පෙන්වන්න',
hidePasswordSetting: 'මුරපදය සඟවන්න',
length: 'දිග',
uppercase: 'විශාල අක්ෂර',
numbers: 'අංක',
symbols: 'සංකේත',
copied: 'මුරපදය පිටපත් කළා:',
reveal: 'මුරපදය පෙන්වන්න',
hide: 'මුරපදය සඟවන්න',
fill: 'මුරපදය සාදමින් පුරවන්න',
close: '✕'
},
km: {
generate: 'បង្កើត',
generateFull: 'បង្កើតពាក្យសម្ងាត់',
settingsMenu: 'ការកំណត់ម៉ាស៊ីនបង្កើតពាក្យសម្ងាត់',
settingsTitle: 'ការកំណត់',
sectionPassword: 'ពាក្យសម្ងាត់',
sectionDisplay: 'ការបង្ហាញ',
showBubble: 'បង្ហាញប៊ូបល',
hidePasswordSetting: 'លាក់ពាក្យសម្ងាត់',
length: 'ប្រវែង',
uppercase: 'អក្សរធំ',
numbers: 'លេខ',
symbols: 'សញ្ញា',
copied: 'បានចម្លងពាក្យសម្ងាត់:',
reveal: 'បង្ហាញពាក្យសម្ងាត់',
hide: 'លាក់ពាក្យសម្ងាត់',
fill: 'បង្កើត និងបំពេញពាក្យសម្ងាត់',
close: '✕'
},
lo: {
generate: 'ສ້າງ',
generateFull: 'ສ້າງລະຫັດຜ່ານ',
settingsMenu: 'ການຕັ້ງຄ່າຕົວສ້າງລະຫັດຜ່ານ',
settingsTitle: 'ການຕັ້ງຄ່າ',
sectionPassword: 'ລະຫັດຜ່ານ',
sectionDisplay: 'ການສະແດງ',
showBubble: 'ສະແດງບັບເບິນ',
hidePasswordSetting: 'ເຊື່ອງລະຫັດຜ່ານ',
length: 'ຄວາມຍາວ',
uppercase: 'ຕົວພິມໃຫຍ່',
numbers: 'ຕົວເລກ',
symbols: 'ສັນຍາລັກ',
copied: 'ຄັດລອກລະຫັດຜ່ານແລ້ວ:',
reveal: 'ສະແດງລະຫັດຜ່ານ',
hide: 'ເຊື່ອງລະຫັດຜ່ານ',
fill: 'ສ້າງ ແລະ ຕື່ມລະຫັດຜ່ານ',
close: '✕'
},
my: {
generate: 'ဖန်တီး',
generateFull: 'စကားဝှက် ဖန်တီးပါ',
settingsMenu: 'စကားဝှက် ဖန်တီးစက် ဆက်တင်များ',
settingsTitle: 'ဆက်တင်များ',
sectionPassword: 'စကားဝှက်',
sectionDisplay: 'ပြသမှု',
showBubble: 'ဘဘလ် ပြပါ',
hidePasswordSetting: 'စကားဝှက် ဝှက်ပါ',
length: 'အရှည်',
uppercase: 'အက္ခရာအကြီး',
numbers: 'နံပါတ်များ',
symbols: 'သင်္ကေတများ',
copied: 'စကားဝှက်ကို ကူးယူပြီးပါပြီ:',
reveal: 'စကားဝှက် ပြပါ',
hide: 'စကားဝှက် ဝှက်ပါ',
fill: 'စကားဝှက် ဖန်တီးပြီး ဖြည့်ပါ',
close: '✕'
},
ne: {
generate: 'सिर्जना गर्नुहोस्',
generateFull: 'पासवर्ड सिर्जना गर्नुहोस्',
settingsMenu: 'पासवर्ड जनरेटर सेटिङहरू',
settingsTitle: 'सेटिङहरू',
sectionPassword: 'पासवर्ड',
sectionDisplay: 'प्रदर्शन',
showBubble: 'बबल देखाउनुहोस्',
hidePasswordSetting: 'पासवर्ड लुकाउनुहोस्',
length: 'लम्बाइ',
uppercase: 'ठूला अक्षर',
numbers: 'संख्या',
symbols: 'प्रतीक',
copied: 'पासवर्ड प्रतिलिपि गरियो:',
reveal: 'पासवर्ड देखाउनुहोस्',
hide: 'पासवर्ड लुकाउनुहोस्',
fill: 'पासवर्ड सिर्जना गरी भर्नुहोस्',
close: '✕'
},
am: {
generate: 'ፍጠር',
generateFull: 'የይለፍ ቃል ፍጠር',
settingsMenu: 'የይለፍ ቃል ፍጠራ ቅንብሮች',
settingsTitle: 'ቅንብሮች',
sectionPassword: 'የይለፍ ቃል',
sectionDisplay: 'ማሳያ',
showBubble: 'አረፋ አሳይ',
hidePasswordSetting: 'የይለፍ ቃል ደብቅ',
length: 'ርዝመት',
uppercase: 'አቢይ ፊደላት',
numbers: 'ቁጥሮች',
symbols: 'ምልክቶች',
copied: 'የይለፍ ቃል ተቀድቷል:',
reveal: 'የይለፍ ቃል አሳይ',
hide: 'የይለፍ ቃል ደብቅ',
fill: 'የይለፍ ቃል ፍጠር እና ሙላ',
close: '✕'
},
sw: {
generate: 'Tengeneza',
generateFull: 'Tengeneza nenosiri',
settingsMenu: 'Mipangilio ya jenereta ya nenosiri',
settingsTitle: 'Mipangilio',
sectionPassword: 'Nenosiri',
sectionDisplay: 'Mwonekano',
showBubble: 'Onyesha kiputo',
hidePasswordSetting: 'Ficha nenosiri',
length: 'Urefu',
uppercase: 'Herufi kubwa',
numbers: 'Nambari',
symbols: 'Alama',
copied: 'Nenosiri limekopiwa:',
reveal: 'Onyesha nenosiri',
hide: 'Ficha nenosiri',
fill: 'Tengeneza na ujaze nenosiri',
close: '✕'
},
af: {
generate: 'Genereer',
generateFull: 'Genereer wagwoord',
settingsMenu: 'Wagwoordgenerator instellings',
settingsTitle: 'Instellings',
sectionPassword: 'Wagwoord',
sectionDisplay: 'Vertoning',
showBubble: 'Wys borrel',
hidePasswordSetting: 'Versteek wagwoord',
length: 'Lengte',
uppercase: 'Hoofletters',
numbers: 'Syfers',
symbols: 'Simbole',
copied: 'Wagwoord gekopieer:',
reveal: 'Wys wagwoord',
hide: 'Versteek wagwoord',
fill: 'Genereer en vul wagwoord in',
close: '✕'
},
et: {
generate: 'Loo',
generateFull: 'Loo parool',
settingsMenu: 'Parooligeneraatori seaded',
settingsTitle: 'Seaded',
sectionPassword: 'Parool',
sectionDisplay: 'Kuva',
showBubble: 'Näita mull',
hidePasswordSetting: 'Peida parool',
length: 'Pikkus',
uppercase: 'Suurtähed',
numbers: 'Numbrid',
symbols: 'Sümbolid',
copied: 'Parool kopeeritud:',
reveal: 'Kuva parool',
hide: 'Peida parool',
fill: 'Loo ja täida parool',
close: '✕'
},
lv: {
generate: 'Ģenerēt',
generateFull: 'Ģenerēt paroli',
settingsMenu: 'Paroļu ģeneratora iestatījumi',
settingsTitle: 'Iestatījumi',
sectionPassword: 'Parole',
sectionDisplay: 'Attēlojums',
showBubble: 'Rādīt burbuli',
hidePasswordSetting: 'Slēpt paroli',
length: 'Garums',
uppercase: 'Lielie burti',
numbers: 'Cipari',
symbols: 'Simboli',
copied: 'Parole nokopēta:',
reveal: 'Rādīt paroli',
hide: 'Slēpt paroli',
fill: 'Ģenerēt un aizpildīt paroli',
close: '✕'
},
lt: {
generate: 'Generuoti',
generateFull: 'Generuoti slaptažodį',
settingsMenu: 'Slaptažodžių generatoriaus nustatymai',
settingsTitle: 'Nustatymai',
sectionPassword: 'Slaptažodis',
sectionDisplay: 'Rodinys',
showBubble: 'Rodyti burbulą',
hidePasswordSetting: 'Slėpti slaptažodį',
length: 'Ilgis',
uppercase: 'Didžiosios raidės',
numbers: 'Skaičiai',
symbols: 'Simboliai',
copied: 'Slaptažodis nukopijuotas:',
reveal: 'Rodyti slaptažodį',
hide: 'Slėpti slaptažodį',
fill: 'Generuoti ir užpildyti slaptažodį',
close: '✕'
},
is: {
generate: 'Búa til',
generateFull: 'Búa til lykilorð',
settingsMenu: 'Stillingar lykilorðagjafa',
settingsTitle: 'Stillingar',
sectionPassword: 'Lykilorð',
sectionDisplay: 'Birting',
showBubble: 'Sýna kúlu',
hidePasswordSetting: 'Fela lykilorð',
length: 'Lengd',
uppercase: 'Hástafir',
numbers: 'Tölur',
symbols: 'Tákn',
copied: 'Lykilorð afritað:',
reveal: 'Sýna lykilorð',
hide: 'Fela lykilorð',
fill: 'Búa til og fylla út lykilorð',
close: '✕'
},
ga: {
generate: 'Gin',
generateFull: 'Gin pasfhocal',
settingsMenu: 'Socruithe gineadóra pasfhocail',
settingsTitle: 'Socruithe',
sectionPassword: 'Pasfhocal',
sectionDisplay: 'Taispeáint',
showBubble: 'Taispeáin boilgeog',
hidePasswordSetting: 'Folaigh pasfhocal',
length: 'Fad',
uppercase: 'Ceannlitreacha',
numbers: 'Uimhreacha',
symbols: 'Siombailí',
copied: 'Pasfhocal cóipeáilte:',
reveal: 'Taispeáin pasfhocal',
hide: 'Folaigh pasfhocal',
fill: 'Gin agus líon pasfhocal',
close: '✕'
},
mt: {
generate: 'Iġġenera',
generateFull: 'Iġġenera password',
settingsMenu: 'Settings tal-ġeneratur tal-password',
settingsTitle: 'Settings',
sectionPassword: 'Password',
sectionDisplay: 'Wiri',
showBubble: 'Uri bużżieqa',
hidePasswordSetting: 'Aħbi password',
length: 'Tul',
uppercase: 'Ittri kbar',
numbers: 'Numri',
symbols: 'Simboli',
copied: 'Password ikkupjata:',
reveal: 'Uri password',
hide: 'Aħbi password',
fill: 'Iġġenera u imla l-password',
close: '✕'
}
};
const userLang = (navigator.language || navigator.userLanguage).slice(0, 2);
const i18n = translations[userLang] || translations.en;
const strings = {
copied: i18n.copied || translations.en.copied,
copyFailed: i18n.copyFailed || 'Copy blocked. Password ready below.',
reveal: i18n.reveal || translations.en.reveal,
hide: i18n.hide || translations.en.hide,
close: i18n.close || translations.en.close,
settingsMenu: i18n.settingsMenu || translations.en.settingsMenu || 'Password Generator Settings',
settingsTitle: i18n.settingsTitle || translations.en.settingsTitle || 'Settings',
sectionPassword: i18n.sectionPassword || translations.en.sectionPassword || 'Password',
sectionDisplay: i18n.sectionDisplay || translations.en.sectionDisplay || 'Display',
showBubble: i18n.showBubble || translations.en.showBubble || 'Show bubble',
hidePasswordSetting: i18n.hidePasswordSetting || translations.en.hidePasswordSetting || 'Hide password',
fill: i18n.fill || translations.en.fill || 'Generate & fill password'
};
const MIN_LENGTH = 4;
const MAX_LENGTH = 50;
const DEFAULT_LENGTH = 12;
function sanitizeLength(rawValue, fallback = DEFAULT_LENGTH) {
const parsed = parseInt(rawValue, 10);
const safeValue = Number.isNaN(parsed) ? fallback : parsed;
return Math.min(Math.max(safeValue, MIN_LENGTH), MAX_LENGTH);
}
function getSecureRandomInt(max) {
if (max <= 0) return 0;
const cryptoObj = (typeof window !== 'undefined') && (window.crypto || window.msCrypto);
if (cryptoObj && typeof cryptoObj.getRandomValues === 'function') {
const array = new Uint32Array(1);
const limit = Math.floor(0x100000000 / max) * max;
let rand;
do {
cryptoObj.getRandomValues(array);
rand = array[0];
} while (rand >= limit);
return rand % max;
}
return Math.floor(Math.random() * max);
}
function pickChar(charset) {
return charset[getSecureRandomInt(charset.length)];
}
function sanitizeBoolean(value, fallback = true) {
if (value === undefined || value === null) return fallback;
return value !== false;
}
const eyeOpenIcon = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M1 12s4-7 11-7 11 7 11 7-4 7-11 7-11-7-11-7z"></path><circle cx="12" cy="12" r="3"></circle></svg>';
const eyeClosedIcon = '<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M17.94 17.94A10.07 10.07 0 0 1 12 20c-7 0-11-8-11-8a18.86 18.86 0 0 1 5.06-5.94"></path><path d="M1 1l22 22"></path><path d="M9.88 9.88a3 3 0 1 0 4.24 4.24"></path></svg>';
const notificationSuccessIcon = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><path d="M20 6L9 17l-5-5"></path></svg>';
const notificationErrorIcon = '<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round"><line x1="12" y1="8" x2="12" y2="13"></line><line x1="12" y1="17" x2="12.01" y2="17"></line><circle cx="12" cy="12" r="10"></circle></svg>';
const storageNamespace = 'spg';
function getSetting(key, fallback) {
try {
if (typeof GM_getValue === 'function') {
const value = GM_getValue(`${storageNamespace}_${key}`, null);
if (value !== null && value !== undefined) return value;
}
} catch (err) {
console.warn('GM_getValue failed', err);
}
try {
const raw = localStorage.getItem(`${storageNamespace}_${key}`);
return raw !== null ? JSON.parse(raw) : fallback;
} catch {
return fallback;
}
}
function setSetting(key, value) {
try {
if (typeof GM_setValue === 'function') {
GM_setValue(`${storageNamespace}_${key}`, value);
}
} catch (err) {
console.warn('GM_setValue failed', err);
}
try {
localStorage.setItem(`${storageNamespace}_${key}`, JSON.stringify(value));
} catch { }
}
function copyTextToClipboard(text) {
if (typeof GM_setClipboard === 'function') {
try {
GM_setClipboard(text, 'text');
return Promise.resolve();
} catch (err) {
console.warn('GM_setClipboard failed', err);
}
}
if (navigator.clipboard?.writeText) {
return navigator.clipboard.writeText(text);
}
return new Promise((resolve, reject) => {
try {
const textarea = document.createElement('textarea');
textarea.value = text;
textarea.setAttribute('readonly', '');
textarea.style.position = 'fixed';
textarea.style.opacity = '0';
textarea.style.pointerEvents = 'none';
textarea.style.top = '-9999px';
document.body.appendChild(textarea);
textarea.focus();
textarea.select();
const copied = document.execCommand('copy');
textarea.remove();
if (!copied) {
reject(new Error('execCommand copy failed'));
return;
}
resolve();
} catch (err) {
reject(err);
}
});
}
let currentPassword = '';
let isPasswordVisible = false;
let notificationAutoHideTimer = null;
function generatePassword(length, includeUppercase, includeNumbers, includeSymbols) {
const lowercase = 'abcdefghijklmnopqrstuvwxyz';
const uppercase = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
const numbers = '0123456789';
const symbols = '?!@#$%^&*()=';
let charset = lowercase;
let password = [];
if (includeUppercase) {
charset += uppercase;
password.push(pickChar(uppercase));
}
if (includeNumbers) {
charset += numbers;
password.push(pickChar(numbers));
}
if (includeSymbols) {
charset += symbols;
password.push(pickChar(symbols));
}
password.push(pickChar(lowercase));
const remainingLength = length - password.length;
for (let i = 0; i < remainingLength; i++) {
password.push(pickChar(charset));
}
for (let i = password.length - 1; i > 0; i--) {
const j = getSecureRandomInt(i + 1);
[password[i], password[j]] = [password[j], password[i]];
}
return password.join('');
}
function maskPassword(pass) {
return pass.replace(/./g, '•');
}
function updateNotificationPassword(notificationPasswordEl, eyeButtonEl) {
if (!currentPassword) {
notificationPasswordEl.textContent = '';
return;
}
if (isPasswordVisible) {
notificationPasswordEl.textContent = currentPassword;
notificationPasswordEl.style.letterSpacing = '0.05em';
eyeButtonEl.innerHTML = eyeOpenIcon;
eyeButtonEl.setAttribute('aria-label', strings.hide);
eyeButtonEl.title = strings.hide;
} else {
notificationPasswordEl.textContent = maskPassword(currentPassword);
notificationPasswordEl.style.letterSpacing = '0.3em';
eyeButtonEl.innerHTML = eyeClosedIcon;
eyeButtonEl.setAttribute('aria-label', strings.reveal);
eyeButtonEl.title = strings.reveal;
}
}
function clearNotificationAutoHide() {
if (!notificationAutoHideTimer) return;
clearTimeout(notificationAutoHideTimer);
notificationAutoHideTimer = null;
}
function scheduleNotificationAutoHide(notificationEl, delay = 6500) {
clearNotificationAutoHide();
notificationAutoHideTimer = setTimeout(() => hideNotification(notificationEl), delay);
}
function hideNotification(notificationEl) {
if (!notificationEl) return;
clearNotificationAutoHide();
notificationEl.style.opacity = '0';
notificationEl.style.transform = 'translateY(12px) scale(0.98)';
setTimeout(() => {
notificationEl.style.display = 'none';
}, 250);
}
function showPasswordNotification(notificationEl, notificationPasswordEl, eyeButtonEl) {
if (!notificationEl) return;
notificationEl.style.display = 'flex';
requestAnimationFrame(() => {
notificationEl.style.opacity = '1';
notificationEl.style.transform = 'translateY(0) scale(1)';
});
updateNotificationPassword(notificationPasswordEl, eyeButtonEl);
scheduleNotificationAutoHide(notificationEl);
}
const uiHost = document.createElement('div');
uiHost.id = 'spg-ui-root';
const uiShadow = uiHost.attachShadow({ mode: 'open' });
const uiStyle = document.createElement('style');
uiStyle.textContent = `
:host {
all: initial;
position: fixed;
display: block;
top: 0;
left: 0;
width: 0;
height: 0;
z-index: 2147483647;
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
color: #0f172a;
}
*, *::before, *::after {
box-sizing: border-box;
font-family: inherit;
}
button, input {
font: inherit;
}
.spg-settings-panel {
flex: 1;
overflow-y: auto;
padding: 8px 2px 2px;
display: flex;
flex-direction: column;
gap: 10px;
scrollbar-width: thin;
scrollbar-color: rgba(191, 219, 254, 0.45) transparent;
}
.spg-settings-panel::-webkit-scrollbar {
width: 6px;
}
.spg-settings-panel::-webkit-scrollbar-thumb {
background: rgba(191, 219, 254, 0.45);
border-radius: 999px;
}
.spg-section {
background: linear-gradient(180deg, rgba(15, 23, 42, 0.3) 0%, rgba(30, 41, 59, 0.22) 100%);
border: 1px solid rgba(191, 219, 254, 0.22);
border-radius: 14px;
padding: 10px 12px;
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.08);
}
.spg-section-title {
display: block;
font-size: 11px;
font-weight: 700;
letter-spacing: 0.08em;
text-transform: uppercase;
color: #bfdbfe;
margin-bottom: 8px;
}
.spg-row {
display: grid;
grid-template-columns: minmax(0, 1fr) auto;
align-items: center;
column-gap: 12px;
color: #e2e8f0;
font-size: 14px;
}
.spg-row--stack {
grid-template-columns: 1fr;
row-gap: 8px;
align-items: stretch;
}
.spg-row + .spg-row {
margin-top: 8px;
}
.spg-row-text {
line-height: 1.22;
}
.spg-length-controls {
display: flex;
align-items: center;
gap: 8px;
width: 100%;
}
.spg-row--stack .spg-length-controls {
justify-content: space-between;
}
.spg-row--stack .spg-slider {
flex: 1;
}
.spg-slider {
-webkit-appearance: none;
appearance: none;
background: transparent;
width: 100%;
height: 4px;
accent-color: #93c5fd;
cursor: pointer;
}
.spg-slider::-webkit-slider-runnable-track {
height: 4px;
background: rgba(191, 219, 254, 0.35);
border-radius: 999px;
}
.spg-slider::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 14px;
height: 14px;
border-radius: 999px;
margin-top: -5px;
background: #ffffff;
border: 1px solid rgba(147, 197, 253, 0.95);
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.45);
}
.spg-slider::-moz-range-track {
height: 4px;
background: rgba(191, 219, 254, 0.35);
border-radius: 999px;
border: none;
}
.spg-slider::-moz-range-thumb {
width: 14px;
height: 14px;
border-radius: 999px;
background: #ffffff;
border: 1px solid rgba(147, 197, 253, 0.95);
box-shadow: 0 2px 8px rgba(15, 23, 42, 0.45);
}
.spg-value-pill {
min-width: 34px;
text-align: center;
padding: 4px 8px;
border-radius: 999px;
border: 1px solid rgba(191, 219, 254, 0.35);
background: rgba(15, 23, 42, 0.35);
color: #f8fafc;
font-size: 13px;
font-weight: 700;
line-height: 1;
}
.spg-switch {
-webkit-appearance: none;
appearance: none;
width: 42px;
height: 24px;
border-radius: 999px;
border: 1px solid rgba(148, 163, 184, 0.6);
background: radial-gradient(circle at 11px 11px, #ffffff 0 8px, transparent 8.5px), rgba(51, 65, 85, 0.95);
cursor: pointer;
transition: background .18s ease, border-color .18s ease, box-shadow .18s ease;
box-shadow: 0 0 0 2px rgba(15, 23, 42, 0.2) inset;
}
`;
uiShadow.appendChild(uiStyle);
const container = document.createElement('div');
container.style.position = 'fixed';
container.style.bottom = '20px';
container.style.left = '20px';
container.style.width = '56px';
container.style.height = '56px';
container.style.borderRadius = '22px';
container.style.background = 'linear-gradient(135deg, #3730a3 0%, #2563eb 100%)';
container.style.zIndex = '9999';
container.style.boxShadow = '0 20px 35px rgba(14, 23, 42, 0.35)';
container.style.border = '1px solid rgba(255,255,255,0.12)';
container.style.transition = 'width 0.3s cubic-bezier(0.4, 0, 0.2, 1), height 0.3s cubic-bezier(0.4, 0, 0.2, 1), background-color 0.3s';
container.style.overflow = 'hidden';
container.style.display = 'flex';
container.style.flexDirection = 'column';
container.style.justifyContent = 'flex-end';
const button = document.createElement('button');
button.type = 'button';
button.style.width = '100%';
button.style.height = '56px';
button.style.backgroundColor = 'transparent';
button.style.border = 'none';
button.style.cursor = 'pointer';
button.style.display = 'flex';
button.style.alignItems = 'center';
button.style.justifyContent = 'center';
button.style.fontSize = '24px';
button.style.color = '#f9fafb';
button.style.fontWeight = '600';
button.style.position = 'relative';
const buttonContent = document.createElement('span');
buttonContent.innerHTML = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="11" width="14" height="10" rx="2" ry="2"></rect>
<path d="M12 16v2"></path>
<path d="M8 11V7a4 4 0 0 1 8 0v4"></path>
</svg>
`;
buttonContent.style.transition = 'opacity 0.2s ease, font-size 0.2s ease, padding 0.2s ease, font-weight 0.2s ease';
buttonContent.style.display = 'inline-flex';
buttonContent.style.alignItems = 'center';
buttonContent.style.justifyContent = 'center';
buttonContent.style.width = '100%';
buttonContent.style.height = '100%';
button.appendChild(buttonContent);
const settingsButton = document.createElement('button');
settingsButton.type = 'button';
settingsButton.innerHTML = `
<svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true">
<circle cx="12" cy="12" r="3"></circle>
<path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 1 1-2.83 2.83l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 1 1-4 0v-.09a1.65 1.65 0 0 0-1-1.51 1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 1 1-2.83-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 1 1 0-4h.09a1.65 1.65 0 0 0 1.51-1 1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 1 1 2.83-2.83l.06.06a1.65 1.65 0 0 0 1.82.33h0a1.65 1.65 0 0 0 1-1.51V3a2 2 0 1 1 4 0v.09a1.65 1.65 0 0 0 1 1.51h0a1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 1 1 2.83 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82v0a1.65 1.65 0 0 0 1.51 1H21a2 2 0 1 1 0 4h-.09a1.65 1.65 0 0 0-1.51 1z"></path>
</svg>
`;
settingsButton.style.position = 'absolute';
settingsButton.style.right = '8px';
settingsButton.style.width = '28px';
settingsButton.style.height = '28px';
settingsButton.style.border = 'none';
settingsButton.style.background = 'transparent';
settingsButton.style.color = '#f9fafb';
settingsButton.style.display = 'none';
settingsButton.style.cursor = 'pointer';
settingsButton.style.alignItems = 'center';
settingsButton.style.justifyContent = 'center';
settingsButton.setAttribute('aria-label', strings.settingsTitle);
settingsButton.title = strings.settingsTitle;
button.appendChild(settingsButton);
const menuContent = document.createElement('div');
menuContent.style.height = '0';
menuContent.style.opacity = '0';
menuContent.style.transition = 'opacity 0.25s ease';
menuContent.style.padding = '0 18px';
menuContent.style.color = '#f8fafc';
menuContent.style.fontFamily = '-apple-system, BlinkMacSystemFont, sans-serif';
menuContent.style.overflow = 'hidden';
menuContent.style.display = 'flex';
menuContent.style.flexDirection = 'column';
menuContent.style.gap = '8px';
const closeButton = document.createElement('button');
closeButton.type = 'button';
closeButton.textContent = strings.close;
closeButton.style.background = 'none';
closeButton.style.border = 'none';
closeButton.style.color = '#F87171';
closeButton.style.fontSize = '16px';
closeButton.style.cursor = 'pointer';
closeButton.style.width = '28px';
closeButton.style.height = '28px';
closeButton.style.display = 'none';
closeButton.style.alignItems = 'center';
closeButton.style.justifyContent = 'center';
closeButton.style.padding = '0';
closeButton.setAttribute('aria-label', strings.close);
const menuHeader = document.createElement('div');
menuHeader.style.display = 'flex';
menuHeader.style.alignItems = 'center';
menuHeader.style.justifyContent = 'space-between';
menuHeader.style.marginTop = '10px';
menuHeader.style.marginBottom = '8px';
const menuHeaderLeftSpacer = document.createElement('span');
menuHeaderLeftSpacer.style.width = '28px';
menuHeaderLeftSpacer.style.height = '28px';
const menuHeaderTitle = document.createElement('span');
menuHeaderTitle.textContent = strings.settingsTitle;
menuHeaderTitle.style.fontSize = '13px';
menuHeaderTitle.style.fontWeight = '700';
menuHeaderTitle.style.letterSpacing = '0.03em';
menuHeaderTitle.style.textTransform = 'uppercase';
menuHeaderTitle.style.color = '#dbeafe';
menuHeader.appendChild(menuHeaderLeftSpacer);
menuHeader.appendChild(menuHeaderTitle);
menuHeader.appendChild(closeButton);
menuContent.appendChild(menuHeader);
const params = document.createElement('div');
params.className = 'spg-settings-panel';
params.innerHTML = `
<section class="spg-section">
<span class="spg-section-title">${strings.sectionPassword}</span>
<label class="spg-row spg-row--stack" for="passwordLength">
<span class="spg-row-text">${i18n.length}</span>
<span class="spg-length-controls">
<input type="range" id="passwordLength" class="spg-slider" value="12" min="4" max="50" step="1">
<span id="passwordLengthValue" class="spg-value-pill">12</span>
</span>
</label>
<label class="spg-row" for="includeUppercase">
<span class="spg-row-text">${i18n.uppercase}</span>
<input type="checkbox" id="includeUppercase" class="spg-switch" checked aria-label="${i18n.uppercase}">
</label>
<label class="spg-row" for="includeNumbers">
<span class="spg-row-text">${i18n.numbers}</span>
<input type="checkbox" id="includeNumbers" class="spg-switch" checked aria-label="${i18n.numbers}">
</label>
<label class="spg-row" for="includeSymbols">
<span class="spg-row-text">${i18n.symbols}</span>
<input type="checkbox" id="includeSymbols" class="spg-switch" checked aria-label="${i18n.symbols}">
</label>
</section>
<section class="spg-section">
<span class="spg-section-title">${strings.sectionDisplay}</span>
<label class="spg-row" for="showBubble">
<span class="spg-row-text">${strings.showBubble}</span>
<input type="checkbox" id="showBubble" class="spg-switch" checked aria-label="${strings.showBubble}">
</label>
<label class="spg-row" for="hidePasswordSetting">
<span class="spg-row-text">${strings.hidePasswordSetting}</span>
<input type="checkbox" id="hidePasswordSetting" class="spg-switch" checked aria-label="${strings.hidePasswordSetting}">
</label>
</section>
`;
menuContent.appendChild(params);
const lengthInput = menuContent.querySelector('#passwordLength');
const lengthValueEl = menuContent.querySelector('#passwordLengthValue');
const includeUppercaseInput = menuContent.querySelector('#includeUppercase');
const includeNumbersInput = menuContent.querySelector('#includeNumbers');
const includeSymbolsInput = menuContent.querySelector('#includeSymbols');
const showBubbleInput = menuContent.querySelector('#showBubble');
const hidePasswordSettingInput = menuContent.querySelector('#hidePasswordSetting');
const storedLength = sanitizeLength(getSetting('length', DEFAULT_LENGTH), DEFAULT_LENGTH);
const storedUppercase = sanitizeBoolean(getSetting('uppercase', true), true);
const storedNumbers = sanitizeBoolean(getSetting('numbers', true), true);
const storedSymbols = sanitizeBoolean(getSetting('symbols', true), true);
const storedShowBubble = sanitizeBoolean(getSetting('showBubble', true), true);
const storedHidePassword = sanitizeBoolean(getSetting('hidePassword', true), true);
let showBubbleEnabled = storedShowBubble;
let hidePasswordEnabled = storedHidePassword;
if (lengthInput) lengthInput.value = storedLength;
if (lengthValueEl) lengthValueEl.textContent = `${storedLength}`;
if (includeUppercaseInput) includeUppercaseInput.checked = storedUppercase;
if (includeNumbersInput) includeNumbersInput.checked = storedNumbers;
if (includeSymbolsInput) includeSymbolsInput.checked = storedSymbols;
if (showBubbleInput) showBubbleInput.checked = showBubbleEnabled;
if (hidePasswordSettingInput) hidePasswordSettingInput.checked = hidePasswordEnabled;
function updateSwitchUI(toggleInput) {
if (!toggleInput) return;
if (toggleInput.checked) {
toggleInput.style.background = 'radial-gradient(circle at 31px 11px, #ffffff 0 8px, transparent 8.5px), #60a5fa';
toggleInput.style.borderColor = 'rgba(147,197,253,0.95)';
} else {
toggleInput.style.background = 'radial-gradient(circle at 11px 11px, #ffffff 0 8px, transparent 8.5px), rgba(51,65,85,0.95)';
toggleInput.style.borderColor = 'rgba(148,163,184,0.6)';
}
}
[
includeUppercaseInput,
includeNumbersInput,
includeSymbolsInput,
showBubbleInput,
hidePasswordSettingInput
].forEach(updateSwitchUI);
lengthInput?.addEventListener('input', () => {
const sanitized = sanitizeLength(lengthInput.value, storedLength);
lengthInput.value = sanitized;
if (lengthValueEl) lengthValueEl.textContent = `${sanitized}`;
setSetting('length', sanitized);
});
includeUppercaseInput?.addEventListener('change', () => {
setSetting('uppercase', includeUppercaseInput.checked);
updateSwitchUI(includeUppercaseInput);
});
includeNumbersInput?.addEventListener('change', () => {
setSetting('numbers', includeNumbersInput.checked);
updateSwitchUI(includeNumbersInput);
});
includeSymbolsInput?.addEventListener('change', () => {
setSetting('symbols', includeSymbolsInput.checked);
updateSwitchUI(includeSymbolsInput);
});
function setBubbleVisibility(forceVisible = false) {
container.style.display = (showBubbleEnabled || forceVisible) ? 'flex' : 'none';
}
function closeSettingsPanel() {
menuContent.style.opacity = '0';
closeButton.style.display = 'none';
buttonContent.style.opacity = '0';
setTimeout(() => {
container.style.width = '56px';
container.style.height = '56px';
container.style.background = 'linear-gradient(135deg, #3730a3 0%, #2563eb 100%)';
menuContent.style.height = '0';
buttonContent.innerHTML = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="11" width="14" height="10" rx="2" ry="2"></rect>
<path d="M12 16v2"></path>
<path d="M8 11V7a4 4 0 0 1 8 0v4"></path>
</svg>
`;
buttonContent.style.fontSize = '24px';
buttonContent.style.fontWeight = 'normal';
buttonContent.style.opacity = '1';
buttonContent.style.padding = '0';
settingsButton.style.display = 'none';
setBubbleVisibility();
}, 200);
}
showBubbleInput?.addEventListener('change', () => {
showBubbleEnabled = showBubbleInput.checked;
setSetting('showBubble', showBubbleEnabled);
updateSwitchUI(showBubbleInput);
const panelOpen = container.style.height !== '56px' && container.style.height !== '';
setBubbleVisibility(panelOpen);
});
hidePasswordSettingInput?.addEventListener('change', () => {
hidePasswordEnabled = hidePasswordSettingInput.checked;
setSetting('hidePassword', hidePasswordEnabled);
updateSwitchUI(hidePasswordSettingInput);
if (currentPassword) {
isPasswordVisible = !hidePasswordEnabled;
updateNotificationPassword(notificationPassword, eyeButton);
}
});
function openSettingsPanel() {
setBubbleVisibility(true);
container.style.width = '240px';
container.style.height = '350px';
container.style.background = 'linear-gradient(135deg, #4f46e5 0%, #2563eb 100%)';
buttonContent.style.opacity = '0';
settingsButton.style.display = 'none';
menuContent.style.height = '292px';
setTimeout(() => {
buttonContent.textContent = i18n.generate;
buttonContent.style.fontSize = '12px';
buttonContent.style.fontWeight = 'bold';
buttonContent.style.opacity = '1';
buttonContent.style.padding = '0';
menuContent.style.opacity = '1';
closeButton.style.display = 'flex';
}, 200);
}
setBubbleVisibility();
function getCurrentOptions() {
const lengthVal = lengthInput?.value ?? storedLength;
const length = sanitizeLength(lengthVal, storedLength);
if (lengthInput) {
lengthInput.value = length;
if (lengthValueEl) lengthValueEl.textContent = `${length}`;
setSetting('length', length);
}
const includeUppercase = includeUppercaseInput?.checked ?? storedUppercase;
const includeNumbers = includeNumbersInput?.checked ?? storedNumbers;
const includeSymbols = includeSymbolsInput?.checked ?? storedSymbols;
return { length, includeUppercase, includeNumbers, includeSymbols };
}
container.appendChild(menuContent);
container.appendChild(button);
const notification = document.createElement('div');
Object.assign(notification.style, {
position: 'fixed',
bottom: '20px',
right: '20px',
padding: '10px',
background: 'linear-gradient(160deg, rgba(15,23,42,0.96) 0%, rgba(30,41,59,0.96) 100%)',
color: '#f8fafc',
borderRadius: '14px',
border: '1px solid rgba(147,197,253,0.28)',
zIndex: '10000',
display: 'none',
flexDirection: 'column',
alignItems: 'stretch',
gap: '8px',
minWidth: '280px',
maxWidth: '360px',
fontFamily: '-apple-system, BlinkMacSystemFont, sans-serif',
boxShadow: '0 18px 42px rgba(2, 6, 23, 0.5), inset 0 1px 0 rgba(255,255,255,0.05)',
backdropFilter: 'blur(6px)',
opacity: '0',
transform: 'translateY(12px) scale(0.98)',
transition: 'opacity 0.25s ease, transform 0.25s ease'
});
notification.setAttribute('role', 'status');
notification.setAttribute('aria-live', 'polite');
const notificationHeader = document.createElement('div');
Object.assign(notificationHeader.style, {
display: 'flex',
alignItems: 'center',
justifyContent: 'space-between',
gap: '10px'
});
const notificationStatus = document.createElement('div');
Object.assign(notificationStatus.style, {
display: 'flex',
alignItems: 'center',
gap: '6px',
minWidth: '0'
});
const notificationStatusIcon = document.createElement('span');
Object.assign(notificationStatusIcon.style, {
width: '24px',
height: '24px',
borderRadius: '999px',
display: 'inline-flex',
alignItems: 'center',
justifyContent: 'center',
flexShrink: '0'
});
const notificationTitle = document.createElement('span');
notificationTitle.textContent = strings.copied;
notificationTitle.style.fontWeight = '700';
notificationTitle.style.fontSize = '14px';
notificationTitle.style.lineHeight = '1.2';
const notificationActions = document.createElement('div');
Object.assign(notificationActions.style, {
display: 'flex',
alignItems: 'center',
gap: '6px'
});
const eyeButton = document.createElement('button');
eyeButton.type = 'button';
Object.assign(eyeButton.style, {
width: '28px',
height: '28px',
background: 'transparent',
border: 'none',
boxShadow: 'none',
color: '#bfdbfe',
cursor: 'pointer',
padding: '0',
borderRadius: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'transform 0.15s ease, filter 0.15s ease, opacity 0.15s ease'
});
const notificationClose = document.createElement('button');
notificationClose.type = 'button';
notificationClose.textContent = strings.close;
Object.assign(notificationClose.style, {
width: '32px',
height: '32px',
background: 'transparent',
border: 'none',
boxShadow: 'none',
color: '#fda4af',
cursor: 'pointer',
fontSize: '16px',
lineHeight: '1',
padding: '0',
borderRadius: '10px',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
transition: 'transform 0.15s ease, filter 0.15s ease, opacity 0.15s ease'
});
notificationClose.setAttribute('aria-label', strings.close);
notificationClose.title = strings.close;
const notificationPasswordWrap = document.createElement('div');
Object.assign(notificationPasswordWrap.style, {
padding: '10px 12px',
borderRadius: '10px',
border: '1px solid rgba(148, 163, 184, 0.3)',
background: 'rgba(15, 23, 42, 0.44)',
minHeight: '42px',
display: 'flex',
alignItems: 'center'
});
const notificationPassword = document.createElement('span');
Object.assign(notificationPassword.style, {
fontFamily: 'SFMono-Regular, Consolas, "Liberation Mono", Menlo, monospace',
letterSpacing: '0.3em',
fontSize: '14px',
lineHeight: '1.25',
wordBreak: 'break-all'
});
notificationStatus.appendChild(notificationStatusIcon);
notificationStatus.appendChild(notificationTitle);
notificationStatus.appendChild(eyeButton);
notificationActions.appendChild(notificationClose);
notificationHeader.appendChild(notificationStatus);
notificationHeader.appendChild(notificationActions);
notificationPasswordWrap.appendChild(notificationPassword);
notification.appendChild(notificationHeader);
notification.appendChild(notificationPasswordWrap);
function setNotificationState(isError = false) {
if (isError) {
notification.style.borderColor = 'rgba(248, 113, 113, 0.42)';
notificationStatusIcon.style.background = 'rgba(248, 113, 113, 0.18)';
notificationStatusIcon.style.color = '#fca5a5';
notificationTitle.style.color = '#fecaca';
notificationStatusIcon.innerHTML = notificationErrorIcon;
return;
}
notification.style.borderColor = 'rgba(147, 197, 253, 0.28)';
notificationStatusIcon.style.background = 'rgba(52, 211, 153, 0.18)';
notificationStatusIcon.style.color = '#6ee7b7';
notificationTitle.style.color = '#e2e8f0';
notificationStatusIcon.innerHTML = notificationSuccessIcon;
}
setNotificationState(false);
eyeButton.innerHTML = eyeClosedIcon;
eyeButton.setAttribute('aria-label', strings.reveal);
eyeButton.title = strings.reveal;
eyeButton.addEventListener('click', () => {
if (!currentPassword) return;
isPasswordVisible = !isPasswordVisible;
updateNotificationPassword(notificationPassword, eyeButton);
});
function bindNotificationActionHover(btn, activeOpacity = '1') {
btn.addEventListener('mouseenter', () => {
btn.style.transform = 'translateY(-1px)';
btn.style.filter = 'brightness(1.08)';
btn.style.opacity = activeOpacity;
});
btn.addEventListener('mouseleave', () => {
btn.style.transform = 'translateY(0)';
btn.style.filter = 'brightness(1)';
btn.style.opacity = '1';
});
}
bindNotificationActionHover(eyeButton);
bindNotificationActionHover(notificationClose, '0.96');
notificationClose.addEventListener('click', () => hideNotification(notification));
notification.addEventListener('mouseenter', clearNotificationAutoHide);
notification.addEventListener('mouseleave', () => {
if (notification.style.display !== 'none') {
scheduleNotificationAutoHide(notification, 2200);
}
});
uiShadow.appendChild(container);
uiShadow.appendChild(notification);
document.body.appendChild(uiHost);
const lockButtons = new WeakMap();
const observedInputs = new Set();
let repositionRaf = null;
let lastLockPassword = '';
const resizeObserver = typeof ResizeObserver !== 'undefined'
? new ResizeObserver(entries => {
entries.forEach(entry => positionLockButton(entry.target));
})
: null;
function lockIconSVG() {
return `
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="11" width="14" height="10" rx="2" ry="2"></rect>
<path d="M12 16v2"></path>
<path d="M8 11V7a4 4 0 0 1 8 0v4"></path>
</svg>
`;
}
function getTextContext(input) {
const labelText = Array.from(input.labels || []).map(l => l.textContent || '').join(' ');
return [
input.name || '',
input.id || '',
input.placeholder || '',
input.getAttribute('aria-label') || '',
labelText
].join(' ').toLowerCase();
}
function isLikelyOldPassword(input) {
const text = getTextContext(input);
return /\b(old|current|existing|previous|ancien|ancienne|actuel|existant|antiguo|anterior|actual|alt|aktuell|vorhanden|vecchio|attuale|antigo|atual|старый|текущий|существующий|旧|已有|当前|古い|既存|이전|기존|oude|bestaand|stary|istniejący|eski|mevcut|gamla|befintlig|vechi|existent|cũ|hiện tại|पुराना|मौजूदा|เก่า|มีอยู่)\b/i.test(text);
}
function isLikelyPasswordField(input) {
if (!input) return false;
const type = (input.type || '').toLowerCase();
const autocomplete = (input.getAttribute('autocomplete') || '').toLowerCase();
const text = getTextContext(input);
const passwordHint = /(password|passwort|passcode|mot de passe|\bmdp\b|senha|contraseña|clave|hasło|пароль|парол|암호|비밀번호|密码|密碼|パスワード|كلمة\s?المرور|şifre|şifresi|parola|jelszó|heslo|slaptažodis|parool|lösenord|adgangskode|salasana|wachtwoord|κωδικός|סיסמה|पासवर्ड|รหัสผ่าน|mật\s?khẩu|kata\s?sandi|kata\s?laluan|senha\s?de\s?acesso|пароль\s?доступу)/i;
return type === 'password' || autocomplete.includes('password') || passwordHint.test(text);
}
function shouldAutoFillPassword(input) {
return input && !input.disabled && !input.readOnly && isLikelyPasswordField(input);
}
function getPlacementBounds(input) {
const inputRect = input.getBoundingClientRect();
const parent = input.parentElement;
if (!parent) return { rect: inputRect, rightEdge: inputRect.right, leftEdge: inputRect.left };
let rightEdge = inputRect.right;
let leftEdge = inputRect.left;
Array.from(parent.children).forEach(node => {
if (node === input) return;
const r = node.getBoundingClientRect();
const verticalOverlap = !(r.bottom < inputRect.top || r.top > inputRect.bottom);
if (r.width > 0 && r.height > 0 && verticalOverlap) {
rightEdge = Math.max(rightEdge, r.right);
leftEdge = Math.min(leftEdge, r.left);
}
});
return { rect: inputRect, rightEdge, leftEdge };
}
function collectPasswordTargets(triggerInput) {
const scope = triggerInput.form || triggerInput.closest('form, fieldset, section, div') || document;
let candidates = Array.from(scope.querySelectorAll('input[type="password"]'));
if (candidates.length === 0 && scope !== document) {
candidates = Array.from(document.querySelectorAll('input[type="password"]'));
}
const filtered = candidates.filter(shouldAutoFillPassword).filter(input => !isLikelyOldPassword(input));
if (!filtered.includes(triggerInput) && shouldAutoFillPassword(triggerInput) && !isLikelyOldPassword(triggerInput)) {
filtered.unshift(triggerInput);
}
return filtered;
}
function positionLockButton(input) {
const btn = lockButtons.get(input);
if (!btn) return;
if (input.offsetParent === null) {
btn.style.display = 'none';
return;
}
const { rect, rightEdge, leftEdge } = getPlacementBounds(input);
if (rect.width === 0 || rect.height === 0) {
btn.style.display = 'none';
return;
}
const size = Math.min(Math.max(rect.height, 24), 48);
const margin = 4;
const viewportWidth = document.documentElement.clientWidth;
const candidateRight = rightEdge + margin;
const candidateLeft = leftEdge - size - margin;
const fitsRight = candidateRight + size <= viewportWidth;
const fitsLeft = candidateLeft >= 0;
const useRight = fitsRight || (!fitsRight && !fitsLeft);
const left = (useRight ? candidateRight : candidateLeft) + window.scrollX;
const top = rect.top + (rect.height - size) / 2 + window.scrollY;
btn.style.width = `${size}px`;
btn.style.height = `${size}px`;
btn.style.left = `${left}px`;
btn.style.top = `${top}px`;
btn.style.display = 'flex';
}
function scheduleRepositionAll() {
if (repositionRaf) return;
repositionRaf = requestAnimationFrame(() => {
repositionRaf = null;
observedInputs.forEach(positionLockButton);
});
}
function removeLockButton(input) {
const btn = lockButtons.get(input);
if (!btn) return;
btn.remove();
lockButtons.delete(input);
observedInputs.delete(input);
if (resizeObserver) resizeObserver.unobserve(input);
}
function createLockButton(input) {
if (lockButtons.has(input) || !isLikelyPasswordField(input)) return;
const btn = document.createElement('button');
btn.type = 'button';
btn.innerHTML = lockIconSVG();
btn.style.all = 'unset';
Object.assign(btn.style, {
position: 'absolute',
background: 'linear-gradient(135deg, #4f46e5 0%, #2563eb 100%)',
border: '1px solid rgba(255,255,255,0.25)',
color: '#f8fafc',
borderRadius: '10px',
boxShadow: '0 8px 16px rgba(0,0,0,0.18)',
padding: '0',
opacity: '0.9',
display: 'none',
alignItems: 'center',
justifyContent: 'center',
cursor: 'pointer',
zIndex: '9999',
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif',
transition: 'transform 0.1s ease, opacity 0.15s ease'
});
btn.setAttribute('aria-label', strings.fill);
btn.title = strings.fill;
btn.addEventListener('mousedown', e => e.preventDefault());
btn.addEventListener('click', (e) => {
e.preventDefault();
e.stopPropagation();
if (input.disabled || input.readOnly) return;
const { length, includeUppercase, includeNumbers, includeSymbols } = getCurrentOptions();
let password = generatePassword(length, includeUppercase, includeNumbers, includeSymbols);
for (let i = 0; i < 3 && password === lastLockPassword; i++) {
password = generatePassword(length, includeUppercase, includeNumbers, includeSymbols);
}
lastLockPassword = password;
const targets = collectPasswordTargets(input);
targets.forEach(field => {
field.value = password;
field.dispatchEvent(new Event('input', { bubbles: true }));
field.dispatchEvent(new Event('change', { bubbles: true }));
});
currentPassword = '';
isPasswordVisible = false;
hideNotification(notification);
});
input.addEventListener('focus', () => {
btn.style.opacity = '1';
btn.style.transform = 'scale(1.02)';
});
input.addEventListener('blur', () => {
btn.style.opacity = '0.9';
btn.style.transform = 'scale(1)';
});
document.body.appendChild(btn);
lockButtons.set(input, btn);
observedInputs.add(input);
if (resizeObserver) resizeObserver.observe(input);
positionLockButton(input);
}
function scanPasswordFields(root = document) {
const fields = root.querySelectorAll('input[type="password"]');
fields.forEach(createLockButton);
}
scanPasswordFields();
function schedulePostLoadRecheck() {
const run = () => {
scheduleRepositionAll();
setTimeout(scheduleRepositionAll, 150);
setTimeout(scheduleRepositionAll, 500);
};
if (document.readyState === 'complete') {
run();
} else {
window.addEventListener('load', run, { once: true });
}
}
schedulePostLoadRecheck();
window.addEventListener('resize', scheduleRepositionAll, { passive: true });
window.addEventListener('scroll', scheduleRepositionAll, { passive: true, capture: true });
const mutationObserver = new MutationObserver(mutations => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.nodeType !== 1) return;
if (node.matches && node.matches('input[type="password"]')) createLockButton(node);
node.querySelectorAll?.('input[type="password"]').forEach(createLockButton);
});
mutation.removedNodes.forEach(node => {
if (node.nodeType !== 1) return;
if (lockButtons.has(node)) removeLockButton(node);
node.querySelectorAll?.('input[type="password"]').forEach(removeLockButton);
});
});
scheduleRepositionAll();
});
if (document.body) {
mutationObserver.observe(document.body, { childList: true, subtree: true });
}
function generateAndCopyPassword() {
const { length, includeUppercase, includeNumbers, includeSymbols } = getCurrentOptions();
const password = generatePassword(length, includeUppercase, includeNumbers, includeSymbols);
const copyPromise = copyTextToClipboard(password);
copyPromise.then(() => {
notificationTitle.textContent = strings.copied;
setNotificationState(false);
}).catch(err => {
console.error('Erreur lors de la copie :', err);
notificationTitle.textContent = strings.copyFailed;
setNotificationState(true);
}).finally(() => {
currentPassword = password;
isPasswordVisible = !hidePasswordEnabled;
showPasswordNotification(notification, notificationPassword, eyeButton);
});
}
container.addEventListener('mouseenter', () => {
if (container.style.height === '56px' || container.style.height === '') {
container.style.width = '220px';
container.style.background = 'linear-gradient(135deg, #4f46e5 0%, #2563eb 100%)';
buttonContent.style.opacity = '0';
setTimeout(() => {
buttonContent.textContent = i18n.generateFull;
buttonContent.style.fontSize = '14px';
buttonContent.style.fontWeight = 'normal';
buttonContent.style.opacity = '1';
buttonContent.style.padding = '0 32px 0 16px';
settingsButton.style.display = 'flex';
}, 200);
}
});
container.addEventListener('mouseleave', () => {
if (container.style.height === '56px' || container.style.height === '') {
container.style.width = '56px';
container.style.background = 'linear-gradient(135deg, #3730a3 0%, #2563eb 100%)';
buttonContent.style.opacity = '0';
settingsButton.style.display = 'none';
setTimeout(() => {
if (container.style.height === '56px' || container.style.height === '') {
buttonContent.innerHTML = `
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<rect x="5" y="11" width="14" height="10" rx="2" ry="2"></rect>
<path d="M12 16v2"></path>
<path d="M8 11V7a4 4 0 0 1 8 0v4"></path>
</svg>
`;
buttonContent.style.fontSize = '24px';
buttonContent.style.fontWeight = 'normal';
buttonContent.style.opacity = '1';
buttonContent.style.padding = '0';
settingsButton.style.display = 'none';
}
}, 200);
}
});
button.addEventListener('click', (e) => {
if (!settingsButton.contains(e.target)) {
generateAndCopyPassword();
}
});
settingsButton.addEventListener('click', () => {
openSettingsPanel();
});
closeButton.addEventListener('click', closeSettingsPanel);
if (typeof GM_registerMenuCommand === 'function') {
try {
GM_registerMenuCommand(strings.settingsMenu, openSettingsPanel);
GM_registerMenuCommand(i18n.generateFull || translations.en.generateFull, generateAndCopyPassword);
} catch (err) {
console.warn('GM_registerMenuCommand failed', err);
}
}
})();