Özelleştirilmiş istemlerinizi (prompts) hızlı ve kolay bir şekilde düzenleyin ve erişin. Etkileşimli bilgiler eklemek için Dinamik İstemleri kullanın ve istemlerinizi her duruma uyarlayın. Şunlarla uyumludur: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao ve ChatGLM.
// ==UserScript==
// @name My Prompt
// @name:pt-BR Meu Prompt
// @name:zh-CN 我的提示词
// @name:zh-TW 我的提示詞
// @name:fr-CA Mon Prompt
// @name:ckb پڕۆمپتەکەم
// @name:ar مُوجِّهي
// @name:be Мой запыт
// @name:bg Моята подкана
// @name:cs Můj pokyn
// @name:da Min prompt
// @name:de Mein Prompt
// @name:el Η προτροπή μου
// @name:en My Prompt
// @name:eo Mia instigo
// @name:es Mi Prompt
// @name:fi Kehotteeni
// @name:fr Mon Prompt
// @name:he ההנחיה שלי
// @name:hr Moja uputa
// @name:hu Promptom
// @name:id Prompt Saya
// @name:it Il mio Prompt
// @name:ja マイプロンプト
// @name:ka ჩემი პრომპტი
// @name:ko 나의 프롬프트
// @name:mr माझी सूचना
// @name:nb Min prompt
// @name:nl Mijn Prompt
// @name:pl Mój Prompt
// @name:ro Prompt-ul meu
// @name:ru Мой Промпт
// @name:sk Môj pokyn
// @name:sr Мој упит
// @name:sv Min prompt
// @name:th พรอมต์ของฉัน
// @name:tr İstemim
// @name:uk Мій запит
// @name:ug مېنىڭ پرومپتۇم
// @name:vi Lời nhắc của tôi
// @namespace https://github.com/0H4S
// @version 2.9
// @description Organize and access your custom prompts quickly and easily. Use Dynamic Prompts to insert interactive information and adapt your prompts for any situation. Compatible with: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao, and ChatGLM.
// @description:pt-BR Organize e acesse seus prompts personalizados com rapidez e facilidade. Use o Prompt Dinâmico para inserir informações interativas e adapte seus prompts para qualquer situação. Compatível com: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao e ChatGLM.
// @description:zh-CN 轻松快速地组织和访问您的自定义提示词。使用动态提示词插入交互式信息,使您的提示词适用于任何情况。支持:ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, 腾讯元宝和ChatGLM。。
// @description:zh-TW 輕鬆快速地組織和存取您的自訂提示詞。使用動態提示詞插入互動資訊,使您的提示詞適用於任何情況。支援:ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, 腾讯元宝和ChatGLM。
// @description:fr-CA Organisez et accédez à vos invites personnalisées rapidement et facilement. Utilisez des invites dynamiques pour insérer des informations interactives et adapter vos invites à toutes les situations. Compatible avec : ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao et ChatGLM.
// @description:ckb ڕێکخستن و دەستگەیشتن بە داواکارییە تایبەتەکانت بە خێرایی و ئاسانی. بەکارهێنانی داواکاری جوڵاو بۆ تێکردنی زانیاری کارلێککار و گونجاندنی داواکارییەکانت بۆ هەر دۆخێک. گونجاوە لەگەڵ: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao و ChatGLM.
// @description:ar تنظيم والوصول إلى مطالباتك المخصصة بسرعة وسهولة. استخدم المطالبات الديناميكية لإدخال معلومات تفاعلية وتكييف مطالباتك مع أي موقف. متوافق مع: ChatGPT و Gemini و DeepSeek و Google AI Studio و Doubao و Claude و Kimi و Qwen و Grok و Mistral و LMArena و LongCat و Z.AI و Perplexity و Poe و Tencent Yuanbao و ChatGLM.
// @description:be Арганізуйце і атрымлівайце доступ да сваіх карыстальніцкіх падказак хутка і лёгка. Выкарыстоўвайце дынамічныя падказкі для ўстаўкі інтэрактыўнай інфармацыі і адаптуйце свае падказкі для любой сітуацыі. Сумяшчальнасць з: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao і ChatGLM.
// @description:bg Организирайте и достъпвайте вашите персонализирани подкани бързо и лесно. Използвайте динамични подкани, за да вмъквате интерактивна информация и да адаптирате подканите си за всяка ситуация. Съвместим с: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao и ChatGLM.
// @description:cs Rychle a snadno organizujte a přistupujte ke svým vlastním promptům. Použijte dynamické prompty k vložení interaktivních informací a přizpůsobte své prompty jakékoli situaci. Kompatibilní s: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao a ChatGLM.
// @description:da Organiser og få adgang til dine tilpassede prompts hurtigt og nemt. Brug dynamiske prompts til at indsætte interaktive oplysninger og tilpas dine prompts til enhver situation. Kompatibel med: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao og ChatGLM.
// @description:de Organisieren und greifen Sie schnell und einfach auf Ihre benutzerdefinierten Prompts zu. Verwenden Sie dynamische Prompts, um interaktive Informationen einzufügen und Ihre Prompts an jede Situation anzupassen. Kompatibel mit: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao und ChatGLM.
// @description:el Οργανώστε και αποκτήστε πρόσβαση στις προσαρμοσμένες εντολές σας γρήγορα και εύκολα. Χρησιμοποιήστε Δυναμικές Εντολές για να εισάγετε διαδραστικές πληροφορίες και να προσαρμόσετε τις εντολές σας σε οποιαδήποτε κατάσταση. Συμβατό με: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao και ChatGLM.
// @description:en Organize and access your custom prompts quickly and easily. Use Dynamic Prompts to insert interactive information and adapt your prompts for any situation. Compatible with: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao, and ChatGLM.
// @description:eo Organizu kaj aliru viajn kutimajn invitojn rapide kaj facile. Uzu Dinamikajn Invitojn por enmeti interagajn informojn kaj adapti viajn invitojn al iu ajn situacio. Kongrua kun: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao kaj ChatGLM.
// @description:es Organice y acceda a sus prompts personalizados de forma rápida y sencilla. Utilice prompts dinámicos para insertar información interactiva y adapte sus prompts a cualquier situación. Compatible con: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao y ChatGLM.
// @description:fi Järjestä ja käytä mukautettuja kehotteitasi nopeasti ja helposti. Käytä dynaamisia kehotteita interaktiivisten tietojen lisäämiseen ja mukauta kehotteesi mihin tahansa tilanteeseen. Yhteensopiva: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao ja ChatGLM.
// @description:fr Organisez et accédez à vos invites personnalisées rapidement et facilement. Utilisez des invites dynamiques pour insérer des informations interactives et adapter vos invites à toutes les situations. Compatible avec : ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao et ChatGLM.
// @description:he ארגן וגש להנחיות המותאמות אישית שלך במהירות ובקלות. השתמש בהנחיות דינמיות כדי להוסיף מידע אינטראקטיבי והתאם את ההנחיות שלך לכל סיטואציה. תואם ל: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao ו-ChatGLM.
// @description:hr Organizirajte i pristupajte svojim prilagođenim upitima brzo i jednostavno. Koristite dinamičke upite za umetanje interaktivnih informacija i prilagodite svoje upite svakoj situaciji. Kompatibilno s: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao i ChatGLM.
// @description:hu Szervezze és érje el egyéni promptjait gyorsan és egyszerűen. Használja a Dinamikus Promptokat interaktív információk beillesztéséhez, és igazítsa promptjait bármilyen helyzethez. Kompatibilis: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao és ChatGLM.
// @description:id Atur dan akses prompt kustom Anda dengan cepat dan mudah. Gunakan Prompt Dinamis untuk menyisipkan informasi interaktif dan sesuaikan prompt Anda dengan situasi apa pun. Kompatibel dengan: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao, dan ChatGLM.
// @description:it Organizza e accedi ai tuoi prompt personalizzati rapidamente e facilmente. Usa i Prompt Dinamici per inserire informazioni interattive e adatta i tuoi prompt a qualsiasi situazione. Compatibile con: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao e ChatGLM.
// @description:ja カスタムプロンプトを簡単かつ迅速に整理してアクセスできます。ダイナミックプロンプトを使用してインタラクティブな情報を挿入し、あらゆる状況に合わせてプロンプトを適応させます。対応:ChatGPT、Gemini、DeepSeek、Google AI Studio、Doubao、Claude、Kimi、Qwen、Grok、Mistral、LMArena、LongCat、Z.AI、Perplexity、Poe、Tencent Yuanbao、ChatGLM。
// @description:ka თქვენი მორგებული მოთხოვნების (prompts) სწრაფი და მარტივი ორგანიზება და წვდომა. გამოიყენეთ დინამიური მოთხოვნები ინტერაქტიული ინფორმაციის ჩასასმელად და მოარგეთ თქვენი მოთხოვნები ნებისმიერ სიტუაციას. თავსებადია: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao და ChatGLM.
// @description:ko 사용자 지정 프롬프트를 빠르고 쉽게 구성하고 액세스하세요. 동적 프롬프트를 사용하여 대화형 정보를 삽입하고 모든 상황에 맞게 프롬프트를 조정하세요. 호환 가능: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao 및 ChatGLM.
// @description:mr आपले सानुकूल प्रॉम्प्ट्स जलद आणि सहजपणे व्यवस्थापित करा आणि त्यात प्रवेश करा. परस्परसंवादी माहिती समाविष्ट करण्यासाठी डायनॅमिक प्रॉम्प्ट्स वापरा आणि कोणत्याही परिस्थितीसाठी आपले प्रॉम्प्ट्स अनुकूल करा. यासह सुसंगत: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao आणि ChatGLM.
// @description:nb Organiser og få tilgang til dine tilpassede prompts raskt og enkelt. Bruk Dynamiske Prompts for å sette inn interaktiv informasjon og tilpass dine prompts til enhver situasjon. Kompatibel med: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao og ChatGLM.
// @description:nl Organiseer en krijg snel en gemakkelijk toegang tot uw aangepaste prompts. Gebruik Dynamische Prompts om interactieve informatie in te voegen en pas uw prompts aan elke situatie aan. Compatibel met: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao en ChatGLM.
// @description:pl Organizuj i korzystaj ze swoich niestandardowych promptów szybko i łatwo. Używaj dynamicznych promptów, aby wstawiać interaktywne informacje i dostosowywać prompty do każdej sytuacji. Kompatybilny z: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao i ChatGLM.
// @description:ro Organizați și accesați prompturile personalizate rapid și ușor. Utilizați prompturi dinamice pentru a insera informații interactive și adaptați prompturile pentru orice situație. Compatibil cu: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao și ChatGLM.
// @description:ru Упорядочивайте и получайте доступ к своим пользовательским подсказкам быстро и легко. Используйте динамические подсказки для вставки интерактивной информации и адаптируйте свои подсказки к любой ситуации. Совместимость с: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao и ChatGLM.
// @description:sk Rýchlo a jednoducho organizujte a pristupujte k svojim vlastným promptom. Použite dynamické prompty na vloženie interaktívnych informácií a prispôsobte svoje prompty akejkoľvek situácii. Kompatibilné s: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao a ChatGLM.
// @description:sr Организујте и приступајте својим прилагођеним упитима брзо и лако. Користите динамичке упите за уметање интерактивних информација и прилагодите своје упите свакој ситуацији. Компатибилно са: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao и ChatGLM.
// @description:sv Organisera och få tillgång till dina anpassade prompts snabbt och enkelt. Använd dynamiska prompts för att infoga interaktiv information och anpassa dina prompts till alla situationer. Kompatibel med: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao och ChatGLM.
// @description:th จัดระเบียบและเข้าถึงคำสั่งพรอมต์ที่ปรับแต่งเองของคุณได้อย่างรวดเร็วและง่ายดาย ใช้พรอมต์แบบไดนามิกเพื่อแทรกข้อมูลเชิงโต้ตอบและปรับเปลี่ยนพรอมต์ของคุณให้เหมาะกับทุกสถานการณ์ รองรับ: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao และ ChatGLM
// @description:tr Özelleştirilmiş istemlerinizi (prompts) hızlı ve kolay bir şekilde düzenleyin ve erişin. Etkileşimli bilgiler eklemek için Dinamik İstemleri kullanın ve istemlerinizi her duruma uyarlayın. Şunlarla uyumludur: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao ve ChatGLM.
// @description:uk Швидко та легко впорядковуйте та отримуйте доступ до власних підказок. Використовуйте динамічні підказки, щоб додавати інтерактивну інформацію та адаптувати підказки до будь-якої ситуації. Сумісність із: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao та ChatGLM.
// @description:ug ئىختىيارىي ئەسكەرتىشلىرىڭىزنى تېز ۋە ئاسان تەشكىللەڭ ۋە زىيارەت قىلىڭ. ھەرىكەتچان ئەسكەرتىش ئىشلىتىپ ئۆز-ئارا تەسىر كۆرسىتىدىغان ئۇچۇرلارنى قىستۇرۇڭ ۋە ئەسكەرتىشلىرىڭىزنى ھەر قانداق ئەھۋالغا ماسلاشتۇرۇڭ. ماسلىشىشچانلىقى: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao ۋە ChatGLM.
// @description:vi Sắp xếp và truy cập các lời nhắc (prompts) tùy chỉnh của bạn một cách nhanh chóng và dễ dàng. Sử dụng Lời nhắc Động để chèn thông tin tương tác và điều chỉnh lời nhắc của bạn cho mọi tình huống. Tương thích với: ChatGPT, Gemini, DeepSeek, Google AI Studio, Doubao, Claude, Kimi, Qwen, Grok, Mistral, LMArena, LongCat, Z.AI, Perplexity, Poe, Tencent Yuanbao và ChatGLM.
// @author OHAS
// @homepage https://github.com/0H4S
// @icon https://cdn-icons-png.flaticon.com/512/4997/4997543.png
// @license CC-BY-NC-ND-4.0
// @copyright 2025 OHAS. All Rights Reserved.
// @match *://aistudio.google.com/*
// @match *://yuanbao.tencent.com/*
// @match *://gemini.google.com/*
// @match *://chat.deepseek.com/*
// @match *://www.perplexity.ai/*
// @match *://chat.mistral.ai/*
// @match *://www.doubao.com/*
// @match *://www.kimi.com/*
// @match *://chat.qwen.ai/*
// @match *://longcat.chat/*
// @match *://chatgpt.com/*
// @match *://lmarena.ai/*
// @match *://chatglm.cn/*
// @match *://chat.z.ai/*
// @match *://claude.ai/*
// @match *://grok.com/*
// @match *://poe.com/*
// @require https://update.greasyfork.org/scripts/549920.js
// @connect gist.github.com
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @run-at document-end
// @noframes
// @compatible chrome
// @compatible firefox
// @compatible edge
// @compatible opera
// @bgf-colorLT #847dfd
// @bgf-colorDT #6963ca
// @bgf-compatible brave
// @bgf-copyright [2025 OHAS. All Rights Reserved.](https://gist.github.com/0H4S/ae2fa82957a089576367e364cbf02438)
// @contributionURL https://linktr.ee/0H4S
// ==/UserScript==
(function() {
'use strict';
/*eslint-disable*/
// #region GLOBAL
const translations = {
'pt-BR': {
langName: 'Português (BR)',
prompt: 'Prompt',
prompts: 'Prompts',
newPrompt: 'Novo Prompt',
editPrompt: 'Editar Prompt',
title: 'Título',
text: 'Prompt',
save: 'Salvar',
close: 'Fechar',
edit: 'Editar',
delete: 'Excluir',
noSavedPrompts: 'Nenhum prompt salvo.',
addPrompt: 'Adicionar prompt',
import: 'Importar',
export: 'Exportar',
confirmDelete: 'Excluir prompt "{title}"?',
noPromptsToExport: 'Não há prompts para exportar.',
promptsImported: '{count} prompts importados com sucesso!',
errorImporting: 'Erro ao importar o arquivo: {error}',
requiredFields: 'Título e prompt são obrigatórios.',
editorNotFound: 'Não foi possível encontrar a área de texto para {platform}.',
languageSettings: '🌐 Idioma',
fillPlaceholders: 'Preencha as Informações',
insert: 'Inserir',
enablePlaceholders: 'Prompt Dinâmico',
autoExecute: 'Envio Automático',
infoTitle: 'Ajuda',
infoDPDesc: 'Acesse o guia de uso completo:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: 'Envia o prompt para o chat instantaneamente após ser inserido, sem necessidade de ação manual.',
search: 'Procurar prompt...',
searchLanguage: 'Procurar idioma...',
selectAll: 'Selecionar Tudo',
countPrompts: '{count} prompts',
confirmDownloads: 'Você está prestes a baixar {count} arquivos de texto individuais. Continuar?',
noSearchResults: 'Nenhum prompt corresponde à pesquisa.',
ffWarningTitle: 'Aviso Importante',
ffWarningText: 'Nesta área específica da página, o <span style="color: #6a6cfc;"><strong>Meu Prompt</strong></span> não consegue interagir corretamente usando o Firefox.',
ffRecommend: 'Para obter suporte completo em todas as áreas do chat no Doubao, recomendamos o uso de navegadores baseados em Chromium (Chrome, Edge, Brave, etc).',
dontShowAgain: 'Remover Botão de Aviso',
select: 'Selecione uma opção:',
context: 'Contexto',
expand: 'Expandir',
collapse: 'Contrair',
idGroup: 'Grupo ID',
uniqueSelection: 'Seleção Única'
},
'en': {
langName: 'English',
prompt: 'Prompt',
prompts: 'Prompts',
newPrompt: 'New Prompt',
editPrompt: 'Edit Prompt',
title: 'Title',
text: 'Prompt',
save: 'Save',
close: 'Close',
edit: 'Edit',
delete: 'Delete',
noSavedPrompts: 'No saved prompts.',
addPrompt: 'Add prompt',
import: 'Import',
export: 'Export',
confirmDelete: 'Delete prompt "{title}"?',
noPromptsToExport: 'No prompts to export.',
promptsImported: '{count} prompts imported successfully!',
errorImporting: 'Error importing file: {error}',
requiredFields: 'Title and prompt are required.',
editorNotFound: 'Could not find the text area for {platform}.',
languageSettings: '🌐 Language',
fillPlaceholders: 'Fill in the Information',
insert: 'Insert',
enablePlaceholders: 'Dynamic Prompt',
autoExecute: 'Auto-Send',
infoTitle: 'Help',
infoDPDesc: 'Access the complete usage guide:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: 'Sends the prompt to the chat instantly after being inserted, without needing manual action.',
search: 'Search prompt...',
searchLanguage: 'Search language...',
selectAll: 'Select All',
countPrompts: '{count} prompts',
confirmDownloads: 'You are about to download {count} individual text files. Continue?',
noSearchResults: 'No prompts match your search.',
ffWarningTitle: 'Important Notice',
ffWarningText: 'In this specific area of the page, <span style="color: #6a6cfc;"><strong>My Prompt</strong></span> cannot interact properly when using Firefox.',
ffRecommend: 'For full support in all chat areas on Doubao, we recommend using Chromium-based browsers (Chrome, Edge, Brave, etc).',
dontShowAgain: 'Remove Warning Button',
select: 'Select an option:',
context: 'Context',
expand: 'Expand',
collapse: 'Collapse',
idGroup: 'Group ID',
uniqueSelection: 'Unique Selection'
},
'es': {
langName: 'Español',
prompt: 'Prompt',
prompts: 'Prompts',
newPrompt: 'Nuevo Prompt',
editPrompt: 'Editar Prompt',
title: 'Título',
text: 'Prompt',
save: 'Guardar',
close: 'Cerrar',
edit: 'Editar',
delete: 'Eliminar',
noSavedPrompts: 'No hay prompts guardados.',
addPrompt: 'Añadir prompt',
import: 'Importar',
export: 'Exportar',
confirmDelete: '¿Eliminar prompt "{title}"?',
noPromptsToExport: 'No hay prompts para exportar.',
promptsImported: '¡{count} prompts importados con éxito!',
errorImporting: 'Error al importar el archivo: {error}',
requiredFields: 'El título y el prompt son obligatorios.',
editorNotFound: 'No se pudo encontrar el área de texto para {platform}.',
languageSettings: '🌐 Idioma',
fillPlaceholders: 'Rellene la Información',
insert: 'Insertar',
enablePlaceholders: 'Prompt Dinámico',
autoExecute: 'Envío Automático',
infoTitle: 'Ayuda',
infoDPDesc: 'Accede a la guía de uso completa:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: 'Envía el prompt al chat instantáneamente después de ser insertado, sin necesidad de acción manual.',
search: 'Buscar prompt...',
searchLanguage: 'Buscar idioma...',
selectAll: 'Seleccionar todo',
countPrompts: '{count} prompts',
confirmDownloads: 'Estás a punto de descargar {count} archivos de texto individuales. ¿Continuar?',
noSearchResults: 'Ningún prompt coincide con tu búsqueda.',
ffWarningTitle: 'Aviso Importante',
ffWarningText: 'En esta área específica de la página, <span style="color: #6a6cfc;"><strong>Mi Prompt</strong></span> no puede interactuar correctamente cuando se usa Firefox.',
ffRecommend: 'Para obtener compatibilidad completa en todas las áreas del chat en Doubao, recomendamos usar navegadores basados en Chromium (Chrome, Edge, Brave, etc).',
dontShowAgain: 'Quitar Botón de Aviso',
select: 'Seleccione una opción:',
context: 'Contexto',
expand: 'Expandir',
collapse: 'Contraer',
idGroup: 'ID de Grupo',
uniqueSelection: 'Selección Única'
},
'fr': {
langName: 'Français',
prompt: 'Prompt',
prompts: 'Prompts',
newPrompt: 'Nouveau prompt',
editPrompt: 'Modifier le prompt',
title: 'Titre',
text: 'Prompt',
save: 'Enregistrer',
close: 'Fermer',
edit: 'Modifier',
delete: 'Supprimer',
noSavedPrompts: 'Aucun prompt enregistré.',
addPrompt: 'Ajouter un prompt',
import: 'Importer',
export: 'Exporter',
confirmDelete: 'Supprimer le prompt "{title}" ?',
noPromptsToExport: 'Aucun prompt à exporter.',
promptsImported: '{count} prompts importés avec succès !',
errorImporting: 'Erreur lors de l\'importation du fichier : {error}',
requiredFields: 'Le titre et le prompt sont obligatoires.',
editorNotFound: 'Impossible de trouver la zone de texte pour {platform}.',
languageSettings: '🌐 Langue',
fillPlaceholders: 'Remplir les informations',
insert: 'Insérer',
enablePlaceholders: 'Prompt Dynamique',
autoExecute: 'Envoi Automatique',
infoTitle: 'Aide',
infoDPDesc: 'Accédez au guide d\'utilisation complet :<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: 'Envoie le prompt au chat instantanément après son insertion, sans nécessiter d\'action manuelle.',
search: 'Rechercher prompt...',
searchLanguage: 'Rechercher langue...',
selectAll: 'Tout sélectionner',
countPrompts: '{count} prompts',
confirmDownloads: 'Vous êtes sur le point de télécharger {count} fichiers texte individuels. Continuer ?',
noSearchResults: 'Aucun prompt ne correspond à la recherche.',
ffWarningTitle: 'Avis Important',
ffWarningText: 'Dans cette zone spécifique de la page, <span style="color: #6a6cfc;"><strong>Mon Prompt</strong></span> ne peut pas interagir correctement avec Firefox.',
ffRecommend: 'Pour une prise en charge complète dans toutes les zones du chat sur Doubao, nous recommandons d’utiliser des navigateurs basés sur Chromium (Chrome, Edge, Brave, etc).',
dontShowAgain: 'Retirer le Bouton d’Avertissement',
select: 'Sélectionnez une option :',
context: 'Contexte',
expand: 'Développer',
collapse: 'Réduire',
idGroup: 'ID de Groupe',
uniqueSelection: 'Sélection Unique'
},
'ru': {
langName: 'Русский',
prompt: 'Промпт',
prompts: 'Промпты',
newPrompt: 'Новый промпт',
editPrompt: 'Редактировать промпт',
title: 'Название',
text: 'Текст промпта',
save: 'Сохранить',
close: 'Закрыть',
edit: 'Редактировать',
delete: 'Удалить',
noSavedPrompts: 'Нет сохраненных промптов.',
addPrompt: 'Добавить промпт',
import: 'Импорт',
export: 'Экспорт',
confirmDelete: 'Удалить промпт "{title}"?',
noPromptsToExport: 'Нет промптов для экспорта.',
promptsImported: 'Успешно импортировано промптов: {count}!',
errorImporting: 'Ошибка при импорте файла: {error}',
requiredFields: 'Название и текст промпта обязательны.',
editorNotFound: 'Не удалось найти текстовое поле для {platform}.',
languageSettings: '🌐 Язык',
fillPlaceholders: 'Заполните данные',
insert: 'Вставить',
enablePlaceholders: 'Динамический промпт',
autoExecute: 'Автоотправка',
infoTitle: 'Справка',
infoDPDesc: 'Открыть полное руководство пользователя:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: 'Автоматически отправляет промпт в чат сразу после вставки, не требуя ручного подтверждения.',
search: 'Поиск промпта...',
searchLanguage: 'Поиск языка...',
selectAll: 'Выбрать все',
countPrompts: '{count} промптов',
confirmDownloads: 'Вы собираетесь скачать {count} отдельных текстовых файлов. Продолжить?',
noSearchResults: 'По вашему запросу промптов не найдено.',
ffWarningTitle: 'Важное уведомление',
ffWarningText: 'В этой конкретной области страницы <span style="color: #6a6cfc;"><strong>Мой Промпты</strong></span> не может корректно взаимодействовать при использовании Firefox.',
ffRecommend: 'Для полной поддержки всех областей чата в Doubao мы рекомендуем использовать браузеры на базе Chromium (Chrome, Edge, Brave и др.).',
dontShowAgain: 'Удалить кнопку предупреждения',
select: 'Выберите вариант:',
context: 'Контекст',
expand: 'Развернуть',
collapse: 'Свернуть',
idGroup: 'ID группы',
uniqueSelection: 'Уникальный выбор'
},
'zh-CN': {
langName: '简体中文',
prompt: '提示',
prompts: '提示',
newPrompt: '新建提示',
editPrompt: '编辑提示',
title: '标题',
text: '提示内容',
save: '保存',
close: '关闭',
edit: '编辑',
delete: '删除',
noSavedPrompts: '没有已保存的提示。',
addPrompt: '添加提示',
import: '导入',
export: '导出',
confirmDelete: '确定要删除提示 "{title}" 吗?',
noPromptsToExport: '沒有可導出的提示。',
promptsImported: '成功导入 {count} 个提示!',
errorImporting: '导入文件时出错: {error}',
requiredFields: '标题和提示内容为必填项。',
editorNotFound: '未能找到 {platform} 的文本输入区域。',
languageSettings: '🌐 语言',
fillPlaceholders: '填写信息',
insert: '插入',
enablePlaceholders: '动态提示',
autoExecute: '自动发送',
infoTitle: '帮助',
infoDPDesc: '访问完整的使用指南:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: '插入后立即将提示发送到聊天室,无需手动操作。',
search: '搜索提示...',
searchLanguage: '搜索语言...',
selectAll: '全选',
countPrompts: '{count} 个提示词',
confirmDownloads: '即将下载 {count} 个单独的文本文件。是否继续?',
noSearchResults: '未找到匹配的提示词。',
ffWarningTitle: '重要提示',
ffWarningText: '在页面的此特定区域,<span style="color: #6a6cfc;"><strong>我的提示词</strong></span> 在使用 Firefox 时无法正常互动。',
ffRecommend: '为在 Doubao 的所有聊天区域获得完整支持,建议使用基于 Chromium 的浏览器(Chrome、Edge、Brave 等)。',
dontShowAgain: '移除警告按钮',
select: '请选择一个选项:',
context: '上下文',
expand: '展开',
collapse: '折叠',
idGroup: '组ID',
uniqueSelection: '唯一选择'
},
'ja': {
langName: '日本語',
prompt: 'プロンプト',
prompts: 'プロンプト',
newPrompt: '新規プロンプト',
editPrompt: 'プロンプト編集',
title: 'タイトル',
text: 'プロンプト',
save: '保存',
close: '閉じる',
edit: '編集',
delete: '削除',
noSavedPrompts: '保存されたプロンプトはありません',
addPrompt: 'プロンプトを追加',
import: 'インポート',
export: 'エクスポート',
confirmDelete: 'プロンプト「{title}」を削除しますか?',
noPromptsToExport: 'エクスポートするプロンプトがありません',
promptsImported: '{count}件のプロンプトをインポートしました',
errorImporting: 'ファイルのインポート中にエラーが発生しました: {error}',
requiredFields: 'タイトルとプロンプトは必須です',
editorNotFound: '{platform}のテキストエリアが見つかりません',
languageSettings: '🌐 言語設定',
fillPlaceholders: '情報を入力',
insert: '挿入',
enablePlaceholders: '動的プロンプト',
autoExecute: '自動送信',
infoTitle: 'ヘルプ',
infoDPDesc: '完全な利用ガイドを見る:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: '挿入後、手動操作なしでプロンプトをチャットに即座に送信します。',
search: 'プロンプトを検索...',
searchLanguage: '言語を検索...',
selectAll: 'すべて選択',
countPrompts: '{count} 件のプロンプト',
confirmDownloads: '{count} 個のテキストファイルをダウンロードしようとしています。続行しますか?',
noSearchResults: '検索条件に一致するプロンプトはありません。',
ffWarningTitle: '重要なお知らせ',
ffWarningText: 'ページのこの特定エリアでは、Firefox を使用している場合、<span style="color: #6a6cfc;"><strong>マイプロンプト</strong></span>が正しく動作できません。',
ffRecommend: 'Doubao のすべてのチャット領域で完全なサポートを受けるには、Chromium ベースのブラウザ(Chrome、Edge、Brave など)の使用を推奨します。',
dontShowAgain: '警告ボタンを削除',
select: 'オプションを選択してください:',
context: 'コンテキスト',
expand: '展開',
collapse: '折りたたむ',
idGroup: 'グループID',
uniqueSelection: '一意選択'
},
'ko': {
langName: '한국어',
prompt: '프롬프트',
prompts: '프롬프트',
newPrompt: '새 프롬프트',
editPrompt: '프롬프트 편집',
title: '제목',
text: '프롬프트',
save: '저장',
close: '닫기',
edit: '편집',
delete: '삭제',
noSavedPrompts: '저장된 프롬프트가 없습니다',
addPrompt: '프롬프트 추가',
import: '가져오기',
export: '내보내기',
confirmDelete: '프롬프트 "{title}"을(를) 삭제하시겠습니까?',
noPromptsToExport: '내보낼 프롬프트가 없습니다',
promptsImported: '프롬프트 {count}개를 성공적으로 가져왔습니다',
errorImporting: '파일 가져오기 오류: {error}',
requiredFields: '제목과 프롬프트는 필수 항목입니다',
editorNotFound: '{platform}의 텍스트 영역을 찾을 수 없습니다',
languageSettings: '🌐 언어 설정',
fillPlaceholders: '정보 채우기',
insert: '삽입',
enablePlaceholders: '동적 프롬프트',
autoExecute: '자동 전송',
infoTitle: '도움말',
infoDPDesc: '전체 사용 가이드 보기:<br><a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-english-md" target="_blank"><span style="color: #63b3ed;">English</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-portugues-br-md" target="_blank"><span style="color: #63b3ed;">Português (BR)</span></a> | <a href="https://gist.github.com/0H4S/109af9d102881cbc34b20d6f2bc196e7#file-md" target="_blank"><span style="color: #63b3ed;">简体中文</span></a>',
infoASDesc: '수동 조치 없이 삽입 후 즉시 프롬프트를 채팅으로 보냅니다.',
search: '프롬프트 검색...',
searchLanguage: '언어 검색...',
selectAll: '전체 선택',
countPrompts: '{count}개의 프롬프트',
confirmDownloads: '{count}개의 개별 텍스트 파일을 다운로드하시겠습니까?',
noSearchResults: '검색 결과와 일치하는 프롬프트가 없습니다.',
ffWarningTitle: '중요 안내',
ffWarningText: '페이지의 이 특정 영역에서는 Firefox 사용 시 <span style="color: #6a6cfc;"><strong>나의 프롬프트</strong></span>가 제대로 동작하지 않습니다.',
ffRecommend: 'Doubao의 모든 채팅 영역에서 완전한 지원을 받으려면 Chromium 기반 브라우저(Chrome, Edge, Brave 등)를 사용할 것을 권장합니다.',
dontShowAgain: '경고 버튼 제거',
select: '옵션을 선택하세요:',
context: '컨텍스트',
expand: '펼치기',
collapse: '접기',
idGroup: '그룹 ID',
uniqueSelection: '고유 선택'
}
};
const SCRIPT_CONFIG = {
notificationsUrl: 'https://gist.github.com/0H4S/b2f9a9f92259deadc35bdccb11cd9a75',
scriptVersion: '2.9',
};
const notifier = new ScriptNotifier(SCRIPT_CONFIG);
notifier.run();
const LANG_STORAGE_KEY = 'UserScriptLang';
const PROMPT_STORAGE_KEY = 'Prompts';
let currentLang = 'en';
let isInitialized = false;
let isInitializing = false;
let currentButton = null;
let currentPlatform = null;
let pageObserver = null;
let currentMenu = null;
let currentModal = null;
let languageModal = null;
let currentPlaceholderModal = null;
let infoModal = null;
const scriptPolicy = window.trustedTypes
? window.trustedTypes.createPolicy('MyPromptPolicy', { createHTML: (input) => input })
: null;
function setSafeInnerHTML(element, html) {
if (!element) return;
if (scriptPolicy) {
element.innerHTML = scriptPolicy.createHTML(html);
} else {
element.innerHTML = html;
}
}
function getTranslation(key, replacements = {}) {
let text = translations[currentLang]?.[key] || translations.en[key];
Object.entries(replacements).forEach(([p, v]) => text = text.replace(`{${p}}`, v));
return text;
}
async function determineLanguage() {
const savedLang = await GM_getValue(LANG_STORAGE_KEY);
if (savedLang && translations[savedLang]) {
currentLang = savedLang;
return;
}
const browserLang = (navigator.language || navigator.userLanguage).toLowerCase();
if (browserLang.startsWith('pt')) currentLang = 'pt-BR';
else if (browserLang.startsWith('zh')) currentLang = 'zh-CN';
else if (browserLang.startsWith('en')) currentLang = 'en';
else if (browserLang.startsWith('es')) currentLang = 'es';
else if (browserLang.startsWith('fr')) currentLang = 'fr';
else if (browserLang.startsWith('ru')) currentLang = 'ru';
else if (browserLang.startsWith('ja')) currentLang = 'ja';
else if (browserLang.startsWith('ko')) currentLang = 'ko';
else currentLang = 'en';
}
function waitFor(selector, timeout = 8000) {
return new Promise((resolve, reject) => {
const el = document.querySelector(selector);
if (el) { resolve(el); return; }
const timer = setTimeout(() => { obs.disconnect(); reject(`Timeout esperando por ${selector}`); }, timeout);
const obs = new MutationObserver(() => {
const target = document.querySelector(selector);
if (target) { clearTimeout(timer); obs.disconnect(); resolve(target); }
});
if (document.body) obs.observe(document.body, { childList: true, subtree: true });
else document.addEventListener('DOMContentLoaded', () => obs.observe(document.body, { childList: true, subtree: true }));
});
}
const debounce = (func, wait) => {
let timeout;
return (...args) => {
clearTimeout(timeout);
timeout = setTimeout(() => func.apply(this, args), wait);
};
};
async function getAll() { return await GM_getValue(PROMPT_STORAGE_KEY, []); }
async function addItem(item) {
const prompts = await getAll();
prompts.unshift(item);
await GM_setValue(PROMPT_STORAGE_KEY, prompts);
}
async function update(index, item) {
let prompts = await getAll();
if (prompts[index]) {
prompts[index] = item;
await GM_setValue(PROMPT_STORAGE_KEY, prompts);
}
}
async function remove(index) {
let prompts = await getAll();
prompts.splice(index, 1);
await GM_setValue(PROMPT_STORAGE_KEY, prompts);
}
function createCustomTooltip(button, text, position = 'top') {
let tooltipElement = null;
const showTooltip = () => {
if (tooltipElement) return;
tooltipElement = document.createElement('div');
tooltipElement.className = 'mp-tooltip';
tooltipElement.textContent = text;
document.body.appendChild(tooltipElement);
const btnRect = button.getBoundingClientRect();
const tooltipRect = tooltipElement.getBoundingClientRect();
let top;
const margin = 8;
if (position === 'bottom') {
top = btnRect.bottom + margin;
if (top + tooltipRect.height > window.innerHeight) {
top = btnRect.top - tooltipRect.height - margin;
}
} else {
top = btnRect.top - tooltipRect.height - margin;
if (top < 0) {
top = btnRect.bottom + margin;
}
}
let left = btnRect.left + (btnRect.width / 2) - (tooltipRect.width / 2);
if (left < 0) { left = margin; }
if (left + tooltipRect.width > window.innerWidth) { left = window.innerWidth - tooltipRect.width - margin; }
tooltipElement.style.left = `${left}px`;
tooltipElement.style.top = `${top}px`;
requestAnimationFrame(() => {
tooltipElement.classList.add('visible');
});
};
const hideTooltip = () => {
if (!tooltipElement) return;
const el = tooltipElement;
tooltipElement = null;
el.classList.remove('visible');
setTimeout(() => {
if (document.body.contains(el)) {
document.body.removeChild(el);
}
}, 150);
};
button.addEventListener('mouseenter', showTooltip);
button.addEventListener('mouseleave', hideTooltip);
button.addEventListener('mousedown', hideTooltip);
}
function injectGlobalStyles() {
const styleId = 'my-prompt-styles';
if (document.getElementById(styleId)) return;
const styleElement = document.createElement('style');
styleElement.id = styleId;
setSafeInnerHTML(styleElement, `
/* ---VARIÁVEIS E TEMA (CLARO / ESCURO)--- */
:root {
/* Tipografia */
--mp-font-family-base: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol" !important;
/* Cores Base (Modo Claro) */
--mp-bg-primary: #ffffff;
--mp-bg-secondary: #f8f9fa;
--mp-bg-tertiary: #f1f3f5;
--mp-bg-overlay: rgba(10, 10, 10, 0.5);
/* Texto */
--mp-text-primary: #212529;
--mp-text-secondary: #495057;
--mp-text-tertiary: #868e96;
/* Bordas */
--mp-border-primary: #dee2e6;
--mp-border-secondary: #ced4da;
/* Cores de Ação */
--mp-accent-primary: #7071fc;
--mp-accent-primary-hover: #595ac9;
--mp-accent-yellow: #fab005;
--mp-accent-yellow-hover: #f08c00;
--mp-accent-red: #f03e3e;
--mp-accent-red-hover: #c92a2a;
/* Sombras e Bordas */
--mp-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.04);
--mp-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.1);
--mp-shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.1);
--mp-border-radius-sm: 4px;
--mp-border-radius-md: 8px;
--mp-border-radius-lg: 16px;
/* Transições */
--mp-transition-fast: 0.2s cubic-bezier(0.25, 1, 0.5, 1);
}
@media (prefers-color-scheme: dark) {
:root {
/* Cores Base (Modo Escuro) */
--mp-bg-primary: #212529;
--mp-bg-secondary: #2c2c30;
--mp-bg-tertiary: #343a40;
--mp-bg-overlay: rgba(0, 0, 0, 0.7);
/* Texto */
--mp-text-primary: #f8f9fa;
--mp-text-secondary: #e9ecef;
--mp-text-tertiary: #adb5bd;
/* Bordas */
--mp-border-primary: #495057;
--mp-border-secondary: #868e96;
/* Sombras */
--mp-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.15);
--mp-shadow-md: 0 4px 12px rgba(0, 0, 0, 0.25);
--mp-shadow-lg: 0 10px 30px rgba(0, 0, 0, 0.3);
}
}
/* ---UTILITÁRIOS GERAIS--- */
.mp-hidden {
display: none !important;
}
/* ---SISTEMA DE SCROLL--- */
.mp-scroll-invisible {
overflow-y: auto !important;
scrollbar-width: none !important; /* Firefox */
-ms-overflow-style: none !important; /* IE/Edge */
scroll-behavior: smooth;
}
/* Remove scrollbar no Chrome/Safari/Webkit */
.mp-scroll-invisible::-webkit-scrollbar {
display: none;
width: 0;
height: 0;
}
/* Wrapper que envolve a lista e contém as setas absolutas */
.mp-scroll-wrapper {
position: relative;
display: flex;
flex-direction: column;
flex: 1;
overflow: hidden;
min-height: 0;
max-width: 100%;
box-sizing: border-box;
}
/* Estilo Base das Setas */
.mp-scroll-arrow {
position: absolute;
left: 0;
right: 0;
height: 28px;
display: flex;
align-items: center;
justify-content: center;
color: var(--mp-text-tertiary);
cursor: pointer;
opacity: 0;
pointer-events: none;
transition: opacity 0.2s ease, color 0.2s ease;
z-index: 10;
}
/* Seta Superior */
.mp-scroll-arrow.up {
top: 0;
background: linear-gradient(to bottom, var(--scroll-bg, var(--mp-bg-primary)) 30%, transparent);
}
/* Seta Inferior */
.mp-scroll-arrow.down {
bottom: 0;
background: linear-gradient(to top, var(--scroll-bg, var(--mp-bg-primary)) 30%, transparent);
}
/* Estados da Seta */
.mp-scroll-arrow:hover {
color: var(--mp-accent-primary);
}
.mp-scroll-arrow.visible {
opacity: 1;
pointer-events: auto;
}
/* Ícone SVG da Seta */
.mp-scroll-arrow svg {
width: 20px;
height: 20px;
filter: drop-shadow(0 1px 2px rgba(0, 0, 0, 0.1));
}
/* --- AJUSTE ESPECÍFICO PARA O TEXTAREA DO EDITOR DE PROMPT --- */
#AB_modal_box_el #__ap_text,
#prompt-menu-container #__ap_text,
.mp-modal-box .form-group:has(#__ap_text) .form-textarea {
border: none !important;
box-shadow: none !important;
background-color: transparent !important;
padding: 10px;
width: 100%;
height: 100%;
}
.mp-modal-box .form-group:has(#__ap_text) .mp-scroll-wrapper {
border: 1px solid var(--mp-border-primary);
border-radius: var(--mp-border-radius-md);
background-color: var(--mp-bg-secondary);
transition: border-color 0.2s, box-shadow 0.2s;
overflow: hidden !important;
display: flex;
flex-direction: column;
height: 300px;
}
.mp-modal-box .form-group:has(#__ap_text) .mp-scroll-wrapper:focus-within {
border-color: var(--mp-accent-primary);
box-shadow: 0 0 0 3px color-mix(in srgb, var(--mp-accent-primary) 25%, transparent);
}
.mp-modal-box.mp-expanded .form-group:has(#__ap_text) .mp-scroll-wrapper {
height: 100% !important;
}
/* ---OVERLAY E MODAL PRINCIPAL--- */
.mp-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background-color: var(--mp-bg-overlay);
z-index: 2147483647;
display: flex;
justify-content: center;
align-items: center;
backdrop-filter: blur(4px);
opacity: 0;
visibility: hidden;
transition: opacity var(--mp-transition-fast), visibility var(--mp-transition-fast);
}
.mp-overlay.visible {
opacity: 1;
visibility: visible;
}
.mp-modal-box {
font-family: var(--mp-font-family-base) !important;
background-color: var(--mp-bg-primary);
color: var(--mp-text-primary);
border-radius: var(--mp-border-radius-lg);
padding: 24px;
box-shadow: var(--mp-shadow-lg);
width: min(90vw, 520px);
border: 1px solid var(--mp-border-primary);
transform: scale(0.95) translateY(10px);
opacity: 0;
transition: transform var(--mp-transition-fast), opacity var(--mp-transition-fast), width 0.3s cubic-bezier(0.4, 0, 0.2, 1), height 0.3s cubic-bezier(0.4, 0, 0.2, 1);
position: relative;
display: flex;
flex-direction: column;
max-height: 85vh;
}
.mp-modal-box.mp-expanded {
width: 900px !important;
max-width: 95vw !important;
height: 85vh !important;
display: flex;
flex-direction: column;
}
.mp-modal-box.mp-expanded .modal-title {
display: block !important;
visibility: visible !important;
text-align: center;
margin-bottom: 20px;
flex-shrink: 0;
}
.mp-modal-box.mp-expanded .form-group:has(.form-textarea) {
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
margin-bottom: 24px;
}
.mp-modal-box.mp-expanded .mp-scroll-wrapper {
flex: 1;
height: 100% !important;
}
.mp-modal-box.mp-expanded .form-textarea {
height: 100% !important;
}
.mp-modal-box.mp-expanded .mp-switch-container {
padding-top: 10px;
}
.mp-overlay.visible .mp-modal-box {
transform: scale(1) translateY(0);
opacity: 1;
}
.modal-title {
font-size: 18px;
font-weight: 600;
margin: 0 0 20px;
text-align: center;
color: var(--mp-text-primary);
flex-shrink: 0;
}
.modal-footer {
display: flex;
justify-content: center;
margin-top: 16px;
flex-shrink: 0;
}
/* Botões de Fechar e Info */
.mp-modal-close-btn,
.mp-modal-info-btn {
position: absolute;
top: 12px;
background: none;
border: none;
color: var(--mp-text-tertiary);
cursor: pointer;
width: 32px;
height: 32px;
border-radius: 50%;
transition: transform 0.3s ease, color 0.3s ease, background-color 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
z-index: 20;
}
.mp-modal-close-btn {
right: 12px;
}
.mp-modal-info-btn {
right: 88px;
}
.mp-modal-close-btn:hover {
transform: rotate(90deg);
color: var(--mp-accent-red);
background-color: color-mix(in srgb, var(--mp-accent-red) 15%, transparent);
}
.mp-modal-info-btn:hover {
transform: scale(1.1);
color: var(--mp-accent-primary);
background-color: color-mix(in srgb, var(--mp-accent-primary) 15%, transparent);
}
.mp-modal-close-btn svg,
.mp-modal-info-btn svg {
width: 20px;
height: 20px;
stroke: currentColor;
stroke-width: 2.5;
fill: none;
}
.mp-modal-info-btn svg {
stroke-width: 0;
fill: currentColor;
}
/* Estilo do Botão de Expandir */
.mp-modal-expand-btn {
position: absolute;
top: 12px;
right: 50px;
background: none;
border: none;
color: var(--mp-text-tertiary);
cursor: pointer;
width: 32px;
height: 32px;
border-radius: 50%;
transition: transform 0.3s ease, color 0.3s ease, background-color 0.3s ease;
display: flex;
justify-content: center;
align-items: center;
padding: 0;
z-index: 20;
}
.mp-modal-expand-btn:hover {
transform: scale(1.1);
color: var(--mp-accent-primary);
background-color: color-mix(in srgb, var(--mp-accent-primary) 15%, transparent);
}
.mp-modal-expand-btn svg {
width: 20px;
height: 20px;
stroke: currentColor;
stroke-width: 2;
fill: none;
}
/* --- HELPERS E TOOLTIPS DE CONTEXTO --- */
.mp-label-wrapper {
display: flex;
align-items: center;
gap: 6px;
margin-bottom: 6px;
width: 100%;
}
.mp-help-icon {
color: var(--mp-accent-primary);
cursor: pointer;
display: flex;
align-items: center;
transition: transform 0.2s;
opacity: 0.8;
}
.mp-help-icon:hover {
transform: scale(1.1);
opacity: 1;
}
.mp-help-icon svg {
width: 15px;
height: 15px;
fill: currentColor;
}
.mp-context-bubble {
display: none;
background-color: var(--mp-bg-tertiary);
border-left: 3px solid var(--mp-accent-primary);
padding: 8px 12px;
border-radius: 0 var(--mp-border-radius-sm) var(--mp-border-radius-sm) 0;
margin-bottom: 12px;
font-size: 13px;
color: var(--mp-text-secondary);
line-height: 1.4;
animation: mp-fade-in-down 0.2s ease-out forwards;
width: 100%;
box-sizing: border-box;
}
.mp-context-bubble.visible {
display: block;
}
.mp-context-bubble strong {
color: var(--mp-text-primary);
font-weight: 600;
}
@keyframes mp-fade-in-down {
from {
opacity: 0;
transform: translateY(-5px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
/* ---MENU FLUTUANTE DE PROMPTS--- */
.prompt-menu {
position: fixed;
min-width: 320px;
max-width: 420px;
background-color: var(--mp-bg-primary);
border: 1px solid var(--mp-border-primary);
border-radius: var(--mp-border-radius-lg);
box-shadow: var(--mp-shadow-lg);
z-index: 2147483647;
display: flex;
flex-direction: column;
user-select: none;
color: var(--mp-text-primary) !important;
font-family: var(--mp-font-family-base) !important;
overflow: hidden;
opacity: 0;
visibility: hidden;
transform: scale(0.95);
transform-origin: top left;
transition: opacity 0.2s ease, transform 0.2s ease, visibility 0s linear 0.2s;
}
.prompt-menu.visible {
opacity: 1;
visibility: visible;
transform: scale(1);
transition-delay: 0s;
}
.prompt-menu-list {
max-height: 220px;
padding: 4px;
}
.prompt-item-row {
display: flex;
align-items: center;
justify-content: space-between;
padding: 8px 12px;
border-radius: var(--mp-border-radius-md);
cursor: pointer;
transition: background-color 0.15s ease-in-out;
}
.prompt-item-row:hover {
background-color: var(--mp-bg-tertiary);
}
.prompt-title {
font-size: 14px;
font-weight: 500;
flex: 1;
padding-right: 12px;
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
color: var(--mp-text-secondary);
}
.prompt-item-row:hover .prompt-title {
color: var(--mp-text-primary);
}
.prompt-actions {
display: flex;
align-items: center;
gap: 4px;
}
.action-btn {
background: transparent;
border: none;
cursor: pointer;
padding: 6px;
border-radius: var(--mp-border-radius-sm);
transition: background-color 0.15s ease-in-out, color 0.15s ease-in-out;
display: flex;
align-items: center;
justify-content: center;
line-height: 0;
color: var(--mp-text-secondary);
}
.action-btn svg {
width: 16px;
height: 16px;
display: block;
}
.action-btn.edit:hover {
background-color: var(--mp-bg-tertiary);
color: var(--mp-accent-yellow);
}
.action-btn.delete:hover {
background-color: var(--mp-bg-tertiary);
color: var(--mp-accent-red);
}
/* Rodapé do Menu */
.menu-footer-grid {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
border-top: 1px solid var(--mp-border-primary);
background-color: var(--mp-bg-secondary);
flex-shrink: 0;
}
.menu-footer-btn {
display: flex;
align-items: center;
justify-content: center;
background: transparent;
border: none;
cursor: pointer;
padding: 12px 0;
color: var(--mp-text-secondary);
transition: all 0.2s ease;
height: auto;
}
.menu-footer-btn:not(:last-child) {
border-right: 1px solid var(--mp-border-primary);
}
.menu-footer-btn svg {
width: 20px;
height: 20px;
transition: transform 0.2s cubic-bezier(0.34, 1.56, 0.64, 1);
}
.menu-footer-btn:hover svg {
transform: scale(1.2);
}
.menu-footer-btn.btn-export:hover {
background-color: rgba(34, 129, 207, 0.1);
color: #2281cfff;
}
.menu-footer-btn.btn-add:hover {
background-color: rgba(32, 201, 97, 0.1);
color: #20c961ff;
transform: none;
}
.menu-footer-btn.btn-add:hover svg {
transform: scale(1.4);
}
.menu-footer-btn.btn-import:hover {
background-color: rgba(253, 126, 20, 0.1);
color: #fd7e14;
}
/* ---FORMULÁRIOS E INPUTS--- */
.form-group {
display: flex;
flex-direction: column;
margin-bottom: 16px;
flex-shrink: 0;
}
.form-label {
margin-bottom: 8px;
font-size: 13px !important;
font-weight: 500 !important;
color: var(--mp-text-secondary);
display: block;
}
.form-input,
.form-textarea {
background-color: var(--mp-bg-secondary) !important;
color: var(--mp-text-primary) !important;
border: 1px solid var(--mp-border-primary) !important;
border-radius: var(--mp-border-radius-md);
padding: 10px;
width: 100%;
box-sizing: border-box;
transition: border-color 0.2s, box-shadow 0.2s;
outline: 0 !important;
font-family: var(--mp-font-family-base) !important;
font-size: 14px !important;
}
.form-textarea {
height: 300px !important;
resize: none !important;
display: block;
}
.form-input:focus,
.form-textarea:focus {
border-color: var(--mp-accent-primary) !important;
box-shadow: 0 0 0 3px color-mix(in srgb, var(--mp-accent-primary) 25%, transparent) !important;
}
.form-input::placeholder,
.form-textarea::placeholder,
.mp-search-input::placeholder,
.menu-search-input::placeholder,
.lang-search-input::placeholder {
color: var(--mp-text-tertiary) !important;
opacity: 0.7;
}
/* Switches (Checkbox estilo Toggle) */
.mp-switch-container {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 4px 0 4px;
margin: -8px 0 24px;
flex-shrink: 0;
}
.mp-switch {
display: flex;
align-items: center;
gap: 8px;
}
.mp-switch input[type="checkbox"] {
height: 0;
width: 0;
visibility: hidden;
position: absolute;
}
.mp-switch label {
cursor: pointer;
text-indent: -9999px;
width: 40px;
height: 22px;
background: var(--mp-bg-tertiary);
display: block;
border-radius: 100px;
position: relative;
transition: background-color var(--mp-transition-fast);
}
.mp-switch label:after {
content: '';
position: absolute;
top: 3px;
left: 3px;
width: 16px;
height: 16px;
background: #fff;
border-radius: 90px;
transition: 0.3s cubic-bezier(.25, 1, .5, 1);
box-shadow: var(--mp-shadow-sm);
}
.mp-switch input:checked+label {
background: var(--mp-accent-primary);
}
.mp-switch input:checked+label:after {
left: calc(100% - 3px);
transform: translateX(-100%);
}
.mp-switch .switch-text {
font-size: 13px;
font-weight: 500;
color: var(--mp-text-secondary);
cursor: pointer;
user-select: none;
}
/* Checkboxe */
.mp-checkbox, .mp-option-item input[type="checkbox"] {
-webkit-appearance: none !important;
appearance: none !important;
width: 18px !important;
height: 18px !important;
border: 1px solid var(--mp-border-primary) !important;
border-radius: var(--mp-border-radius-sm) !important;
background-color: var(--mp-bg-secondary) !important;
cursor: pointer !important;
margin: 0 !important;
display: grid !important;
place-content: center !important;
transition: all 0.2s ease;
}
.mp-checkbox:checked, .mp-option-item input[type="checkbox"]:checked {
background-color: var(--mp-accent-primary) !important;
border-color: var(--mp-accent-primary) !important;
}
.mp-checkbox::before, .mp-option-item input[type="checkbox"]::before {
content: "";
width: 10px;
height: 10px;
clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
background-color: #ffffff;
transform: scale(0);
transition: 0.15s transform ease-in-out;
}
.mp-checkbox:checked::before, .mp-option-item input[type="checkbox"]:checked::before {
transform: scale(1);
}
/* Campos de Busca */
.mp-search-container,
.menu-search-container {
position: sticky;
top: 0;
z-index: 10;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.menu-search-container {
padding: 10px 12px;
background-color: var(--mp-bg-secondary);
border-bottom: 1px solid var(--mp-border-primary);
}
.mp-search-input,
.menu-search-input,
.lang-search-input {
width: 100%;
padding: 10px 12px;
border-radius: var(--mp-border-radius-md);
border: 1px solid var(--mp-border-primary);
background-color: var(--mp-bg-secondary);
color: var(--mp-text-primary);
font-family: var(--mp-font-family-base) !important;
font-size: 13px;
box-sizing: border-box;
outline: none;
transition: border-color 0.2s;
}
.menu-search-input {
background-color: var(--mp-bg-primary) !important;
}
.lang-search-input {
margin-bottom: 12px;
}
.mp-search-input:focus,
.menu-search-input:focus,
.lang-search-input:focus {
border-color: var(--mp-accent-primary);
}
/* ---MENUS ESPECÍFICOS (EXPORTAÇÃO, DINÂMICO, IDIOMAS)--- */
/* Exportação */
.mp-export-actions {
display: flex;
justify-content: space-between;
align-items: center;
margin-top: 20px;
margin-bottom: 20px;
font-size: 13px;
color: var(--mp-text-secondary);
border-bottom: 1px solid var(--mp-border-primary);
padding-bottom: 16px;
}
.mp-checkbox-wrapper {
display: flex;
align-items: center;
cursor: pointer;
user-select: none;
}
.mp-export-list {
display: flex;
flex-direction: column;
gap: 4px;
margin: 0 -8px;
padding: 0 8px;
}
.mp-export-item {
display: flex;
align-items: center;
padding: 8px;
border-radius: var(--mp-border-radius-md);
transition: background 0.15s;
cursor: pointer;
border: 1px solid transparent;
}
.mp-export-item:hover {
background-color: var(--mp-bg-tertiary);
border-color: var(--mp-border-primary);
}
.mp-item-content {
display: flex;
flex-direction: column;
overflow: hidden;
margin-left: 12px;
}
.mp-item-title {
font-size: 14px;
font-weight: 500;
color: var(--mp-text-primary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.mp-item-preview {
font-size: 12px;
color: var(--mp-text-tertiary);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
margin-top: 2px;
}
.mp-export-buttons {
display: flex;
gap: 10px;
margin-top: 20px;
justify-content: flex-end;
border-top: 1px solid var(--mp-border-primary);
padding-top: 16px;
flex-shrink: 0;
}
/* Dinâmico */
#__ap_placeholders_container {
padding: 4px;
margin-top: 15px;
box-sizing: border-box;
transition: padding-top 0.2s ease;
}
.mp-option-group {
display: flex;
flex-direction: column;
gap: 4px;
margin-bottom: 12px;
padding: 8px;
border: 1px solid var(--mp-border-primary);
border-radius: var(--mp-border-radius-md);
background-color: var(--mp-bg-tertiary);
max-height: none !important;
overflow: visible !important;
}
.mp-option-item {
display: flex;
align-items: center;
gap: 10px;
font-size: 13px;
cursor: pointer;
padding: 8px 8px 8px 12px !important;
border-radius: var(--mp-border-radius-sm);
background-color: var(--mp-bg-primary);
transition: background-color 0.2s;
user-select: none;
border-left: 5px solid transparent;
position: relative;
}
.mp-option-item:hover {
background-color: var(--mp-bg-secondary);
}
/* Modo Expandido */
.mp-modal-box.mp-expanded #__ap_placeholders_container {
max-height: none !important;
height: 100% !important;
flex: 1;
display: flex;
flex-direction: column;
min-height: 0;
}
.mp-modal-box.mp-expanded #__ap_placeholders_container .mp-scroll-wrapper {
height: 100% !important;
flex: 1;
}
.dynamic-input {
min-height: 45px !important;
line-height: 1.5;
}
/* Idiomas */
.lang-box {
width: min(90vw, 320px);
}
.lang-button {
all: unset;
box-sizing: border-box;
display: block;
width: 100%;
padding: 12px 20px;
border-radius: var(--mp-border-radius-md);
background-color: var(--mp-bg-secondary);
color: var(--mp-text-primary);
border: 1px solid var(--mp-border-primary);
font-weight: 500;
cursor: pointer;
text-align: center;
transition: all 0.2s ease;
font-family: var(--mp-font-family-base) !important;
flex-shrink: 0;
}
.lang-button:hover {
transform: translateY(-2px);
box-shadow: var(--mp-shadow-sm);
background-color: var(--mp-bg-tertiary);
}
.lang-button.selected {
border-color: var(--mp-accent-primary);
color: var(--mp-accent-primary);
background-color: color-mix(in srgb, var(--mp-accent-primary) 5%, transparent);
font-weight: 600;
}
/* ---BOTÕES PRINCIPAIS--- */
.save-button {
padding: 10px 28px;
border-radius: var(--mp-border-radius-md);
background-color: var(--mp-accent-primary);
color: #fff;
border: none;
font-weight: 600;
cursor: pointer;
transition: all 0.2s ease-in-out;
font-family: var(--mp-font-family-base) !important;
}
.save-button:hover {
background-color: var(--mp-accent-primary-hover);
transform: translateY(-1px);
}
.mp-btn-secondary {
background: transparent;
border: 1px solid var(--mp-border-secondary);
color: var(--mp-text-secondary);
}
.mp-btn-secondary:hover {
background-color: var(--mp-bg-tertiary);
color: var(--mp-text-primary);
}
/* ---TABELA DE INFORMAÇÕES--- */
.mp-info-table {
display: flex;
flex-direction: column;
border: 1px solid var(--mp-border-primary);
border-radius: var(--mp-border-radius-md);
overflow: hidden;
margin-top: 8px;
}
.mp-info-row {
display: flex;
text-align: center;
}
.mp-info-row:not(:last-child) {
border-bottom: 1px solid var(--mp-border-primary);
}
.mp-info-col {
padding: 16px;
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
}
.mp-info-col:not(:last-child) {
border-right: 1px solid var(--mp-border-primary);
}
.mp-info-col h3 {
font-size: 14px;
font-weight: 600;
color: var(--mp-text-primary);
margin: 0 0 8px;
}
.mp-info-col p {
font-size: 13px;
color: var(--mp-text-secondary);
line-height: 1.5;
margin: 0;
}
/* ---EXTRAS (Tooltips, Empty States, Animações)--- */
.mp-tooltip {
position: fixed;
z-index: 2147483647;
border-radius: var(--mp-border-radius-sm);
padding: 6px 12px;
pointer-events: none;
white-space: nowrap;
font-family: var(--mp-font-family-base) !important;
font-size: 14px;
font-weight: 500;
background-color: var(--mp-text-primary);
color: var(--mp-bg-primary);
box-shadow: var(--mp-shadow-md);
opacity: 0;
transform: scale(0.95);
transition: opacity 150ms cubic-bezier(0.4, 0, 0.2, 1), transform 150ms cubic-bezier(0.4, 0, 0.2, 1);
}
.mp-tooltip.visible {
opacity: 1;
transform: scale(1);
}
.empty-state {
padding: 24px 16px;
text-align: center;
color: var(--mp-text-tertiary);
font-size: 14px;
}
@keyframes mp-fade-in-up {
from {
opacity: 0;
transform: translateY(10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
`);
document.head.appendChild(styleElement);
}
// #endregion GLOBAL
// #region BOTÕES DE PROMPT
// ---CHATGPT---
function createChatGPTButton() {
const btn = document.createElement('button');
btn.type = 'button';
btn.setAttribute('data-testid', 'composer-button-prompts');
btn.className = 'composer-btn';
setSafeInnerHTML(btn, `<svg width="20" height="20" viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"/></svg>`);
createCustomTooltip(btn, getTranslation('prompts'));
return btn;
}
// ---DEEPSEEK---
function createDeepSeekButton() {
const btn = document.createElement('button');
btn.setAttribute('role', 'button');
btn.setAttribute('aria-disabled', 'false');
btn.setAttribute('tabindex', '0');
btn.style.transform = 'translateZ(0px)';
btn.setAttribute('data-testid', 'composer-button-prompts');
const innerHTML = `<div class="ds-icon ds-atom-button__icon" style="font-size: 14px; width: 14px; height: 14px; color: var(--dsw-alias-label-primary); margin-right: 0px;"><svg width="20" height="20" fill="currentColor" viewBox="0 0 20 20"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"></path></svg></div><span class=""><span class="_6dbc175">${getTranslation('prompt')}</span></span>`;
setSafeInnerHTML(btn, innerHTML);
return btn;
}
// ---GOOGLE AI STUDIO---
function createGoogleStudioButton() {
const styleId = 'my-prompt-gstudio-hover-fix';
if (!document.getElementById(styleId)) {
const styleElement = document.createElement('style');
styleElement.id = styleId;
setSafeInnerHTML(styleElement, `button[data-testid="composer-button-prompts"]:hover { background-color: rgba(60, 64, 67, 0.08) !important; } @media (prefers-color-scheme: dark) { button[data-testid="composer-button-prompts"]:hover { background-color: rgba(232, 234, 237, 0.08) !important; } } `);
document.head.appendChild(styleElement);
}
const btn = document.createElement('button');
btn.setAttribute('data-testid', 'composer-button-prompts');
btn.type = 'button';
btn.style.backgroundColor = 'transparent';
btn.style.border = 'none';
btn.style.boxShadow = 'none';
btn.style.borderRadius = '50%';
btn.style.width = '48px';
btn.style.height = '48px';
btn.style.padding = '0';
btn.style.margin = '0';
btn.style.cursor = 'pointer';
btn.style.display = 'inline-flex';
btn.style.alignItems = 'center';
btn.style.justifyContent = 'center';
btn.style.transition = 'background-color 150ms ease-in-out';
setSafeInnerHTML(btn, `<svg fill="currentColor" style="width: 24px; height: 24px;" viewBox="0 0 100 100"><g><path d="M17.563,30.277h0.012c0,1.245,1.004,2.254,2.246,2.267v0.002h60.359v-0.001c1.248-0.006,2.259-1.018,2.259-2.268h0.01 l0-10.459h0c-0.002-1.251-1.017-2.265-2.269-2.265l0,0H19.821v0c0,0,0,0,0,0c-1.253,0-2.269,1.017-2.269,2.269 c0,0.039,0.01,0.076,0.012,0.115L17.563,30.277z"/><path d="M80.179,42.504L80.179,42.504H19.821v0c0,0,0,0,0,0c-1.253,0-2.269,1.017-2.269,2.269c0,0.039,0.01,0.076,0.012,0.115 l0,10.34h0.012c0,1.245,1.004,2.254,2.246,2.267v0.002h60.359v-0.001c1.248-0.006,2.259-1.018,2.259-2.268h0.01l0-10.459h0 C82.446,43.518,81.431,42.504,80.179,42.504z"/><path d="M80.179,67.454L80.179,67.454H19.821l0,0c0,0,0,0,0,0c-1.253,0-2.269,1.017-2.269,2.269c0,0.039,0.01,0.076,0.012,0.115 l0,10.34h0.012c0,1.245,1.004,2.254,2.246,2.267v0.002h60.359v-0.001c1.248-0.006,2.259-1.019,2.259-2.269h0.01l0-10.459h0 C82.446,68.468,81.431,67.454,80.179,67.454z"/></g></svg>`);
createCustomTooltip(btn, getTranslation('prompts'));
return btn;
}
// ---QWEN---
function createQwenButton() {
const btn = document.createElement('div');
btn.className = 'chat-input-feature-btn';
btn.setAttribute('data-testid', 'composer-button-prompts');
btn.style.cursor = 'pointer';
setSafeInnerHTML(btn, `<span class="chat-input-feature-btn-icon"><svg width="1em" height="1em" viewBox="0 0 20 20" fill="currentColor" style="font-size: 16px;"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"/></svg></span><span class="chat-input-feature-btn-text">${getTranslation('prompt')}</span>`);
return btn;
}
// ---Z.AI---
function createZaiButton() {
const btnWrapper = document.createElement('div');
setSafeInnerHTML(btnWrapper, `<button type="button" class="px-2 @xl:px-3 py-1.5 flex gap-1.5 items-center text-sm rounded-lg border transition-colors duration-300 focus:outline-hidden max-w-full overflow-hidden bg-transparent dark:text-gray-300 border-[#E5E5E5] dark:border-[#3C3E3F] hover:bg-black/5 dark:hover:bg-white/5"><svg class="size-4" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 20 20"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"></path></svg><span class="hidden @sm:block whitespace-nowrap overflow-hidden text-ellipsis translate-y-[0.5px] mr-0.5">${getTranslation('prompt')}</span></button>`);
const btn = btnWrapper.firstElementChild;
btn.setAttribute('data-testid', 'composer-button-prompts');
return btn;
}
// ---GEMINI---
function createGeminiButton() {
const btn = document.createElement('button');
btn.setAttribute('data-testid', 'composer-button-prompts');
btn.className = 'mdc-icon-button mat-mdc-icon-button mat-mdc-button-base mat-primary mat-mdc-tooltip-trigger';
const svgHTML = `<span class="mat-mdc-button-persistent-ripple mdc-icon-button__ripple"></span><span class="mat-icon notranslate" style="display: inline-flex; align-items: center; justify-content: center;"><svg style="width: 24px; height: 24px;" viewBox="0 0 20 20" fill="currentColor"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"></path></svg></span><span class="mat-focus-indicator"></span><span class="mat-mdc-button-touch-target"></span><span class="mat-ripple mat-mdc-button-ripple"></span>`;
setSafeInnerHTML(btn, svgHTML);
createCustomTooltip(btn, getTranslation('prompt'), 'bottom');
return btn;
}
// ---LMARENA---
function createLmarenaButton() {
const btn = document.createElement('button');
btn.setAttribute('data-testid', 'composer-button-prompts');
btn.className = 'inline-flex items-center justify-center gap-2 whitespace-nowrap text-sm font-medium focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ring-offset-2 focus-visible:ring-offset-surface-primary disabled:pointer-events-none disabled:opacity-50 text-interactive-active border border-border-faint bg-transparent hover:text-interactive-normal active:text-text-tertiary h-8 w-8 p-2 rounded-md active:scale-[0.96] active:transition-transform active:duration-75 transition-colors duration-150 ease-out hover:shadow-sm hover:bg-interactive-normal/10 hover:border-interactive-normal/10';
btn.type = 'button';
setSafeInnerHTML(btn, `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 20 20" fill="currentColor" class="w-4 h-4"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"/></svg>`);
createCustomTooltip(btn, getTranslation('prompt'));
return btn;
}
// ---KIMI---
function createKimiButton() {
const div = document.createElement('div');
div.className = 'ok-computer-switch';
div.setAttribute('data-v-8e6d90bb', '');
div.setAttribute('data-testid', 'composer-button-prompts');
div.style.cursor = 'pointer';
div.style.display = 'flex';
div.style.alignItems = 'center';
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
svg.setAttribute('data-v-8e6d90bb', '');
svg.setAttribute('xmlns', 'http://www.w3.org/2000/svg');
svg.setAttribute('width', '1em');
svg.setAttribute('height', '1em');
svg.setAttribute('viewBox', '0 0 20 20');
svg.setAttribute('class', 'iconify icon');
svg.setAttribute('fill', 'currentColor');
setSafeInnerHTML(svg, `<path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2" fill="none"/>`);
const span = document.createElement('span');
span.setAttribute('data-v-8e6d90bb', '');
span.textContent = getTranslation('prompt');
div.appendChild(svg);
div.appendChild(span);
return div;
}
// ---CLAUDE---
function createClaudeButton() {
const wrapper = document.createElement('div');
wrapper.className = 'relative shrink-0';
const btnHTML = `<button class="border-0.5 transition-all h-8 w-8 rounded-lg flex items-center justify-center group !pointer-events-auto !outline-offset-1 text-text-300 border-border-300 hover:text-text-200/90 hover:bg-bg-100 active:scale-[0.98]" data-testid="composer-button-prompts" type="button"><svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 20 20"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"></path></svg></button>`;
setSafeInnerHTML(wrapper, btnHTML);
const btn = wrapper.querySelector('button');
createCustomTooltip(btn, getTranslation('prompts'));
return wrapper;
}
// ---GROK---
function createGrokButton() {
const btn = document.createElement('button');
btn.type = 'button';
btn.setAttribute('data-testid', 'composer-button-prompts');
const btnHTML = `<svg width="18" height="18" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" class="stroke-[2] text-primary transition-colors duration-100"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor"></path></svg>`;
setSafeInnerHTML(btn, btnHTML);
createCustomTooltip(btn, getTranslation('prompts'));
return btn;
}
// ---PERPLEXITY---
function createPerplexityButton() {
const span = document.createElement('span');
span.innerHTML = `<button data-testid="composer-button-prompts" type="button" class="focus-visible:bg-subtle hover:bg-subtle text-quiet hover:text-foreground dark:hover:bg-subtle px-[4px] font-sans focus:outline-none outline-none outline-transparent transition duration-300 ease-out select-none items-center relative group/button font-semimedium justify-center text-center items-center rounded-lg cursor-pointer active:scale-[0.97] active:duration-150 active:ease-outExpo origin-center whitespace-nowrap inline-flex text-sm h-8 aspect-[9/8]" aria-label="Prompts"><div class="flex items-center min-w-0 gap-two justify-center"><div><div class="isolate my-xs flex items-center"><div class="flex items-center"><div class="relative flex items-center justify-center rounded-full size-5" style="z-index: 1;"><div class="flex size-5 items-center justify-center"><svg xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2.5" stroke-linecap="round" stroke-linejoin="round" class="inline-flex tabler-icon"><path d="M4 6h16M4 12h16M4 18h16" /></svg></div></div></div></div></div></div></button>`;
const innerBtn = span.querySelector('button');
createCustomTooltip(innerBtn, getTranslation('prompts'));
return span;
}
/* ---DOUBAO---
=================================================
特别感谢 @xiaolongmr 的贡献!
了解他的工作:https://greasyfork.org/users/1005301
感谢合作!🇧🇷🤝🇨🇳
=================================================
*/
function createDoubaoButton(variant = 'default') {
const wrapper = document.createElement('div');
wrapper.className = '';
let classes = "semi-button medium-VC3b8a icon-hKywyK icon-only-BL0gii button-EWcT9p semi-button-with-icon semi-button-with-icon-only";
let extraStyle = "";
if (variant === 'minimal') {
classes += " semi-button-tertiary semi-button-borderless";
extraStyle = "color: #070707ff;";
} else {
classes += " semi-button-primary samantha-button-BghSMg tertiary-o1h39p";
}
const btnHTML = `<button tabindex="0" data-testid="composer-button-prompts" class="${classes}" type="button" aria-disabled="false" style="${extraStyle}"><span class="semi-button-content"><span role="img" class="semi-icon semi-icon-default block !text-16"><svg xmlns="http://www.w3.org/2000/svg" width="1em" height="1em" fill="none" viewBox="0 0 24 24"><path d="M4 6h16M4 12h16M4 18h16" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"/></svg></span></span></button>`;
setSafeInnerHTML(wrapper, btnHTML);
const btn = wrapper.querySelector('button');
createCustomTooltip(btn, getTranslation('prompts'));
return wrapper;
}
// ---BOTÃO DE AVISO DOUBAO FIREFOX---
function createDoubaoWarningButton(variant = 'default') {
const wrapper = document.createElement('div');
let classes = "semi-button medium-VC3b8a icon-hKywyK icon-only-BL0gii button-EWcT9p semi-button-with-icon semi-button-with-icon-only";
let extraStyle = "color: #f97316 !important;";
if (variant === 'minimal') {
classes += " semi-button-tertiary semi-button-borderless";
} else {
classes += " semi-button-primary samantha-button-BghSMg tertiary-o1h39p";
}
const btnHTML = `<button tabindex="0" data-testid="composer-button-warning" data-is-warning="true" class="${classes}" type="button" aria-disabled="false" style="${extraStyle}"><span class="semi-button-content"><span role="img" class="semi-icon semi-icon-default block !text-16"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill="currentColor" d="M19.59 15.86 12.01 1.92C11.5 1.02 10.78.5 9.99.5c-.8 0-1.52.52-2.02 1.43L.41 15.86c-.5.9-.55 1.83-.14 2.53.4.7 1.22 1.11 2.22 1.11h15.02c1 0 1.81-.4 2.22-1.1.4-.71.35-1.63-.14-2.54M10 4.86c.4 0 .72.32.72.72v6.59c0 .4-.33.73-.72.73a.7.7 0 0 1-.71-.73V5.58c0-.39.32-.72.71-.72m0 11.62c-.62 0-1.11-.5-1.11-1.14s.5-1.14 1.11-1.14 1.11.51 1.11 1.14-.5 1.14-1.11 1.14"/></svg></span></span></button>`;
setSafeInnerHTML(wrapper, btnHTML);
const btn = wrapper.querySelector('button');
createCustomTooltip(btn, getTranslation('ffWarningTitle'));
return wrapper;
}
// ---LONGCAT---
function createLongCatButton() {
const wrapper = document.createElement('div');
wrapper.className = 'upload-button-content';
wrapper.setAttribute('data-v-625d7fb6', '');
wrapper.setAttribute('data-v-6f28d2aa', '');
wrapper.setAttribute('data-v-4eeb4222-s', '');
wrapper.style.flexShrink = '0';
wrapper.style.cursor = 'pointer';
const btnHTML = `<div data-v-20f31264="" data-v-625d7fb6="" class="background-mp upload-button" style="--active-color:rgba(17,25,37,0.05);"><div data-v-b0734dc6="" class="upload-file default-upload"><svg data-v-641b0c80="" data-v-b0734dc6="" class="svg-icon icon-size" aria-hidden="true" width="16px" height="16px" viewBox="0 0 24 24"><path d="M3 6h18M3 12h18M3 18h18" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" fill="none"></path></svg><span data-v-b0734dc6="" class="text"></span></div><input data-v-b0734dc6="" class="upload-input" type="file" hidden=""></div>`;
setSafeInnerHTML(wrapper, btnHTML);
const btn = wrapper.querySelector('.background-mp');
createCustomTooltip(btn, getTranslation('prompts'));
return wrapper;
}
// ---MISTRAL---
function createMistralButton() {
const btn = document.createElement('button');
btn.className = 'flex items-center justify-center text-center font-medium cursor-pointer outline-hidden focus-visible:ring-3 relative whitespace-nowrap transition-colors focus-visible:ring-default focus-visible:ring-offset-1 aria-disabled:cursor-not-allowed aria-busy:cursor-wait aria-busy:text-transparent aria-disabled:aria-busy:text-transparent text-default bg-state-secondary hover:bg-state-secondary-hover active:bg-state-secondary-press aria-busy:bg-state-secondary-loading border-darker border-[0.5px] aria-disabled:text-muted h-9 w-9 text-sm rounded-md shrink-0 gap-0 overflow-hidden p-0';
btn.setAttribute('data-testid', 'composer-button-prompts');
btn.type = 'button';
const iconHTML = `<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="w-5 h-5 text-icon-default"><path d="M4 6h16M4 12h16M4 18h16" /></svg>`;
setSafeInnerHTML(btn, iconHTML);
createCustomTooltip(btn, getTranslation('prompts'));
return btn;
}
// ---TENCENT YUANBAO---
function createYuanbaoButton() {
const wrapper = document.createElement('div');
wrapper.className = 'ybc-atomSelect-tools-wrapper';
wrapper.style.display = 'flex';
wrapper.style.alignItems = 'center';
const btn = document.createElement('button');
btn.className = 'ybc-atomSelect-tools t-button t-button--theme-default t-button--variant-text';
btn.setAttribute('type', 'button');
btn.setAttribute('data-testid', 'composer-button-prompts');
const iconHTML = `<svg width="16" height="16" viewBox="0 0 20 20" fill="none" xmlns="http://www.w3.org/2000/svg" style="z-index: 1; margin-right: 4px;"><path d="M4 5h12M4 10h12M4 15h12" stroke="currentColor" stroke-width="2"></path></svg>`;
const textHTML = `<span class="t-button__text" style="z-index: 1;">${getTranslation('prompts')}</span>`;
setSafeInnerHTML(btn, iconHTML + textHTML);
wrapper.appendChild(btn);
return wrapper;
}
// ---CHATGLM---
function createChatGLMButton(type) {
const wrapper = document.createElement('div');
wrapper.setAttribute('data-testid', 'composer-button-prompts');
wrapper.style.cursor = 'pointer';
const mySvg = `<svg class="model-select-icon" xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" viewBox="0 0 20 20" style="z-index: 10; height: 24px;"><path d="M4 5h12M4 10h12M4 15h12" stroke="var(--txt_icon_black_1)" stroke-width="2" fill="none"></path></svg>`;
let innerHTML = '';
if (type === 'element1') {
wrapper.setAttribute('data-v-7dc2591c', '');
wrapper.setAttribute('data-v-b023d9fa', '');
wrapper.className = 'prompt-item dark el-tooltip__trigger el-tooltip__trigger';
innerHTML = mySvg;
} else if (type === 'element2') {
wrapper.setAttribute('data-v-7a34b085', '');
wrapper.className = 'prompt-item el-tooltip__trigger el-tooltip__trigger';
innerHTML = mySvg;
} else {
wrapper.setAttribute('data-v-5170ca64', '');
wrapper.setAttribute('data-v-b023d9fa', '');
wrapper.className = 'model-select-container flex flex-y-center flex-x-center';
innerHTML = `<span data-v-5170ca64="" class="el-tooltip__trigger"><div data-v-5170ca64="" class="model-select-icon-container flex flex-y-center flex-x-center animate-glide el-tooltip__trigger el-tooltip__trigger">${mySvg}</div></span>`;
}
setSafeInnerHTML(wrapper, innerHTML);
createCustomTooltip(wrapper, getTranslation('prompts'));
return wrapper;
}
// ---POE---
function createPoeButton() {
const wrapper = document.createElement('div');
const btnHTML = `<button class="button_root__TL8nv button_ghost__YsMI5 button_sm__hWzjK button_center__RsQ_o button_showIconOnly-always__05Gb5" type="button" aria-label="${getTranslation('prompts')}" data-theme="ghost"><svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="none" viewBox="0 0 24 24" style="height: 20px; width: 20px; display: block; flex: 0 0 auto;"><path d="M5 5h14M5 12h14M5 19h14" stroke="currentColor" stroke-width="2" stroke-linecap="round"></path></svg><span class="button_label__mCaDf"></span></button>`;
setSafeInnerHTML(wrapper, btnHTML);
const btn = wrapper.querySelector('button');
createCustomTooltip(btn, getTranslation('prompts'));
return wrapper;
}
// #endregion BOTÕES DE PROMPT
// #region MENUS E MODAIS
// ---PROMPT DINÂMICO PT1---
function parsePromptInternal(rawText) {
if (!rawText) return { processedText: '', ignoreMap: new Map(), selectMap: new Map(), inputMap: new Map() };
let processedText = rawText;
const ignoreMap = new Map();
const selectMap = new Map();
const inputMap = new Map();
let ignoreCounter = 0;
let selectCounter = 0;
let inputCounter = 0;
processedText = processedText.replace(/[ \t]*#ignore[ \t]*(?:\r?\n)?([\s\S]*?)(?:\r?\n)?[ \t]*#end[ \t]*/g, (match, content) => {
const key = `__IGNORE_${ignoreCounter++}__`;
ignoreMap.set(key, content);
return key;
});
processedText = processedText.replace(/\\\[/g, () => {
const key = `__ESC_OPEN_${ignoreCounter++}__`;
ignoreMap.set(key, '[');
return key;
}).replace(/\\\]/g, () => {
const key = `__ESC_CLOSE_${ignoreCounter++}__`;
ignoreMap.set(key, ']');
return key;
});
const regionRegex = /#start([\s\S]*?)#end/g;
processedText = processedText.replace(regionRegex, (match, body) => {
const key = `__SELECT_${selectCounter++}__`;
const options = [];
let currentOption = null;
const tokenRegex = /(?:^\s*#\s*(?!start|end)(.*)$)|(?:^\s*([+\-]|\d+)\s*\[([^\]]*)\])|(?:'([^'\\]*(?:\\.[^'\\]*)*)')/gm;
let m;
while ((m = tokenRegex.exec(body)) !== null) {
if (m[1]) {
const label = m[1].trim();
if (label) {
options.push({ type: 'header', label: label });
currentOption = null;
}
}
else if (m[2]) {
const prefix = m[2];
const label = m[3];
let type = prefix === '+' ? 'multi' : (prefix === '-' ? 'sovereign' : 'id');
let id = (type === 'id') ? prefix : null;
currentOption = { label, value: label, type, id };
options.push(currentOption);
}
else if (m[4] !== undefined) {
if (currentOption) {
let val = m[4];
val = val.replace(/\\'/g, "'");
currentOption.value = val;
}
}
}
selectMap.set(key, { title: (typeof getTranslation !== 'undefined' ? getTranslation('select') : 'Select'), options });
return key;
});
const getContext = (fullText, offset, length) => {
const start = Math.max(0, offset - 25);
const end = Math.min(fullText.length, offset + length + 25);
const prefix = fullText.substring(start, offset).replace(/\n/g, ' ').trim();
const suffix = fullText.substring(offset + length, end).replace(/\n/g, ' ').trim();
return (prefix ? "..." + prefix : "") + " [ ] " + (suffix ? suffix + "..." : "");
};
processedText = processedText.replace(/\[([^\]=]+?)\s*=\s*(\$[a-zA-Z0-9_]+)\](?:\(([^)]*)\))?/g, (match, label, varName, comment, offset, fullString) => {
if (label.startsWith('__') && label.endsWith('__')) return match;
const key = `__INPUT_${inputCounter++}__`;
let finalContext = comment;
if (comment === "") finalContext = getContext(fullString, offset, match.length);
inputMap.set(key, { label: label.trim(), varName: varName.trim(), context: finalContext });
return key;
});
processedText = processedText.replace(/\[([^\]]+?)\](?:\(([^)]*)\))?/g, (match, label, comment, offset, fullString) => {
if (label.startsWith('__') && label.endsWith('__')) return match;
if (inputMap.has(match)) return match;
const key = `__INPUT_${inputCounter++}__`;
let finalContext = comment;
if (comment === "") finalContext = getContext(fullString, offset, match.length);
inputMap.set(key, { label: label.trim(), varName: null, context: finalContext });
return key;
});
return { processedText, ignoreMap, selectMap, inputMap };
}
// ---CRIAR MENU DE PROMPT---
function createPromptMenu() {
const menu = document.createElement('div');
menu.className = 'prompt-menu';
menu.id = 'prompt-menu-container';
return menu;
}
// ---CRIAR MODAL DE EDITAR/CRIAR PROMPT---
function createPromptModal() {
const overlay = document.createElement('div');
overlay.className = 'mp-overlay mp-hidden';
overlay.id = '__ap_modal_overlay';
const box = document.createElement('div');
box.className = 'mp-modal-box';
box.id = '__ap_modal_box_el';
box.onclick = e => e.stopPropagation();
const iconExpand = `<svg viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>`;
const iconCollapse = `<svg viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/></svg>`;
const modalContentHTML = `
<button id="__ap_expand_btn" class="mp-modal-expand-btn" title="${getTranslation('expand')}">${iconExpand}</button>
<button id="__ap_info_btn" class="mp-modal-info-btn" title="${getTranslation('infoTitle')}"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path d="M10 2a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 14a6 6 0 1 1 0-12 6 6 0 0 1 0 12ZM9 5h2v2H9V5Zm0 4h2v6H9V9Z"/></svg></button>
<button id="__ap_close_prompt" class="mp-modal-close-btn" title="${getTranslation('close')}"><svg viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" /></svg></button>
<h2 class="modal-title">${getTranslation('newPrompt')}</h2>
<div class="form-group">
<label for="__ap_title" class="form-label">${getTranslation('title')}</label>
<input id="__ap_title" class="form-input" />
</div>
<div class="form-group" style="flex: 1; display: flex; flex-direction: column; min-height: 0;">
<label for="__ap_text" class="form-label">${getTranslation('text')}</label>
<textarea id="__ap_text" class="form-textarea" spellcheck="false"></textarea>
</div>
<div class="mp-switch-container">
<div class="mp-switch">
<input type="checkbox" id="__ap_use_placeholders" />
<label for="__ap_use_placeholders">Toggle</label>
<span class="switch-text" onclick="document.getElementById('__ap_use_placeholders').click()">${getTranslation('enablePlaceholders')}</span>
</div>
<div class="mp-switch">
<input type="checkbox" id="__ap_auto_execute" />
<label for="__ap_auto_execute">Toggle</label>
<span class="switch-text" onclick="document.getElementById('__ap_auto_execute').click()">${getTranslation('autoExecute')}</span>
</div>
</div>
<div class="modal-footer">
<button id="__ap_save" class="save-button">${getTranslation('save')}</button>
</div>
`;
setSafeInnerHTML(box, modalContentHTML);
overlay.appendChild(box);
const textarea = box.querySelector('#__ap_text');
setTimeout(() => {
const wrapper = setupEnhancedScroll(textarea, null, 'var(--mp-border-radius-md)');
if (wrapper) wrapper.classList.add('prompt-editor-scroll-wrapper');
}, 0);
const expandBtn = box.querySelector('#__ap_expand_btn');
let isExpanded = false;
expandBtn.onclick = (e) => {
e.stopPropagation();
isExpanded = !isExpanded;
if (isExpanded) {
box.classList.add('mp-expanded');
setSafeInnerHTML(expandBtn, iconCollapse);
expandBtn.title = getTranslation('collapse');
} else {
box.classList.remove('mp-expanded');
setSafeInnerHTML(expandBtn, iconExpand);
expandBtn.title = getTranslation('expand');
}
setTimeout(() => {
if (textarea.updateScrollArrows) textarea.updateScrollArrows();
}, 350);
};
return overlay;
}
// ---CRIAR MODAL DE INFORMAÇÕES---
function createInfoModal() {
const overlay = document.createElement('div');
overlay.className = 'mp-overlay mp-hidden';
overlay.id = '__ap_info_modal_overlay';
const box = document.createElement('div');
box.className = 'mp-modal-box';
box.onclick = e => e.stopPropagation();
const modalContentHTML = `
<button id="__ap_close_info" class="mp-modal-close-btn" aria-label="${getTranslation('close')}"><svg viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" /></svg></button>
<h2 class="modal-title">${getTranslation('infoTitle')}</h2>
<div class="mp-info-table">
<div class="mp-info-row">
<div class="mp-info-col">
<h3>${getTranslation('enablePlaceholders')}</h3>
</div>
<div class="mp-info-col">
<h3>${getTranslation('autoExecute')}</h3>
</div>
</div>
<div class="mp-info-row">
<div class="mp-info-col">
<p>${getTranslation('infoDPDesc')}</p>
</div>
<div class="mp-info-col">
<p>${getTranslation('infoASDesc')}</p>
</div>
</div>
</div>
`;
setSafeInnerHTML(box, modalContentHTML);
overlay.appendChild(box);
return overlay;
}
// ---MENU DE SELEÇÃO DE IDIOMA---
function createLanguageModal() {
const overlay = document.createElement('div');
overlay.className = 'mp-overlay mp-hidden lang-overlay';
overlay.id = '__ap_lang_modal_overlay';
overlay.onclick = () => hideModal(overlay);
const box = document.createElement('div');
box.className = 'mp-modal-box lang-box';
box.onclick = (e) => e.stopPropagation();
const searchInput = document.createElement('input');
searchInput.className = 'lang-search-input';
searchInput.placeholder = (typeof getTranslation === 'function' && translations[currentLang]?.searchLanguage)
? getTranslation('searchLanguage')
: 'Search language...';
searchInput.type = 'text';
searchInput.autocomplete = 'off';
const buttonsContainer = document.createElement('div');
buttonsContainer.style.display = 'flex';
buttonsContainer.style.flexDirection = 'column';
buttonsContainer.style.gap = '8px';
buttonsContainer.style.maxHeight = '400px';
function renderButtons(filterText = '') {
setSafeInnerHTML(buttonsContainer, '');
const term = filterText.toLowerCase();
Object.keys(translations).forEach((langKey, index) => {
const langName = translations[langKey].langName;
if (term && !langName.toLowerCase().includes(term)) return;
const btn = document.createElement('button');
btn.className = 'lang-button';
btn.textContent = langName;
if (langKey === currentLang) btn.classList.add('selected');
btn.style.animation = `mp-fade-in-up .3s ease forwards`;
btn.style.animationDelay = `${Math.min(index * 30, 200)}ms`;
btn.style.opacity = '0';
btn.onclick = async () => {
await GM_setValue(LANG_STORAGE_KEY, langKey);
window.location.reload();
};
buttonsContainer.appendChild(btn);
});
}
renderButtons();
searchInput.oninput = (e) => renderButtons(e.target.value);
box.appendChild(searchInput);
box.appendChild(buttonsContainer);
setupEnhancedScroll(buttonsContainer);
overlay.appendChild(box);
const observer = new MutationObserver(() => {
if (overlay.classList.contains('visible')) {
setTimeout(() => searchInput.focus(), 50);
}
});
observer.observe(overlay, { attributes: true, attributeFilter: ['class'] });
return overlay;
}
function showModal(modal) {
if (!modal) return;
modal.classList.remove('mp-hidden');
setTimeout(() => modal.classList.add('visible'), 10);
}
function hideModal(modal) {
if (!modal) return;
modal.classList.remove('visible');
setTimeout(() => modal.classList.add('mp-hidden'), 200);
}
// ---ABRIR MODAL DE EDITAR/CRIAR PROMPT---
function openPromptModal(item = null, index = -1) {
if (!currentModal) return;
const isEditing = !!item;
currentModal.dataset.index = index;
currentModal.querySelector('.modal-title').textContent = isEditing ? getTranslation('editPrompt') : getTranslation('newPrompt');
document.getElementById('__ap_title').value = item?.title || '';
document.getElementById('__ap_text').value = item?.text || '';
document.getElementById('__ap_use_placeholders').checked = item?.usePlaceholders || false;
document.getElementById('__ap_auto_execute').checked = item?.autoExecute || false;
showModal(currentModal);
setTimeout(() => document.getElementById('__ap_title').focus(), 100);
}
// ---CRIAR MODAL DE PROMPT DINÂMICO---
function createPlaceholderModal() {
const overlay = document.createElement('div');
overlay.className = 'mp-overlay mp-hidden';
overlay.id = '__ap_placeholder_modal_overlay';
const box = document.createElement('div');
box.className = 'mp-modal-box';
box.onclick = e => e.stopPropagation();
const iconExpand = `<svg viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M15 3h6v6M9 21H3v-6M21 3l-7 7M3 21l7-7"/></svg>`;
const iconCollapse = `<svg viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" d="M8 3v3a2 2 0 0 1-2 2H3m18 0h-3a2 2 0 0 1-2-2V3m0 18v-3a2 2 0 0 1 2-2h3M3 16h3a2 2 0 0 1 2 2v3"/></svg>`;
const modalContentHTML = `
<button id="__ap_ph_expand_btn" class="mp-modal-expand-btn" title="${getTranslation('expand')}">${iconExpand}</button>
<button id="__ap_close_placeholder" class="mp-modal-close-btn" aria-label="${getTranslation('close')}"><svg viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" /></svg></button>
<h2 class="modal-title">${getTranslation('fillPlaceholders')}</h2>
<div id="__ap_placeholders_container"></div>
<div class="modal-footer">
<button id="__ap_insert_prompt" class="save-button">${getTranslation('insert')}</button>
</div>
`;
setSafeInnerHTML(box, modalContentHTML);
overlay.appendChild(box);
const container = box.querySelector('#__ap_placeholders_container');
container.style.maxHeight = '350px';
setupEnhancedScroll(container);
const expandBtn = box.querySelector('#__ap_ph_expand_btn');
let isExpanded = false;
expandBtn.onclick = (e) => {
e.stopPropagation();
isExpanded = !isExpanded;
if (isExpanded) {
box.classList.add('mp-expanded');
setSafeInnerHTML(expandBtn, iconCollapse);
expandBtn.title = getTranslation('collapse');
} else {
box.classList.remove('mp-expanded');
setSafeInnerHTML(expandBtn, iconExpand);
expandBtn.title = getTranslation('expand');
}
setTimeout(() => {
if (container.updateScrollArrows) container.updateScrollArrows();
}, 350);
};
return overlay;
}
// ---AVISO FIREFOX DOUBAO---
function createFirefoxWarningModal() {
const overlay = document.createElement('div');
overlay.className = 'mp-overlay';
overlay.id = '__ap_warning_modal_overlay';
const box = document.createElement('div');
box.className = 'mp-modal-box';
box.onclick = e => e.stopPropagation();
const modalContentHTML = `<button id="__ap_close_warning" class="mp-modal-close-btn" aria-label="${getTranslation('close')}"><svg viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" /></svg></button><div style="display: flex; flex-direction: column; align-items: center; gap: 15px; text-align: center; padding-top: 10px;"><p style="font-size: 0.95rem; color: var(--text-primary, inherit); opacity: 0.9;">${getTranslation('ffWarningText')}</p><p style="font-size: 0.9rem; background: rgba(249, 115, 22, 0.1); padding: 10px; border-radius: 6px; border: 1px solid rgba(249, 115, 22, 0.2);">${getTranslation('ffRecommend')}</p></div><div class="modal-footer" style="margin-top: 20px;"><button id="__ap_dismiss_forever" class="save-button" style="width: 100%; background-color: rgba(128, 128, 128, 0.51);">${getTranslation('dontShowAgain')}</button></div>`;
setSafeInnerHTML(box, modalContentHTML);
overlay.appendChild(box);
const closeBtn = box.querySelector('#__ap_close_warning');
const dismissBtn = box.querySelector('#__ap_dismiss_forever');
const close = () => hideModal(overlay);
closeBtn.onclick = (e) => {
e.stopPropagation();
close();
};
dismissBtn.onclick = async (e) => {
e.stopPropagation();
await GM_setValue('doubaoffwarning', true);
const currentWarningBtn = document.querySelector('button[data-is-warning="true"]');
if (currentWarningBtn) {
const wrapper = currentWarningBtn.closest('div');
if (wrapper) wrapper.remove();
}
close();
};
return overlay;
}
// #endregion MENUS E MODAIS
// #region FUNÇÕES AUXILIARES
// --- FUNÇÃO HELPER PARA SCROLL COM SETAS ---
function setupEnhancedScroll(scrollContainer, customBgVariable = null, borderRadius = null) {
if (!scrollContainer) return;
const computedStyle = window.getComputedStyle(scrollContainer);
const marginBottom = computedStyle.marginBottom;
const marginTop = computedStyle.marginTop;
scrollContainer.classList.add('mp-scroll-invisible');
const wrapper = document.createElement('div');
wrapper.className = 'mp-scroll-wrapper';
if (customBgVariable) wrapper.style.setProperty('--scroll-bg', customBgVariable);
if (borderRadius) wrapper.style.borderRadius = borderRadius;
wrapper.style.marginBottom = marginBottom;
wrapper.style.marginTop = marginTop;
scrollContainer.style.marginBottom = '0';
scrollContainer.style.marginTop = '0';
scrollContainer.parentNode.insertBefore(wrapper, scrollContainer);
wrapper.appendChild(scrollContainer);
const createArrow = (cls) => {
const arr = document.createElement('div');
arr.className = `mp-scroll-arrow ${cls}`;
setSafeInnerHTML(arr, cls === 'up'
? `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 16"><path fill="currentColor" fill-rule="evenodd" d="M15.81 9.9a1 1 0 0 1-.65-.2L8.93 5.54 2.9 9.74a1.2 1.2 0 0 1-1.63-.33 1.17 1.17 0 0 1 .32-1.63l6.69-4.63a1.2 1.2 0 0 1 1.3 0l6.88 4.59a1.18 1.18 0 0 1-.65 2.16"/></svg>`
: `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 17 17"><path fill="currentColor" fill-rule="evenodd" d="M2.16 6.246c.225 0 .45.062.65.196l6.229 4.156 6.037-4.197a1.175 1.175 0 0 1 1.304 1.958l-6.688 4.63a1.17 1.17 0 0 1-1.304.002l-6.88-4.589a1.178 1.178 0 0 1 .652-2.156"/></svg>`);
return arr;
};
const arrowUp = createArrow('up');
const arrowDown = createArrow('down');
wrapper.appendChild(arrowUp);
wrapper.appendChild(arrowDown);
const updateArrows = () => {
const { scrollTop, scrollHeight, clientHeight } = scrollContainer;
if (scrollTop > 1) arrowUp.classList.add('visible');
else arrowUp.classList.remove('visible');
if (scrollHeight - scrollTop - clientHeight > 1) arrowDown.classList.add('visible');
else arrowDown.classList.remove('visible');
};
arrowUp.onclick = (e) => { e.stopPropagation(); scrollContainer.scrollBy({ top: -100, behavior: 'smooth' }); };
arrowDown.onclick = (e) => { e.stopPropagation(); scrollContainer.scrollBy({ top: 100, behavior: 'smooth' }); };
scrollContainer.addEventListener('scroll', updateArrows);
const obs = new MutationObserver(updateArrows);
obs.observe(scrollContainer, { childList: true, subtree: true });
const resizeObs = new ResizeObserver(updateArrows);
resizeObs.observe(scrollContainer);
scrollContainer.updateScrollArrows = updateArrows;
setTimeout(updateArrows, 0);
return wrapper;
}
// ---MOVER CURSOR PARA O FIM DO CAMPO DE TEXTO---
function moveCursorToEnd(editor) {
setTimeout(() => {
try {
editor.focus();
if (currentPlatform === 'gemini') {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(editor);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
editor.scrollTop = editor.scrollHeight;
} else if (currentPlatform === 'mistral') {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(editor);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
let scrollContainer = editor.parentElement;
let i = 0;
while (scrollContainer && i < 10) {
if (scrollContainer.scrollHeight > scrollContainer.clientHeight) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
scrollContainer = scrollContainer.parentElement;
i++;
}
} else if (currentPlatform === 'chatgpt' || currentPlatform === 'claude' || currentPlatform === 'grok') {
const selection = window.getSelection();
const range = document.createRange();
range.selectNodeContents(editor);
range.collapse(false);
selection.removeAllRanges();
selection.addRange(range);
if (currentPlatform === 'grok') {
editor.scrollTop = editor.scrollHeight;
} else {
const scrollContainer = editor.parentElement;
if (scrollContainer) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
}
} else if (currentPlatform === 'googlestudio') {
const textLength = editor.value.length;
editor.selectionStart = editor.selectionEnd = textLength;
let scrollContainer = editor.parentElement;
let i = 0;
while (scrollContainer && i < 10) {
const style = window.getComputedStyle(scrollContainer);
if (style.overflowY === 'auto' || style.overflowY === 'scroll') {
break;
}
scrollContainer = scrollContainer.parentElement;
i++;
}
if (scrollContainer) {
scrollContainer.scrollTop = scrollContainer.scrollHeight;
}
} else {
const textLength = editor.value.length;
editor.selectionStart = editor.selectionEnd = textLength;
editor.scrollTop = editor.scrollHeight;
}
} catch (e) {}
}, 10);
}
// ---FECHAR MENU---
function closeMenu() {
if (currentMenu && currentMenu.classList.contains('visible')) {
currentMenu.classList.remove('visible');
}
}
// ---POSICIONAR MENU---
function positionMenu(menu, button) {
const btnRect = button.getBoundingClientRect();
const menuHeight = menu.offsetHeight;
const menuWidth = menu.offsetWidth;
const viewportHeight = window.innerHeight;
const viewportWidth = window.innerWidth;
const margin = 8;
let top, left;
const spaceBelow = viewportHeight - btnRect.bottom - margin;
const spaceAbove = btnRect.top - margin;
if (spaceBelow >= menuHeight) {
top = btnRect.bottom + margin;
} else if (spaceAbove >= menuHeight) {
top = btnRect.top - menuHeight - margin;
} else {
top = Math.max(margin, viewportHeight - menuHeight - margin);
}
const spaceRight = viewportWidth - btnRect.left - margin;
const spaceLeft = btnRect.right - margin;
if (spaceRight >= menuWidth) {
left = btnRect.left;
} else if (spaceLeft >= menuWidth) {
left = btnRect.right - menuWidth;
} else {
left = (viewportWidth - menuWidth) / 2;
}
menu.style.top = `${Math.max(margin, Math.min(top, viewportHeight - menuHeight - margin))}px`;
menu.style.left = `${Math.max(margin, Math.min(left, viewportWidth - menuWidth - margin))}px`;
}
// ---ATUALIZAR MENU DE PROMPTS---
async function refreshMenu() {
if (!currentMenu) return;
setSafeInnerHTML(currentMenu, '');
// ---BUSCA---
const searchContainer = document.createElement('div');
searchContainer.className = 'menu-search-container';
const searchInput = document.createElement('input');
searchInput.className = 'menu-search-input';
searchInput.placeholder = getTranslation('search');
searchInput.type = 'text';
searchInput.autocomplete = 'off';
searchInput.onclick = (e) => e.stopPropagation();
searchInput.onkeydown = (e) => e.stopPropagation();
searchContainer.appendChild(searchInput);
currentMenu.appendChild(searchContainer);
// ---LISTA---
const listContainer = document.createElement('div');
listContainer.className = 'prompt-menu-list';
listContainer.id = 'prompt-menu-list-el';
const items = await getAll();
const emptyState = document.createElement('div');
emptyState.className = 'empty-state';
emptyState.textContent = getTranslation('noSavedPrompts');
if (items.length === 0) {
listContainer.appendChild(emptyState);
} else {
emptyState.style.display = 'none';
emptyState.textContent = getTranslation('noSearchResults');
listContainer.appendChild(emptyState);
items.forEach((p, index) => {
const row = document.createElement('div');
row.className = 'prompt-item-row';
row.dataset.searchText = (p.title + ' ' + p.text).toLowerCase();
const titleDiv = document.createElement('div');
titleDiv.className = 'prompt-title';
titleDiv.textContent = p.title;
titleDiv.onclick = (e) => {
e.stopPropagation();
if (p.usePlaceholders) openPlaceholderModal(p, index);
else insertPrompt(p, index);
closeMenu();
};
const actionsDiv = document.createElement('div');
actionsDiv.className = 'prompt-actions';
const createBtn = (cls, icon, title, clickFn) => {
const b = document.createElement('button');
b.className = `action-btn ${cls}`;
b.title = title;
setSafeInnerHTML(b, icon);
b.onclick = clickFn;
return b;
};
// ---EDITAR/DELETAR---
const btnE = createBtn('edit', `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 416 432"><path fill="currentColor" d="m366 237 45 35q7 6 3 14l-43 74q-4 8-13 4l-53-21q-18 13-36 21l-8 56q-1 9-11 9h-85q-9 0-11-9l-8-56q-19-8-36-21l-53 21q-9 3-13-4L1 286q-4-8 3-14l45-35q-1-12-1-21t1-21L4 160q-7-6-3-14l43-74q5-8 13-4l53 21q18-13 36-21l8-56q2-9 11-9h85q10 0 11 9l8 56q19 8 36 21l53-21q9-3 13 4l43 74q4 8-3 14l-45 35q2 12 2 21t-2 21m-158.5 54q30.5 0 52.5-22t22-53-22-53-52.5-22-52.5 22-22 53 22 53 52.5 22"/></svg>`, getTranslation('edit'), (e) => { e.stopPropagation(); openPromptModal(p, index); });
const btnD = createBtn('delete', `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 304 384"><path fill="currentColor" d="M21 341V85h256v256q0 18-12.5 30.5T235 384H64q-18 0-30.5-12.5T21 341M299 21v43H0V21h75L96 0h107l21 21z"/></svg>`, getTranslation('delete'), (e) => { e.stopPropagation(); if (confirm(getTranslation('confirmDelete', { title: p.title }))) remove(index).then(refreshMenu); });
actionsDiv.appendChild(btnE);
actionsDiv.appendChild(btnD);
row.appendChild(titleDiv);
row.appendChild(actionsDiv);
listContainer.appendChild(row);
});
}
currentMenu.appendChild(listContainer);
setupEnhancedScroll(listContainer);
// ---FILTRO DE BUSCA---
searchInput.oninput = (e) => {
const term = e.target.value.toLowerCase();
const rows = listContainer.querySelectorAll('.prompt-item-row');
let visibleCount = 0;
rows.forEach(row => {
if (row.dataset.searchText && row.dataset.searchText.includes(term)) {
row.style.display = 'flex';
visibleCount++;
} else {
row.style.display = 'none';
}
});
if (items.length > 0) {
if (visibleCount === 0) {
emptyState.style.display = 'block';
emptyState.textContent = getTranslation('noSearchResults');
} else {
emptyState.style.display = 'none';
}
}
};
// ---BOTÕES DO RODAPÉ---
const footerGrid = document.createElement('div');
footerGrid.className = 'menu-footer-grid';
const createFooterBtn = (cls, icon, title, clickFn) => {
const b = document.createElement('button');
b.className = `menu-footer-btn ${cls}`;
b.title = title;
setSafeInnerHTML(b, icon);
b.onclick = clickFn;
return b;
};
// ---EXPORTAR/ADICIONAR/IMPORTAR---
footerGrid.appendChild(createFooterBtn('btn-export', `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M21 14a1 1 0 0 0-1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-4a1 1 0 0 0-1-1m-9.71 1.71a1 1 0 0 0 .33.21 1 1 0 0 0 .76 0 1 1 0 0 0 .33-.21l4-4a1 1 0 0 0-1.42-1.42L13 12.59V3a1 1 0 0 0-2 0v9.59l-2.29-2.3a1 1 0 1 0-1.42 1.42Z"/></svg>`, getTranslation('export'), (e) => { e.stopPropagation(); exportPrompts(); }));
footerGrid.appendChild(createFooterBtn('btn-add', `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 14 14"><path fill="currentColor" fill-rule="evenodd" d="M8 1a1 1 0 0 0-2 0v5H1a1 1 0 0 0 0 2h5v5a1 1 0 1 0 2 0V8h5a1 1 0 1 0 0-2H8z" clip-rule="evenodd"/></svg>`, getTranslation('addPrompt'), (e) => { e.stopPropagation(); openPromptModal(); }));
footerGrid.appendChild(createFooterBtn('btn-import', `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24"><path fill="currentColor" d="M8.71 7.71 11 5.41V15a1 1 0 0 0 2 0V5.41l2.29 2.3a1 1 0 0 0 1.42 0 1 1 0 0 0 0-1.42l-4-4a1 1 0 0 0-.33-.21 1 1 0 0 0-.76 0 1 1 0 0 0-.33.21l-4 4a1 1 0 1 0 1.42 1.42M21 14a1 1 0 0 0-1 1v4a1 1 0 0 1-1 1H5a1 1 0 0 1-1-1v-4a1 1 0 0 0-2 0v4a3 3 0 0 0 3 3h14a3 3 0 0 0 3-3v-4a1 1 0 0 0-1-1"/></svg>`, getTranslation('import'), (e) => { e.stopPropagation(); importPrompts(); }));
currentMenu.appendChild(footerGrid);
setTimeout(() => searchInput.focus(), 50);
}
// ---PROMPT DINÂMICO PT2---
function openPlaceholderModal(item, index) {
const { processedText, ignoreMap, selectMap, inputMap } = parsePromptInternal(item.text);
if (selectMap.size === 0 && inputMap.size === 0) {
let finalText = processedText;
ignoreMap.forEach((val, key) => { finalText = finalText.replace(key, val); });
const finalItem = { ...item, text: finalText };
insertPrompt(finalItem, index);
return;
}
if (!currentPlaceholderModal) return;
const container = document.getElementById('__ap_placeholders_container');
setSafeInnerHTML(container, '');
currentPlaceholderModal.dataset.parseData = JSON.stringify({
processedText,
ignoreMap: Array.from(ignoreMap.entries()),
selectMap: Array.from(selectMap.entries()),
inputMap: Array.from(inputMap.entries())
});
currentPlaceholderModal.dataset.index = index;
currentPlaceholderModal.dataset.originalItem = JSON.stringify(item);
inputMap.forEach((data, key) => {
const labelText = typeof data === 'string' ? data : data.label;
let contextText = (typeof data === 'object' && data.context) ? data.context : null;
if (contextText) {
inputMap.forEach((iData, iKey) => {
if (contextText.includes(iKey)) {
const iLabel = typeof iData === 'string' ? iData : iData.label;
contextText = contextText.split(iKey).join(`[${iLabel}]`);
}
});
selectMap.forEach((sData, sKey) => {
if (contextText.includes(sKey)) {
contextText = contextText.split(sKey).join(`[List: ${sData.title}]`);
}
});
ignoreMap.forEach((val, iKey) => {
if (contextText.includes(iKey)) {
contextText = contextText.split(iKey).join('[...Code/Block...]');
}
});
}
const formGroup = document.createElement('div');
formGroup.className = 'form-group';
formGroup.style.marginBottom = '12px';
const labelWrapper = document.createElement('div');
labelWrapper.className = 'mp-label-wrapper';
const lbl = document.createElement('label');
lbl.className = 'form-label';
lbl.textContent = labelText;
lbl.style.marginBottom = '0';
labelWrapper.appendChild(lbl);
if (contextText) {
const icon = document.createElement('div');
icon.className = 'mp-help-icon';
icon.title = getTranslation('clickToShowContext');
setSafeInnerHTML(icon, `<svg viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm1 15h-2v-6h2v6zm0-8h-2V7h2v2z"/></svg>`);
icon.onclick = (e) => { e.stopPropagation(); formGroup.querySelector('.mp-context-bubble').classList.toggle('visible'); };
labelWrapper.appendChild(icon);
}
formGroup.appendChild(labelWrapper);
if (contextText) {
const bubble = document.createElement('div');
bubble.className = 'mp-context-bubble';
bubble.textContent = contextText;
formGroup.appendChild(bubble);
}
const textarea = document.createElement('textarea');
textarea.className = 'form-input dynamic-input';
textarea.dataset.key = key;
textarea.rows = 1;
textarea.style.resize = 'vertical';
textarea.style.height = 'auto';
textarea.placeholder = data.varName ? data.varName : '';
textarea.addEventListener('keydown', (event) => {
if (event.key === 'Enter' && !event.shiftKey) {
event.preventDefault();
document.getElementById('__ap_insert_prompt').click();
}
});
formGroup.appendChild(textarea);
container.appendChild(formGroup);
});
selectMap.forEach((data, key) => {
const regionContainer = document.createElement('div');
regionContainer.dataset.selectKey = key;
regionContainer.style.marginBottom = '16px';
let currentOptionGroup = null;
if (data.options.length > 0 && data.options[0].type !== 'header') {
const defaultLabel = document.createElement('label');
defaultLabel.className = 'form-label';
defaultLabel.textContent = data.title;
defaultLabel.style.marginBottom = '6px';
regionContainer.appendChild(defaultLabel);
currentOptionGroup = document.createElement('div');
currentOptionGroup.className = 'mp-option-group';
regionContainer.appendChild(currentOptionGroup);
}
data.options.forEach((opt) => {
if (opt.type === 'header') {
if (regionContainer.children.length > 0) {
const spacer = document.createElement('div');
spacer.style.height = '8px';
regionContainer.appendChild(spacer);
}
const headerLabel = document.createElement('label');
headerLabel.className = 'form-label';
headerLabel.textContent = opt.label;
headerLabel.style.color = 'var(--mp-accent-secondary)';
regionContainer.appendChild(headerLabel);
currentOptionGroup = document.createElement('div');
currentOptionGroup.className = 'mp-option-group';
regionContainer.appendChild(currentOptionGroup);
}
else {
if (!currentOptionGroup) {
currentOptionGroup = document.createElement('div');
currentOptionGroup.className = 'mp-option-group';
regionContainer.appendChild(currentOptionGroup);
}
const optLabel = document.createElement('label');
optLabel.className = 'mp-option-item';
if (opt.type === 'id' && opt.id) {
const color = getColorForId(opt.id);
optLabel.style.cssText = `border-left: 5px solid ${color} !important; padding-left: 8px;`;
const idGroupText = typeof getTranslation === 'function' ? getTranslation('idGroup') : 'ID Group';
optLabel.title = `${idGroupText}: ${opt.id}`;
}
else if (opt.type === 'sovereign') {
optLabel.style.cssText = `border-left: 5px solid #FF4444 !important; padding-left: 8px;`;
optLabel.title = typeof getTranslation === 'function' ? getTranslation('uniqueSelection') : 'Unique Selection';
}
else {
optLabel.style.borderLeft = "5px solid transparent";
}
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'mp-checkbox';
checkbox.dataset.type = opt.type;
if (opt.id) checkbox.dataset.id = opt.id;
checkbox.value = opt.value;
checkbox.onchange = function() {
if (!this.checked) return;
const myGroup = this.closest('.mp-option-group');
const siblings = Array.from(myGroup.querySelectorAll('input[type="checkbox"]'));
const myType = this.dataset.type;
const myId = this.dataset.id;
siblings.forEach(other => {
if (other === this) return;
if (myType === 'sovereign') { other.checked = false; return; }
if (other.dataset.type === 'sovereign') other.checked = false;
if (myType === 'id' && other.dataset.type === 'id' && other.dataset.id === myId) other.checked = false;
});
};
const span = document.createElement('span');
span.textContent = opt.label;
if (opt.type === 'sovereign') {
span.style.fontWeight = '600';
span.style.color = 'var(--mp-text-primary)';
}
optLabel.appendChild(checkbox);
optLabel.appendChild(span);
currentOptionGroup.appendChild(optLabel);
}
});
container.appendChild(regionContainer);
});
showModal(currentPlaceholderModal);
setTimeout(() => container.querySelector('textarea, input')?.focus(), 100);
}
// ---APLICAR PADDING ESPECIAL PARA GROK---
function applyGrokPadding() {
const styleId = 'my-prompt-grok-padding';
if (document.getElementById(styleId)) return;
const styleElement = document.createElement('style');
styleElement.id = styleId;
const cssRule = `div.query-bar > div[class*="ps-11"] {padding-left: 95px !important; padding-right: 95px !important;}`;
setSafeInnerHTML(styleElement, cssRule);
document.head.appendChild(styleElement);
}
// ---APLICAR ESTILOS ESPECÍFICOS DO CHATGLM---
function applyChatGLMCustomStyles() {
const styleId = 'my-prompt-chatglm-left-align';
if (document.getElementById(styleId)) return;
const styleElement = document.createElement('style');
styleElement.id = styleId;
const cssRule = `body {text-align: left;}`;
setSafeInnerHTML(styleElement, cssRule);
document.head.appendChild(styleElement);
}
// ---HELPER DE CORES PROMPT DINÂMICO---
const mpColorPalette = [
'#4ECDC4', '#45B7D1', '#98D8C8', '#F7DC6F', '#BB8FCE',
'#82E0AA', '#85C1E9', '#DAF7A6', '#FFC300', '#3498DB',
'#2ECC71', '#9B59B6', '#F4D03F', '#1ABC9C', '#27AE60',
'#F39C12', '#16A085', '#34495E', '#F5B041', '#5499C7'
];
function getColorForId(id) {
if (!id) return 'transparent';
let str = String(id);
let hash = 0;
for (let i = 0; i < str.length; i++) {
hash = str.charCodeAt(i) + ((hash << 5) - hash);
}
const index = Math.abs(hash) % mpColorPalette.length;
return mpColorPalette[index];
}
// #endregion FUNÇÕES AUXILIARES
// #region INTERAÇÃO COM A PÁGINA
// ---DETECTAR PLATAFORMA---
function detectPlatform() {
const hostname = window.location.hostname;
if (hostname.includes('chatgpt.com')) return 'chatgpt';
if (hostname.includes('deepseek.com')) return 'deepseek';
if (hostname.includes('aistudio.google.com')) return 'googlestudio';
if (hostname.includes('chat.qwen.ai')) return 'qwen';
if (hostname.includes('chat.z.ai')) return 'zai';
if (hostname.includes('gemini.google.com')) return 'gemini';
if (hostname.includes('lmarena.ai')) return 'lmarena';
if (hostname.includes('kimi.com')) return 'kimi';
if (hostname.includes('claude.ai')) return 'claude';
if (hostname.includes('grok.com')) return 'grok';
if (hostname.includes('www.perplexity.ai')) return 'perplexity';
if (hostname.includes('doubao.com')) return 'doubao';
if (hostname.includes('longcat.chat')) return 'longcat';
if (hostname.includes('mistral.ai')) return 'mistral';
if (hostname.includes('yuanbao.tencent.com')) return 'yuanbao';
if (hostname.includes('chatglm.cn')) return 'chatglm';
if (hostname.includes('poe.com')) return 'poe';
return null;
}
// ---INSERIR PROMPT NO CAMPO DE TEXTO---
async function insertPrompt(promptItem, index) {
const platformSelectors = {
chatgpt: '#prompt-textarea',
deepseek: 'textarea.ds-scroll-area',
googlestudio: 'ms-autosize-textarea textarea',
qwen: 'textarea#chat-input',
zai: 'textarea#chat-input',
gemini: 'div.ql-editor[contenteditable="true"]',
lmarena: 'textarea[name="message"]',
kimi: 'div.chat-input-editor[contenteditable="true"]',
claude: 'div.ProseMirror[contenteditable="true"]',
grok: 'div.tiptap.ProseMirror[contenteditable="true"]',
perplexity: '#ask-input',
doubao: 'textarea[data-testid="chat_input_input"], div[data-testid="chat_input_input"]',
longcat: 'div.tiptap.ProseMirror',
mistral: '.ProseMirror',
yuanbao: 'div.chat-input-editor > div.ql-editor',
chatglm: 'textarea.scroll-display-none',
poe: 'textarea[class*="GrowingTextArea_textArea"]',
};
let editor = document.querySelector(platformSelectors[currentPlatform]);
if (!editor && currentPlatform === 'grok') {
editor = document.querySelector('textarea[aria-label]');
}
if (!editor) { return; }
editor.focus();
setTimeout(() => {
// ---INSERÇÃO ESPECIAL PARA FIREFOX---
const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
if (isFirefox && (currentPlatform === 'kimi' || currentPlatform === 'perplexity')) {
document.execCommand('selectAll', false, null);
document.execCommand('insertText', false, promptItem.text);
}
else if (isFirefox && (currentPlatform === 'chatgpt' || currentPlatform === 'claude' || currentPlatform === 'grok' || currentPlatform === 'longcat' || currentPlatform === 'mistral' || currentPlatform === 'yuanbao')) {
setSafeInnerHTML(editor, '');
const lines = promptItem.text.split('\n');
lines.forEach(line => {
const p = document.createElement('p');
if (line.trim() === '') {
p.appendChild(document.createElement('br'));
} else {
p.textContent = line;
}
editor.appendChild(p);
});
editor.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
editor.focus();
const range = document.createRange();
range.selectNodeContents(editor);
range.collapse(false);
const sel = window.getSelection();
sel.removeAllRanges();
sel.addRange(range);
}
else if (currentPlatform === 'gemini') {
let p = editor.querySelector('p') || document.createElement('p');
setSafeInnerHTML(editor, '');
p.textContent = promptItem.text;
editor.appendChild(p);
editor.dispatchEvent(new Event('input', { bubbles: true, composed: true }));
}
// ---ESTADO NORMAL---
else {
const dt = new DataTransfer();
dt.setData('text/plain', promptItem.text);
editor.dispatchEvent(new ClipboardEvent('paste', { clipboardData: dt, bubbles: true, cancelable: true }));
if (editor.value !== undefined && editor.value !== promptItem.text) {
const setter = Object.getOwnPropertyDescriptor(window.HTMLTextAreaElement.prototype, 'value').set;
setter.call(editor, promptItem.text);
editor.dispatchEvent(new Event('input', { bubbles: true }));
}
}
// ---ENVIO AUTOMÁTICO---
// ---CASOS ESPECIAIS (ATIVAR BOTÕES DE AÇÃO)---
moveCursorToEnd(editor);
if (promptItem.autoExecute) {
setTimeout(() => {
let sent = false;
// ---QWEN---
if (currentPlatform === 'qwen') {
try {
const sendButton = document.querySelector('#send-message-button');
if (sendButton && !sendButton.disabled) {
sendButton.click();
sent = true;
}
} catch (e) {}
}
// ---GOOGLE AI STUDIO---
if (currentPlatform === 'googlestudio') {
try {
const runButton = document.querySelector('button[mattooltipclass="run-button-tooltip"]') || document.querySelector('button.run-button');
if (runButton && !runButton.disabled) {
runButton.click();
sent = true;
}
} catch (e) {}
}
// ---CASOS NORMAIS (SIMULAR ENTER)---
if (!sent) {
try {
const enterEvent = new KeyboardEvent('keydown', {
key: 'Enter',
code: 'Enter',
which: 13,
keyCode: 13,
bubbles: true,
cancelable: true
});
editor.dispatchEvent(enterEvent);
} catch (e) {}
}
}, 150);
}
}, 100);
// ---MOVER PROMPT USADO PARA O TOPO---
let prompts = await getAll();
if (index > 0) {
const item = prompts.splice(index, 1)[0];
prompts.unshift(item);
await GM_setValue(PROMPT_STORAGE_KEY, prompts);
}
}
// ---MENU DE EXPORTAÇÃO---
async function openExportMenu() {
closeMenu();
const overlay = document.createElement('div');
overlay.className = 'mp-overlay';
overlay.id = '__ap_export_overlay';
const box = document.createElement('div');
box.className = 'mp-modal-box';
box.onclick = e => e.stopPropagation();
const prompts = await getAll();
const htmlStructure = `
<button id="__ap_close_export" class="mp-modal-close-btn"><svg viewBox="0 0 24 24"><path d="M6 18L18 6M6 6l12 12" /></svg></button>
<h2 class="modal-title">${getTranslation('export')}</h2>
<div class="mp-search-container">
<input type="text" id="__ap_export_search" class="mp-search-input" placeholder="${getTranslation('search')}" autocomplete="off">
<div class="mp-export-actions">
<label class="mp-checkbox-wrapper" style="cursor:pointer; user-select:none;">
<input type="checkbox" id="__ap_select_all" class="mp-checkbox" checked>
<span style="margin-left:8px;">${getTranslation('selectAll')}</span>
</label>
<span id="__ap_count_label">${getTranslation('countPrompts', { count: prompts.length })}</span>
</div>
</div>
<div class="mp-export-list" id="__ap_export_list">
</div>
<div class="mp-export-buttons">
<button id="__ap_do_export_txt" class="save-button mp-btn-secondary" style="margin-right:auto">TXT</button>
<button id="__ap_do_export_json" class="save-button">JSON</button>
</div>
`;
setSafeInnerHTML(box, htmlStructure);
overlay.appendChild(box);
document.body.appendChild(overlay);
setTimeout(() => overlay.classList.add('visible'), 10);
const listContainer = box.querySelector('#__ap_export_list');
listContainer.style.maxHeight = '300px';
setupEnhancedScroll(listContainer);
function renderList(filterText = '') {
listContainer.textContent = '';
filterText = filterText.toLowerCase();
let visibleCount = 0;
if (prompts.length === 0) {
const emptyMsg = document.createElement('div');
emptyMsg.className = 'empty-state';
emptyMsg.textContent = getTranslation('noSavedPrompts');
listContainer.appendChild(emptyMsg);
return;
}
prompts.forEach((p, index) => {
const match = p.title.toLowerCase().includes(filterText) || p.text.toLowerCase().includes(filterText);
if (!match) return;
visibleCount++;
const item = document.createElement('div');
item.className = 'mp-export-item';
item.onclick = (e) => {
if (e.target.type !== 'checkbox') {
const cb = item.querySelector('input');
cb.checked = !cb.checked;
updateSelectAllState();
}
};
const checkbox = document.createElement('input');
checkbox.type = 'checkbox';
checkbox.className = 'mp-checkbox prompt-selector';
checkbox.checked = true;
checkbox.dataset.index = index;
checkbox.onclick = (e) => { e.stopPropagation(); updateSelectAllState(); };
const content = document.createElement('div');
content.className = 'mp-item-content';
const title = document.createElement('div');
title.className = 'mp-item-title';
title.textContent = p.title;
const preview = document.createElement('div');
preview.className = 'mp-item-preview';
preview.textContent = p.text.substring(0, 60).replace(/\n/g, ' ') + '...';
const cbWrapper = document.createElement('div');
cbWrapper.className = 'mp-checkbox-wrapper';
cbWrapper.appendChild(checkbox);
content.appendChild(title);
content.appendChild(preview);
item.appendChild(cbWrapper);
item.appendChild(content);
listContainer.appendChild(item);
});
document.getElementById('__ap_count_label').textContent = getTranslation('countPrompts', { count: visibleCount });
}
renderList();
const searchInput = document.getElementById('__ap_export_search');
const selectAllCb = document.getElementById('__ap_select_all');
searchInput.oninput = (e) => {
renderList(e.target.value);
updateSelectAllState();
};
selectAllCb.onchange = (e) => {
const checkboxes = listContainer.querySelectorAll('.prompt-selector');
checkboxes.forEach(cb => cb.checked = e.target.checked);
};
function updateSelectAllState() {
const checkboxes = Array.from(listContainer.querySelectorAll('.prompt-selector'));
if (checkboxes.length === 0) return;
const allChecked = checkboxes.every(cb => cb.checked);
const someChecked = checkboxes.some(cb => cb.checked);
selectAllCb.checked = allChecked;
selectAllCb.indeterminate = someChecked && !allChecked;
}
const getSelectedPrompts = () => {
const checkboxes = Array.from(listContainer.querySelectorAll('.prompt-selector:checked'));
return checkboxes.map(cb => prompts[parseInt(cb.dataset.index)]);
};
const closeExportModal = () => {
overlay.classList.remove('visible');
setTimeout(() => overlay.remove(), 200);
};
box.querySelector('#__ap_close_export').onclick = closeExportModal;
overlay.onclick = (e) => { if (e.target === overlay) closeExportModal(); };
document.getElementById('__ap_do_export_json').onclick = () => {
const selected = getSelectedPrompts();
if (selected.length === 0) { alert(getTranslation('noPromptsToExport')); return; }
const a = document.createElement('a');
a.href = URL.createObjectURL(new Blob([JSON.stringify(selected, null, 2)], { type: 'application/json' }));
a.download = 'Prompts.json';
a.click();
URL.revokeObjectURL(a.href);
closeExportModal();
};
document.getElementById('__ap_do_export_txt').onclick = async () => {
const selected = getSelectedPrompts();
if (selected.length === 0) { alert(getTranslation('noPromptsToExport')); return; }
if (selected.length > 10 && !confirm(getTranslation('confirmDownloads', { count: selected.length }))) return;
for (let i = 0; i < selected.length; i++) {
const p = selected[i];
const a = document.createElement('a');
const content = p.text;
a.href = URL.createObjectURL(new Blob([content], { type: 'text/plain' }));
let rawTitle = p.title || 'prompt';
const safeTitle = rawTitle.replace(/[<>:"/\\|?*]/g, '').trim();
a.download = `${safeTitle || 'prompt'}.txt`;
a.style.display = 'none';
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
await new Promise(r => setTimeout(r, 200));
}
closeExportModal();
};
setTimeout(() => searchInput.focus(), 100);
}
// ---ABRIR MENU DE EXPORTAR---
function exportPrompts() {
openExportMenu();
}
// ---AÇÃO DE IMPORTAÇÃO---
function importPrompts() {
const input=document.createElement('input');
input.type='file';
input.accept='.json';
input.onchange=e=>{
const file=e.target.files[0];
if(!file)return;
const reader=new FileReader();
reader.onload=async event=>{
try{
const imported=JSON.parse(event.target.result);
if(!Array.isArray(imported))throw new Error("Not an array.");
const current=await getAll();
const newPrompts = imported.map(p => ({
title: p.title || 'No Title',
text: p.text || '',
usePlaceholders: p.usePlaceholders || false,
autoExecute: p.autoExecute || false
}));
await GM_setValue(PROMPT_STORAGE_KEY,[...current,...newPrompts]);
await refreshMenu();
alert(getTranslation('promptsImported',{count:newPrompts.length}));
}catch(err){alert(getTranslation('errorImporting',{error:err.message}))}
};
reader.readAsText(file);
};
input.click();
closeMenu();
}
// ---LIMPAR---
function cleanup() {
if (currentButton) { currentButton.remove (); currentButton = null; }
if (currentMenu) { currentMenu.remove (); currentMenu = null; }
if (currentModal) { currentModal.remove (); currentModal = null; }
if (languageModal) { languageModal.remove (); languageModal = null; }
if (currentPlaceholderModal) { currentPlaceholderModal.remove(); currentPlaceholderModal = null; }
isInitialized = false;
}
// #endregion INTERAÇÃO COM A PÁGINA
// #region INJETAR E OBSERVAR
// ---INICIALIZAR INTERFACE---
async function initUI() {
let warningModal = null;
if (pageObserver) pageObserver.disconnect();
cleanup();
currentPlatform = detectPlatform();
if (!currentPlatform) return;
try {
let btn, elementToInsert, insertionPoint, insertionMethod = 'before';
// ---CHATGPT---
if (currentPlatform === 'chatgpt') {
insertionPoint = await waitFor('div[class*="[grid-area:leading]"]');
insertionPoint.style.display = 'flex';
insertionPoint.style.alignItems = 'center';
btn = createChatGPTButton();
elementToInsert = btn;
insertionMethod = 'append';
}
// ---DEEPSEEK---
else if (currentPlatform === 'deepseek') {
const container = await waitFor('div:has(> input[type="file"])').then(el => el.parentElement);
if (!container) return;
const uniqueButtonId = 'composer-button-prompts';
let existingBtn = container.querySelector(`[data-testid="${uniqueButtonId}"]`);
if (existingBtn) {
btn = existingBtn;
} else {
const nativeButtons = Array.from(container.querySelectorAll('button:not([data-testid="composer-button-prompts"])'));
if (nativeButtons.length === 0) return;
const refBtn = nativeButtons[nativeButtons.length - 1];
btn = createDeepSeekButton();
let cleanClass = refBtn.className.replace('ds-toggle-button--selected', '').trim();
btn.className = cleanClass;
refBtn.insertAdjacentElement('afterend', btn);
}
elementToInsert = btn;
insertionPoint = container;
insertionMethod = 'handled_manually';
}
// ---GOOGLE STUDIO---
else if (currentPlatform === 'googlestudio') {
insertionPoint = await waitFor('ms-add-chunk-menu', 5000).then(el => el.closest('.button-wrapper'));
const wrapper = document.createElement('div');
wrapper.className = 'button-wrapper';
btn = createGoogleStudioButton();
wrapper.appendChild(btn);
elementToInsert = wrapper;
const parent = insertionPoint.closest('.prompt-input-wrapper-container');
if (parent) parent.style.alignItems = 'center';
}
// ---QWEN---
else if (currentPlatform === 'qwen') {
const buttonContainer = await waitFor('.action-bar-left-btns', 5000);
if (!buttonContainer) return;
insertionPoint = buttonContainer.firstChild;
insertionMethod = 'before';
btn = createQwenButton();
elementToInsert = btn;
if (!buttonContainer.contains(btn)) {
buttonContainer.prepend(btn);
}
const qwenPositionObserver = new MutationObserver(() => {
if (document.body.contains(buttonContainer) && !buttonContainer.contains(btn)) {
buttonContainer.prepend(btn);
}
});
qwenPositionObserver.observe(buttonContainer, { childList: true, subtree: true });
}
// ---Z.AI---
else if (currentPlatform === 'zai') {
const referenceElement = await waitFor('button[data-autothink]', 8000);
if (referenceElement) {
insertionPoint = referenceElement.closest('.flex.gap-\\[8px\\].items-center') || referenceElement.parentElement?.parentElement;
if (insertionPoint) {
btn = createZaiButton();
elementToInsert = btn;
insertionMethod = 'append';
}
}
}
// ---GEMENI---
else if (currentPlatform === 'gemini') {
insertionPoint = await waitFor('uploader', 8000);
btn = createGeminiButton();
elementToInsert = btn;
insertionMethod = 'after';
const wrapper = insertionPoint.parentElement;
if (wrapper) {
wrapper.style.display = 'flex';
wrapper.style.alignItems = 'center';
wrapper.style.gap = '3px';
}
}
// ---LMARENA---
else if (currentPlatform === 'lmarena') {
insertionPoint = await waitFor('div[data-sentry-component="SelectChatModality"]', 8000);
btn = createLmarenaButton();
elementToInsert = btn;
insertionMethod = 'append';
}
// ---KIMI---
else if (currentPlatform === 'kimi') {
insertionPoint = await waitFor('div.left-area', 8000);
btn = createKimiButton();
elementToInsert = btn;
const ensureButtonIsPresent = () => {
if (!insertionPoint.contains(elementToInsert)) {
insertionPoint.appendChild(elementToInsert);
}
};
ensureButtonIsPresent();
const guardianObserver = new MutationObserver(() => {
ensureButtonIsPresent();
});
guardianObserver.observe(insertionPoint, { childList: true, subtree: true });
insertionMethod = 'handled_by_kimi_guardian';
}
// ---CLAUDE---
else if (currentPlatform === 'claude') {
insertionPoint = await waitFor('button[data-testid="input-menu-plus"]', 8000).then(el => el.closest('div.relative.shrink-0'));
btn = createClaudeButton();
elementToInsert = btn;
insertionMethod = 'after';
}
// ---GROK---
else if (currentPlatform === 'grok') {
const attachButtonSelector = 'button:has(svg > path[d^="M10 9V15C"])';
insertionPoint = await waitFor(attachButtonSelector);
btn = createGrokButton();
btn.className = insertionPoint.className;
btn.style.marginLeft = '-6px';
elementToInsert = btn;
insertionMethod = 'after';
}
// ---PERPLEXITY---
else if (currentPlatform === 'perplexity') {
const containerSelector = 'div.flex.items-center.justify-self-end.col-start-3.row-start-2';
const container = await waitFor(containerSelector);
const spanWrapper = createPerplexityButton();
elementToInsert = spanWrapper;
btn = spanWrapper.querySelector('button');
if (container.firstElementChild) {
insertionPoint = container.firstElementChild;
insertionMethod = 'before';
} else {
insertionPoint = container;
insertionMethod = 'append';
}
}
// ---DOUBAO---
else if (currentPlatform === 'doubao') {
const uploadSelectors = 'div[data-testid="custom-input-file-upload-button"], div[data-testid="write_chat_input_upload_button"]';
const codeSelector = 'button[data-testid="code-btn-prompt-optimize"]';
const micSelector = 'div[data-testid="asr_btn"]';
try {
await waitFor(uploadSelectors, 1500);
await new Promise(resolve => setTimeout(resolve, 300));
} catch (e) {
await waitFor(`${codeSelector}, ${micSelector}, ${uploadSelectors}`, 1500).catch(() => {});
await new Promise(resolve => setTimeout(resolve, 800));
await waitFor(uploadSelectors, 500).catch(() => {});
}
let target = null;
let method = 'after';
let variant = 'default';
target = document.querySelector(uploadSelectors);
if (!target) {
const codeBtn = document.querySelector(codeSelector);
if (codeBtn) {
target = codeBtn.closest('span') || codeBtn.parentElement;
method = 'after';
variant = 'minimal';
}
}
if (!target) {
const mic = document.querySelector(micSelector);
if (mic) {
target = mic.parentElement || mic.closest('div');
method = 'before';
variant = 'minimal';
}
}
if (target) {
const isFirefox = navigator.userAgent.toLowerCase().includes('firefox');
const isSafeSpot = target.matches('div[data-testid="custom-input-file-upload-button"]');
if (isFirefox && !isSafeSpot) {
const hideWarning = await GM_getValue('doubaoffwarning', false);
if (!hideWarning) {
insertionPoint = target;
btn = createDoubaoWarningButton(variant);
elementToInsert = btn;
insertionMethod = method;
}
} else {
insertionPoint = target;
btn = createDoubaoButton(variant);
elementToInsert = btn;
insertionMethod = method;
}
}
}
// ---LONGCAT---
else if (currentPlatform === 'longcat') {
insertionPoint = await waitFor('.chat-input-buttons .upload-button-content', 8000);
btn = createLongCatButton();
elementToInsert = btn;
insertionMethod = 'after';
}
// ---MISTRAL---
else if (currentPlatform === 'mistral') {
const fileBtn = await waitFor('[data-testid="attach-file-button"]', 8000);
insertionPoint = fileBtn.parentElement;
btn = createMistralButton();
elementToInsert = btn;
insertionMethod = 'after';
}
// ---TENCENT YUANBAO---
else if (currentPlatform === 'yuanbao') {
insertionPoint = await waitFor('.ybc-atomSelect-tools-wrapper', 8000);
if (insertionPoint && insertionPoint.parentElement) {
insertionPoint.parentElement.style.display = 'flex';
insertionPoint.parentElement.style.alignItems = 'center';
insertionPoint.parentElement.style.gap = '8px';
}
btn = createYuanbaoButton();
elementToInsert = btn;
insertionMethod = 'after';
}
// ---CHATGLM---
else if (currentPlatform === 'chatglm') {
let container = document.querySelector('div.options-container.flex.flex-y-center');
let targetType = 'original';
let anchor = null;
if (container) {
anchor = container.querySelector('.upload-image-wrap');
}
if (!container || !anchor) {
container = document.querySelector('div.options[data-v-7dc2591c]');
if (container) {
targetType = 'element1';
anchor = container.lastElementChild;
}
}
if (!container || !anchor) {
container = document.querySelector('div.options[data-v-7a34b085]');
if (container) {
targetType = 'element2';
anchor = container.lastElementChild;
}
}
if (!container || !anchor) {
container = await waitFor('.options, .options-container', 5000);
if (!container) return;
if (container.matches('[data-v-7dc2591c]')) {
targetType = 'element1';
anchor = container.lastElementChild;
} else if (container.matches('[data-v-7a34b085]')) {
targetType = 'element2';
anchor = container.lastElementChild;
} else {
targetType = 'original';
anchor = container.querySelector('.upload-image-wrap');
}
}
if (!container || !anchor) return;
btn = container.querySelector('[data-testid="composer-button-prompts"]');
if (!btn) {
btn = createChatGLMButton(targetType);
}
elementToInsert = btn;
insertionPoint = anchor;
insertionMethod = 'after';
}
// ---POE---
else if (currentPlatform === 'poe') {
insertionPoint = await waitFor('div[class*="ChatMessageInputContainer_actionContainerLeft"]', 8000);
btn = createPoeButton();
elementToInsert = btn;
insertionMethod = 'append';
}
// ---VERIFICAÇÃO FINAL---
if (!btn || !insertionPoint) return;
currentButton = elementToInsert;
const clickable = btn;
if (insertionMethod === 'append'){insertionPoint.appendChild(elementToInsert);}
else if (insertionMethod === 'before'){insertionPoint.parentNode.insertBefore(elementToInsert, insertionPoint);}
else if (insertionMethod === 'after' ){insertionPoint.parentNode.insertBefore(elementToInsert, insertionPoint.nextSibling);}
if (currentPlatform === 'grok' ){applyGrokPadding();}
else if (currentPlatform === 'chatglm'){applyChatGLMCustomStyles();}
// ---INICIALIZAR INTERFACE---
currentMenu = createPromptMenu();
currentModal = createPromptModal();
languageModal = createLanguageModal();
currentPlaceholderModal = createPlaceholderModal();
infoModal = createInfoModal();
warningModal = createFirefoxWarningModal();
document.body.appendChild(currentMenu);
document.body.appendChild(currentModal);
document.body.appendChild(languageModal);
document.body.appendChild(currentPlaceholderModal);
document.body.appendChild(infoModal);
document.body.appendChild(warningModal);
clickable.addEventListener('click', e => {
e.stopPropagation();
e.preventDefault();
if (clickable.querySelector('[data-is-warning="true"]')) {
showModal(warningModal);
return;
}
const menu = currentMenu;
if (menu.classList.contains('visible')) {
closeMenu();
return;
}
refreshMenu().then(() => {
positionMenu(menu, clickable);
menu.classList.add('visible');
setTimeout(() => {
const list = menu.querySelector('#prompt-menu-list-el');
if (list && list.updateScrollArrows) {
list.updateScrollArrows();
}
}, 250);
});
});
currentModal.querySelector('#__ap_save').onclick = async (e) => {
e.stopPropagation();
const index = parseInt(currentModal.dataset.index, 10);
const title = document.getElementById('__ap_title').value.trim();
const text = document.getElementById('__ap_text').value.trim();
const usePlaceholders = document.getElementById('__ap_use_placeholders').checked;
const autoExecute = document.getElementById('__ap_auto_execute').checked;
if (!title || !text) { alert(getTranslation('requiredFields')); return; }
const newItem = { title, text, usePlaceholders, autoExecute };
const op = index > -1 ? update(index, newItem) : addItem(newItem);
op.then(() => { hideModal(currentModal); refreshMenu(); });
};
currentModal.querySelector('#__ap_close_prompt').onclick = (e) => {
e.stopPropagation();
hideModal(currentModal);
};
// ---PROMPT DINÂMICO PT3---
currentPlaceholderModal.querySelector('#__ap_insert_prompt').onclick = async (e) => {
e.stopPropagation();
const parseData = JSON.parse(currentPlaceholderModal.dataset.parseData);
const originalItem = JSON.parse(currentPlaceholderModal.dataset.originalItem);
const index = parseInt(currentPlaceholderModal.dataset.index, 10);
let finalText = parseData.processedText;
const ignoreMap = new Map(parseData.ignoreMap);
const selectMap = new Map(parseData.selectMap);
const inputMap = new Map(parseData.inputMap);
const variablesToApply = [];
const container = document.getElementById('__ap_placeholders_container');
inputMap.forEach((data, key) => {
const inputEl = container.querySelector(`textarea[data-key="${key}"]`);
const val = inputEl ? inputEl.value : '';
finalText = finalText.replace(key, val);
if (typeof data === 'object' && data.varName) {
variablesToApply.push({ name: data.varName, value: val });
}
});
selectMap.forEach((data, key) => {
const group = container.querySelector(`div[data-select-key="${key}"]`);
const checked = Array.from(group.querySelectorAll('input:checked'));
const selectedText = checked.map(cb => cb.value).join('\n');
finalText = finalText.replace(key, selectedText);
});
variablesToApply.forEach(v => {
const escapedVar = v.name.replace(/\$/g, '\\$');
const varRegex = new RegExp(escapedVar, 'g');
finalText = finalText.replace(varRegex, v.value);
});
ignoreMap.forEach((content, key) => {
finalText = finalText.replace(key, content);
});
const finalPrompt = { ...originalItem, text: finalText };
await insertPrompt(finalPrompt, index);
hideModal(currentPlaceholderModal);
};
currentPlaceholderModal.querySelector('#__ap_close_placeholder').onclick = (e) => {
e.stopPropagation();
hideModal(currentPlaceholderModal);
};
currentModal.querySelector('#__ap_info_btn').onclick = (e) => {
e.stopPropagation();
showModal(infoModal);
};
infoModal.querySelector('#__ap_close_info').onclick = (e) => {
e.stopPropagation();
hideModal(infoModal);
};
isInitialized = true;
} catch (error) {
cleanup();
} finally {
setupPageObserver();
}
}
const debouncedTryInit = debounce(tryInit, 500);
// ---OBSERVAR A PÁGINA---
function setupPageObserver() {
if (pageObserver) pageObserver.disconnect();
pageObserver = new MutationObserver(() => {
if (!document.body.contains(currentButton)) {
debouncedTryInit();
}
});
pageObserver.observe(document.body, { childList: true, subtree: true });
}
// ---EVENTOS GLOBAIS---
function setupGlobalEventListeners() {
document.addEventListener('click', ev => {
if (!currentMenu || !currentButton) return;
if (ev.target.closest('#prompt-menu-container, [data-testid="composer-button-prompts"]')) return;
closeMenu();
});
document.addEventListener('keydown', ev => {
if (ev.key === 'Escape') {
closeMenu();
if (currentModal && currentModal.classList.contains('visible')) hideModal(currentModal);
if (languageModal && languageModal.classList.contains('visible')) hideModal(languageModal);
if (currentPlaceholderModal && currentPlaceholderModal.classList.contains('visible')) hideModal(currentPlaceholderModal);
}
});
window.addEventListener('resize', debounce(() => {
if (currentMenu && currentMenu.classList.contains('visible')) {
positionMenu(currentMenu, currentButton);
}
}, 100));
}
function tryInit() {
if (isInitializing) return;
if (isInitialized && currentButton && document.body.contains(currentButton) && currentPlatform === detectPlatform()) {
return;
}
isInitializing = true;
initUI().finally(() => { isInitializing = false; });
}
async function start() {
await determineLanguage();
injectGlobalStyles();
setupGlobalEventListeners();
GM_registerMenuCommand(getTranslation('languageSettings'), () => {
if (!languageModal) {
languageModal = createLanguageModal();
document.body.appendChild(languageModal);
}
showModal(languageModal);
});
tryInit();
}
start();
// #endregion
})();