更好的 Greasy Fork

通过多项改进使Greasy Fork更加实用且美观:在标题旁添加脚本图标;实现用于编辑描述和评论的完整HTML工具栏;创建新的脚本直接下载按钮;允许通过元数据进行自定义(颜色、版权、社交图标);以及其他界面和可用性优化。

当前为 2025-11-07 提交的版本,查看 最新版本

// ==UserScript==
// @name                Better Greasy Fork
// @name:pt-BR          Greasy Fork Aprimorado
// @name:zh-CN          更好的 Greasy Fork
// @name:zh-TW          更好的 Greasy Fork
// @name:en             Better Greasy Fork
// @name:es             Greasy Fork Mejorado
// @name:ja             改良版 Greasy Fork
// @name:ko             향상된 Greasy Fork
// @name:de             Verbesserter Greasy Fork
// @name:fr             Greasy Fork Amélioré
// @namespace           https://github.com/0H4S
// @version             1.7
// @description         Makes Greasy Fork more functional and visually appealing through various improvements: adds script icons next to titles; implements a complete HTML toolbar for editing descriptions and comments; creates a new direct script download button; allows customizations via metadata (colors, copyright, social icons); plus other interface and usability optimizations.
// @description:pt-BR   Torna o Greasy Fork mais funcional e visualmente agradável através de várias melhorias: adiciona ícones de scripts ao lado dos títulos; implementa uma barra de ferramentas HTML completa para editar descrições e comentários; cria um novo botão de download direto de scripts; permite personalizações via metadados (cores, copyright, ícones sociais); além de outras otimizações de interface e usabilidade.
// @description:zh-CN   通过多项改进使Greasy Fork更加实用且美观:在标题旁添加脚本图标;实现用于编辑描述和评论的完整HTML工具栏;创建新的脚本直接下载按钮;允许通过元数据进行自定义(颜色、版权、社交图标);以及其他界面和可用性优化。
// @description:zh-TW   透過多項改進使Greasy Fork更加實用且美觀:在標題旁新增腳本圖示;實作用於編輯描述和留言的完整HTML工具列;建立新的腳本直接下載按鈕;允許透過中繼資料進行自訂(顏色、版權、社群圖示);以及其他介面和可用性最佳化。
// @description:en      Makes Greasy Fork more functional and visually appealing through various improvements: adds script icons next to titles; implements a complete HTML toolbar for editing descriptions and comments; creates a new direct script download button; allows customizations via metadata (colors, copyright, social icons); plus other interface and usability optimizations.
// @description:es      Hace que Greasy Fork sea más funcional y visualmente atractivo mediante diversas mejoras: añade iconos de scripts junto a los títulos; implementa una barra de herramientas HTML completa para editar descripciones y comentarios; crea un nuevo botón de descarga directa de scripts; permite personalizaciones mediante metadatos (colores, copyright, iconos sociales); además de otras optimizaciones de interfaz y usabilidad.
// @description:ja      複数の改善によりGreasy Forkをより機能的で視覚的に魅力的にします:タイトルの横にスクリプトアイコンを追加、説明文やコメントを編集するための完全なHTMLツールバーを実装、スクリプトの新しい直接ダウンロードボタンを作成、メタデータによるカスタマイズ(色、著作権、ソーシャルアイコン)を可能にし、その他のインターフェースとユーザビリティの最適化も提供します。
// @description:ko      다양한 개선을 통해 Greasy Fork를 더욱 기능적이고 시각적으로 매력적으로 만듭니다: 제목 옆에 스크립트 아이콘 추가, 설명 및 댓글 편집을 위한 완전한 HTML 도구 모음 구현, 새로운 스크립트 직접 다운로드 버튼 생성, 메타데이터를 통한 사용자 지정(색상, 저작권, 소셜 아이콘) 허용, 기타 인터페이스 및 사용성 최적화 제공.
// @description:de      Macht Greasy Fork funktionaler und optisch ansprechender durch verschiedene Verbesserungen: fügt Script-Icons neben Titeln hinzu; implementiert eine vollständige HTML-Symbolleiste zum Bearbeiten von Beschreibungen und Kommentaren; erstellt eine neue Schaltfläche für direkten Script-Download; ermöglicht Anpassungen über Metadaten (Farben, Urheberrecht, Social-Icons); sowie weitere Interface- und Usability-Optimierungen.
// @description:fr      Rend Greasy Fork plus fonctionnel et visuellement agréable grâce à diverses améliorations : ajoute des icônes de scripts à côté des titres ; implémente une barre d'outils HTML complète pour éditer les descriptions et commentaires ; crée un nouveau bouton de téléchargement direct de scripts ; permet des personnalisations via les métadonnées (couleurs, copyright, icônes sociales) ; ainsi que d'autres optimisations d'interface et d'utilisabilité.
// @author              OHAS
// @license             CC-BY-NC-ND-4.0
// @copyright           2025 OHAS. All Rights Reserved.
// @match               https://greasyfork.org/*
// @icon                data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIyMDAiIGhlaWdodD0iMjAwIiB2aWV3Qm94PSIwIDAgMjQgMjQiIG92ZXJmbG93PSJ2aXNpYmxlIj4KICAgIDxzdHlsZT4KICAgICAgICAuZGVzZW5oYXItY29udG9ybm8gewogICAgICAgICAgICBzdHJva2UtZGFzaGFycmF5OiAxMDA7CiAgICAgICAgICAgIHN0cm9rZS1kYXNob2Zmc2V0OiAxMDA7CiAgICAgICAgICAgIGFuaW1hdGlvbjogZGVzZW5oYXItY29udG9ybm8gMTBzIGVhc2UtaW4tb3V0IGluZmluaXRlOwogICAgICAgIH0KCiAgICAgICAgLm9uZGEtcHJlZW5jaGltZW50byB7CiAgICAgICAgICAgIGFuaW1hdGlvbjogb25kYS1wcmVlbmNoaW1lbnRvIDEwcyBlYXNlLWluLW91dCBpbmZpbml0ZTsKICAgICAgICB9CgogICAgICAgIEBrZXlmcmFtZXMgZGVzZW5oYXItY29udG9ybm8gewogICAgICAgICAgICAwJSB7CiAgICAgICAgICAgICAgICBzdHJva2UtZGFzaG9mZnNldDogMTAwOwogICAgICAgICAgICAgICAgc3Ryb2tlOiAjZmZmZmZmZmY7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgMjUlIHsKICAgICAgICAgICAgICAgIHN0cm9rZS1kYXNob2Zmc2V0OiAwOwogICAgICAgICAgICAgICAgc3Ryb2tlOiAjZmZmZmZmZmY7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgNzUlIHsKICAgICAgICAgICAgICAgIHN0cm9rZS1kYXNob2Zmc2V0OiAwOwogICAgICAgICAgICAgICAgc3Ryb2tlOiAjZmZmZmZmZmY7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgMTAwJSB7CiAgICAgICAgICAgICAgICBzdHJva2UtZGFzaG9mZnNldDogLTEwMDsKICAgICAgICAgICAgICAgIHN0cm9rZTogI2ZmZmZmZmZmOwogICAgICAgICAgICB9CiAgICAgICAgfQoKICAgICAgICBAa2V5ZnJhbWVzIG9uZGEtcHJlZW5jaGltZW50byB7CiAgICAgICAgICAgIDAlIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlWSgyNHB4KTsKICAgICAgICAgICAgICAgIGZpbGw6ICNmZmZmZmZmZjsKICAgICAgICAgICAgfQogICAgICAgICAgICAyNSUgewogICAgICAgICAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKDBweCk7CiAgICAgICAgICAgICAgICBmaWxsOiAjZmZmZmZmZmY7CiAgICAgICAgICAgIH0KICAgICAgICAgICAgNzUlIHsKICAgICAgICAgICAgICAgIHRyYW5zZm9ybTogdHJhbnNsYXRlWSgwcHgpOwogICAgICAgICAgICAgICAgZmlsbDogI2ZmZmZmZmZmOwogICAgICAgICAgICB9CiAgICAgICAgICAgIDEwMCUgewogICAgICAgICAgICAgICAgdHJhbnNmb3JtOiB0cmFuc2xhdGVZKDI0cHgpOwogICAgICAgICAgICAgICAgZmlsbDogI2ZmZmZmZmZmOwogICAgICAgICAgICB9CiAgICAgICAgfQogICAgPC9zdHlsZT4KICAgIDxkZWZzPgogICAgICAgIDxjbGlwUGF0aCBpZD0ibW9sZGUtZ2FyZm8iPgogICAgICAgICAgICA8cGF0aCBkPSJNNS44OSAyLjIyN2EuMjguMjggMCAwIDEgLjI2Ni4wNzZsNS4wNjMgNS4wNjJjLjU0LjU0LjUwOSAxLjY1Mi0uMDMxIDIuMTkybDguNzcxIDguNzdjMS4zNTYgMS4zNTUtLjM2IDMuMDk3LTEuNzMgMS43MjhsLTguNzcyLTguNzdjLS41NC41NC0xLjY1MS41NzEtMi4xOTEuMDMxbC01LjA2My01LjA2Yy0uMzA0LS4zMDQuMzA0LS45MTEuNjA4LS42MDhsMy43MTQgMy43MTNMNy41OSA4LjI5N0wzLjg3NSA0LjU4MmMtLjMwNC0uMzA0LjMwNC0uOTExLjYwNy0uNjA3bDMuNzE1IDMuNzE0bDEuMDY3LTEuMDY2TDUuNTQ5IDIuOTFjLS4yMjgtLjIyOC4wNTctLjYyNi4zNDItLjY4M1oiLz4KICAgICAgICA8L2NsaXBQYXRoPgogICAgPC9kZWZzPgogICAgPGc+CiAgICAgICAgPHBhdGggZmlsbD0iIzAwMDAwMGZmIiBkPSJNMTIgMEM1LjM3NCAwIDAgNS4zNzUgMCAxMnM1LjM3NCAxMiAxMiAxMmM2LjYyNSAwIDEyLTUuMzc1IDEyLTEyUzE4LjYyNSAwIDEyIDAiLz4KICAgICAgICA8ZyBjbGlwLXBhdGg9InVybCgjbW9sZGUtZ2FyZm8pIj4KICAgICAgICAgICAgPHBhdGggY2xhc3M9Im9uZGEtcHJlZW5jaGltZW50byIgCiAgICAgICAgICAgICAgICAgIGQ9Ik0gLTIsMjQgTCAtMiwwIGMgNiwtNSA2LDUgMTIsMCBzIDYsLTUgMTIsMCBzIDYsLTUgMTIsMCBMIDMwLDI0IFoiLz4KICAgICAgICA8L2c+CiAgICAgICAgPHBhdGggY2xhc3M9ImRlc2VuaGFyLWNvbnRvcm5vIiBmaWxsPSJub25lIiBzdHJva2Utd2lkdGg9IjAuNSIgZD0iTTUuODkgMi4yMjdhLjI4LjI4IDAgMCAxIC4yNjYuMDc2bDUuMDYzIDUuMDYyYy41NC41NC41MDkgMS42NTItLjAzMSAyLjE5Mmw4Ljc3MSA4Ljc3YzEuMzU2IDEuMzU1LS4zNiAzLjA5Ny0xLjczIDEuNzI4bC04Ljc3Mi04Ljc3Yy0uNTQuNTQtMS42NTEuNTcxLTIuMTkxLjAzMWwtNS4wNjMtNS4wNmMtLjMwNC0uMzA0LjMwNC0uOTExLjYwOC0uNjA4bDMuNzE0IDMuNzEzTDcuNTkgOC4yOTdMMy44NzUgNC41ODJjLS4zMDQtLjMwNC4zMDQtLjkxMS42MDctLjYwN2wzLjcxNSAzLjcxNGwxLjA2Ny0xLjA2Nkw1LjU0OSAyLjkxYy0uMjI4LS4yMjguMDU3LS42MjYuMzQyLS42ODNaIi8+CiAgICA8L2c+Cjwvc3ZnPg==
// @resource            customCSS https://cdn.jsdelivr.net/gh/0H4S/Better-Greasy-Fork/estilo.css
// @resource            iconsJSON https://cdn.jsdelivr.net/gh/0H4S/Better-Greasy-Fork/icones.json
// @require             https://update.greasyfork.org/scripts/549920.js
// @connect             gist.github.com
// @connect             update.greasyfork.org
// @grant               GM_addStyle
// @grant               GM_getValue
// @grant               GM_setValue
// @grant               GM_deleteValue
// @grant               GM_xmlhttpRequest
// @grant               GM_getResourceText
// @grant               GM_registerMenuCommand
// @run-at              document-idle
// @noframes
// @compatible          chrome
// @compatible          firefox
// @compatible          edge
// @compatible          opera
// @bgf-colorLT         #0059ffff
// @bgf-colorDT         #ffffffff
// @bgf-copyright       [2025 OHAS. All Rights Reserved.](https://gist.github.com/0H4S/ae2fa82957a089576367e364cbf02438)
// @bgf-compatible      brave, mobile
// @bgf-social          https://github.com/0H4S, https://www.instagram.com/o_h_a_s
// @contributionURL     https://linktr.ee/0H4S
// ==/UserScript==

(function () {
    'use strict';
    // ================
    // #region GLOBAL
    // ================
    const allTranslations = {
        "en": {
            "langName":                         "English",
            "languageSettings":                 "🌐 Language",
            "close":                            "Close",
            "confirm":                          "Confirm",
            "cancel":                           "Cancel",
            "download":                         "Download",
            "compatible_with":                  "Compatible with",
            "force_update":                     "🔄️ Force Update",
            "force_update_alert":               "Cache cleared. The page will reload to fetch the updated data.",
            "titles":                           "Headings",
            "title_placeholder":                "Heading",
            "bold":                             "Bold",
            "bold_placeholder":                 "bold text",
            "italic":                           "Italic",
            "italic_placeholder":               "italic text",
            "underline":                        "Underline",
            "underline_placeholder":            "underlined text",
            "strikethrough":                    "Strikethrough",
            "strikethrough_placeholder":        "strikethrough text",
            "unordered_list":                   "Unordered List",
            "ordered_list":                     "Ordered List",
            "list_item_placeholder":            "Item",
            "quote":                            "Quote",
            "inline_code":                      "Inline Code",
            "inline_code_placeholder":          "code",
            "code_block":                       "Code Block",
            "code_block_placeholder":           "code here",
            "horizontal_line":                  "Horizontal Line",
            "horizontal_line_style":            "Horizontal Line Style",
            "prompt_hr_size":                   "Size (px)",
            "prompt_hr_color":                  "Color",
            "link":                             "Link",
            "prompt_insert_url":                "Enter the URL:",
            "link_text_placeholder":            "link text",
            "image":                            "Image",
            "prompt_insert_image_url":          "Enter the image URL (https):",
            "prompt_image_title":               "Image title (optional):",
            "image_title_placeholder":          "e.g. My beautiful image",
            "prompt_image_width":               "Width (optional):",
            "prompt_image_height":              "Height (optional):",
            "video":                            "Video",
            "prompt_video_type":                "Video Type",
            "video_type_embed":                 "Embed (YouTube, Bilibili)",
            "video_type_html5":                 "HTML5 Video (Direct URL)",
            "prompt_video_poster_url":          "Poster Image URL",
            "prompt_insert_video_url":          "Enter the video URL:",
            "prompt_video_width":               "Width (optional):",
            "prompt_video_height":              "Height (optional):",
            "alert_invalid_video_url":          "Invalid or unsupported video URL.",
            "table":                            "Table",
            "prompt_columns":                   "Number of columns:",
            "prompt_rows":                      "Number of rows:",
            "table_header_placeholder":         "Header",
            "table_cell_placeholder":           "Cell",
            "subscript":                        "Subscript",
            "subscript_placeholder":            "sub",
            "superscript":                      "Superscript",
            "superscript_placeholder":          "sup",
            "highlight":                        "Highlight",
            "highlight_placeholder":            "highlighted",
            "keyboard":                         "Keyboard",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "Abbreviation",
            "prompt_abbreviation_meaning":      "What does the abbreviation stand for?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "Text Color",
            "colored_text_placeholder":         "colored text",
            "background_color":                 "Background Color",
            "colored_background_placeholder":   "colored background",
            "details":                          "Collapsible section",
            "details_summary_placeholder":      "Summary or Title",
            "details_content_placeholder":      "Content to be hidden...",
            "center":                           "Center Align",
            "center_placeholder":               "centered text",
            "notFound":                         "Code not found!",
            "scriptIdNotFound":                 "Could not identify the script ID.",
            "downloading":                      "Downloading...",
            "downloadError":                    "An error occurred while downloading the script.",
            "downloadTimeout":                  "The script download timed out.",
            "info_tooltip":                     "Shortcuts",
            "info_shortcuts_title":             "Keyboard Shortcuts",
            "info_header_shortcut":             "Shortcut",
            "info_header_action":               "Action",
            "info_shortcut_tab":                "Inserts a tab space.",
            "info_shortcut_shift_enter":        "Inserts a line break <br>.",
            "info_shortcut_ctrl_d":             "Wraps the selection in a <div> tag.",
            "info_shortcut_ctrl_p":             "Wraps the selection in a <p> paragraph.",
            "info_shortcut_ctrl_m":             "Wraps the selection in a markdown code block.",
            "info_shortcut_ctrl_space":         "Inserts a non-breaking space  .",
            "prompt_link_text":                 "Link text:",
            "prompt_abbreviation_text":         "Abbreviation text:",
            "border_style":                     "Border Style",
            "prompt_border_size":               "Border size (px)",
            "prompt_border_color":              "Border color",
            "prompt_border_text":               "Text",
            "prompt_border_tag_type":           "Tag Type",
            "border_text_placeholder":          "Text"
        },
        "pt-BR": {
            "langName":                         "Português (BR)",
            "languageSettings":                 "🌐 Idioma",
            "close":                            "Fechar",
            "confirm":                          "Confirmar",
            "cancel":                           "Cancelar",
            "download":                         "Baixar",
            "compatible_with":                  "Compatível com",
            "force_update":                     "🔄️ Forçar Atualização",
            "force_update_alert":               "O cache foi limpo. A página será recarregada para buscar os dados atualizados.",
            "titles":                           "Títulos",
            "title_placeholder":                "Título",
            "bold":                             "Negrito",
            "bold_placeholder":                 "negrito",
            "italic":                           "Itálico",
            "italic_placeholder":               "itálico",
            "underline":                        "Sublinhado",
            "underline_placeholder":            "sublinhado",
            "strikethrough":                    "Riscado",
            "strikethrough_placeholder":        "riscado",
            "unordered_list":                   "Lista não ordenada",
            "ordered_list":                     "Lista ordenada",
            "list_item_placeholder":            "Item",
            "quote":                            "Citação",
            "inline_code":                      "Código Inline",
            "inline_code_placeholder":          "código",
            "code_block":                       "Bloco de Código",
            "code_block_placeholder":           "código aqui",
            "horizontal_line":                  "Linha Horizontal",
            "horizontal_line_style":            "Estilo da Linha Horizontal",
            "prompt_hr_size":                   "Tamanho (px)",
            "prompt_hr_color":                  "Cor",
            "link":                             "Link",
            "prompt_insert_url":                "Insira a URL:",
            "link_text_placeholder":            "texto do link",
            "image":                            "Imagem",
            "prompt_insert_image_url":          "Insira a URL da imagem (https):",
            "prompt_image_title":               "Título da imagem (opcional):",
            "image_title_placeholder":          "ex: Minha bela imagem",
            "prompt_image_width":               "Largura (opcional):",
            "prompt_image_height":              "Altura (opcional):",
            "video":                            "Vídeo",
            "prompt_video_type":                "Tipo de Vídeo",
            "video_type_embed":                 "Incorporado (YouTube, Bilibili)",
            "video_type_html5":                 "Vídeo HTML5 (URL direta)",
            "prompt_video_poster_url":          "URL da Imagem de Capa (poster)",
            "prompt_insert_video_url":          "Insira a URL do vídeo:",
            "prompt_video_width":               "Largura (opcional):",
            "prompt_video_height":              "Altura (opcional):",
            "alert_invalid_video_url":          "URL de vídeo inválida ou não suportada.",
            "table":                            "Tabela",
            "prompt_columns":                   "Número de colunas:",
            "prompt_rows":                      "Número de linhas:",
            "table_header_placeholder":         "Cabeçalho",
            "table_cell_placeholder":           "Célula",
            "subscript":                        "Subscrito",
            "subscript_placeholder":            "sub",
            "superscript":                      "Sobrescrito",
            "superscript_placeholder":          "sup",
            "highlight":                        "Marcação",
            "highlight_placeholder":            "marcado",
            "keyboard":                         "Teclado",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "Abreviação",
            "prompt_abbreviation_meaning":      "Qual o significado da abreviação?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "Cor do Texto",
            "colored_text_placeholder":         "texto colorido",
            "background_color":                 "Cor de Fundo",
            "colored_background_placeholder":   "fundo colorido",
            "details":                          "Seção Recolhível",
            "details_summary_placeholder":      "Resumo ou Título",
            "details_content_placeholder":      "Conteúdo a ser ocultado...",
            "center":                           "Centralizar",
            "center_placeholder":               "texto centralizado",
            "notFound":                         "Código não encontrado!",
            "scriptIdNotFound":                 "Não foi possível identificar o ID do script.",
            "downloading":                      "Baixando...",
            "downloadError":                    "Ocorreu um erro ao baixar o script.",
            "downloadTimeout":                  "O tempo para baixar o script esgotou.",
            "info_tooltip":                     "Atalhos",
            "info_shortcuts_title":             "Atalhos do Teclado",
            "info_header_shortcut":             "Atalho",
            "info_header_action":               "Ação",
            "info_shortcut_tab":                "Insere um espaço de tabulação.",
            "info_shortcut_shift_enter":        "Insere uma quebra de linha <br>.",
            "info_shortcut_ctrl_d":             "Envolve a seleção em uma tag <div>.",
            "info_shortcut_ctrl_p":             "Envolve a seleção em um parágrafo <p>.",
            "info_shortcut_ctrl_m":             "Envolve a seleção em um bloco de código markdown.",
            "info_shortcut_ctrl_space":         "Insere um espaço não separável  .",
            "prompt_link_text":                 "Texto do link:",
            "prompt_abbreviation_text":         "Texto da abreviação:",
            "border_style":                     "Estilo da Borda",
            "prompt_border_size":               "Tamanho da borda (px)",
            "prompt_border_color":              "Cor da borda",
            "prompt_border_text":               "Texto",
            "prompt_border_tag_type":           "Tipo de Tag",
            "border_text_placeholder":          "Texto"
        },
        "es": {
            "langName":                         "Español",
            "languageSettings":                 "🌐 Idioma",
            "close":                            "Cerrar",
            "confirm":                          "Confirmar",
            "cancel":                           "Cancelar",
            "download":                         "Descargar",
            "compatible_with":                  "Compatible con",
            "force_update":                     "🔄️ Forzar actualización",
            "force_update_alert":               "La caché se limpió. La página se recargará para obtener los datos actualizados.",
            "titles":                           "Títulos",
            "title_placeholder":                "Título",
            "bold":                             "Negrita",
            "bold_placeholder":                 "texto en negrita",
            "italic":                           "Cursiva",
            "italic_placeholder":               "texto en cursiva",
            "underline":                        "Subrayado",
            "underline_placeholder":            "texto subrayado",
            "strikethrough":                    "Tachado",
            "strikethrough_placeholder":        "texto tachado",
            "unordered_list":                   "Lista sin ordenar",
            "ordered_list":                     "Lista ordenada",
            "list_item_placeholder":            "Elemento",
            "quote":                            "Cita",
            "inline_code":                      "Código en línea",
            "inline_code_placeholder":          "código",
            "code_block":                       "Bloque de código",
            "code_block_placeholder":           "código aquí",
            "horizontal_line":                  "Línea horizontal",
            "horizontal_line_style":            "Estilo de Línea Horizontal",
            "prompt_hr_size":                   "Tamaño (px)",
            "prompt_hr_color":                  "Color",
            "link":                             "Enlace",
            "prompt_insert_url":                "Introduzca la URL:",
            "link_text_placeholder":            "texto del enlace",
            "image":                            "Imagen",
            "prompt_insert_image_url":          "Introduzca la URL de la imagen (https):",
            "prompt_image_title":               "Título de la imagen (opcional):",
            "image_title_placeholder":          "ej: Mi hermosa imagen",
            "prompt_image_width":               "Ancho (opcional):",
            "prompt_image_height":              "Alto (opcional):",
            "video":                            "Video",
            "prompt_video_type":                "Tipo de Video",
            "video_type_embed":                 "Incrustado (YouTube, Bilibili)",
            "video_type_html5":                 "Video HTML5 (URL directa)",
            "prompt_video_poster_url":          "URL de la imagen de portada (póster)",
            "prompt_insert_video_url":          "Introduzca la URL del video:",
            "prompt_video_width":               "Ancho (opcional):",
            "prompt_video_height":              "Alto (opcional):",
            "alert_invalid_video_url":          "URL de video no válida o no compatible.",
            "table":                            "Tabla",
            "prompt_columns":                   "Número de columnas:",
            "prompt_rows":                      "Número de filas:",
            "table_header_placeholder":         "Encabezado",
            "table_cell_placeholder":           "Celda",
            "subscript":                        "Subíndice",
            "subscript_placeholder":            "sub",
            "superscript":                      "Superíndice",
            "superscript_placeholder":          "sup",
            "highlight":                        "Resaltado",
            "highlight_placeholder":            "resaltado",
            "keyboard":                         "Teclado",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "Abreviatura",
            "prompt_abbreviation_meaning":      "¿Qué significa la abreviação?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "Color del texto",
            "colored_text_placeholder":         "texto coloreado",
            "background_color":                 "Color de fondo",
            "colored_background_placeholder":   "fondo coloreado",
            "details":                          "Sección Plegable",
            "details_summary_placeholder":      "Resumen o Título",
            "details_content_placeholder":      "Contenido a ocultar...",
            "center":                           "Centrar",
            "center_placeholder":               "texto centrado",
            "notFound":                         "¡Código no encontrado!",
            "scriptIdNotFound":                 "No se pudo identificar el ID del script.",
            "downloading":                      "Descargando...",
            "downloadError":                    "Ocurrió un error al descargar el script.",
            "downloadTimeout":                  "Se agotó el tiempo de espera para la descarga del script.",
            "info_tooltip":                     "Atajos",
            "info_shortcuts_title":             "Atajos de Teclado",
            "info_header_shortcut":             "Atajo",
            "info_header_action":               "Acción",
            "info_shortcut_tab":                "Inserta un espacio de tabulación.",
            "info_shortcut_shift_enter":        "Inserta un salto de línea <br>.",
            "info_shortcut_ctrl_d":             "Envuelve la selección en una etiqueta <div>.",
            "info_shortcut_ctrl_p":             "Envuelve la selección en un párrafo <p>.",
            "info_shortcut_ctrl_m":             "Envuelve la selección en un bloque de código markdown.",
            "info_shortcut_ctrl_space":         "Inserta un espacio no separable  .",
            "prompt_link_text":                 "Texto del enlace:",
            "prompt_abbreviation_text":         "Texto de la abreviatura:",
            "border_style":                     "Estilo de Borde",
            "prompt_border_size":               "Tamaño del borde (px)",
            "prompt_border_color":              "Color del borde",
            "prompt_border_text":               "Texto",
            "prompt_border_tag_type":           "Tipo de Etiqueta",
            "border_text_placeholder":          "Texto"
        },
        "fr": {
            "langName":                         "Français",
            "languageSettings":                 "🌐 Langue",
            "close":                            "Fermer",
            "confirm":                          "Confirmer",
            "cancel":                           "Annuler",
            "download":                         "Télécharger",
            "compatible_with":                  "Compatible avec",
            "force_update":                     "🔄️ Forcer la mise à jour",
            "force_update_alert":               "Cache vidé. La page va se recharger pour récupérer les données mises à jour.",
            "titles":                           "Titres",
            "title_placeholder":                "Titre",
            "bold":                             "Gras",
            "bold_placeholder":                 "texte en gras",
            "italic":                           "Italique",
            "italic_placeholder":               "texte en italique",
            "underline":                        "Souligné",
            "underline_placeholder":            "texte souligné",
            "strikethrough":                    "Barré",
            "strikethrough_placeholder":        "texte barré",
            "unordered_list":                   "Liste non ordonnée",
            "ordered_list":                     "Liste ordonnée",
            "list_item_placeholder":            "Élément",
            "quote":                            "Citation",
            "inline_code":                      "Code en ligne",
            "inline_code_placeholder":          "code",
            "code_block":                       "Bloc de code",
            "code_block_placeholder":           "code ici",
            "horizontal_line":                  "Ligne horizontale",
            "horizontal_line_style":            "Style de ligne horizontale",
            "prompt_hr_size":                   "Taille (px)",
            "prompt_hr_color":                  "Couleur",
            "link":                             "Lien",
            "prompt_insert_url":                "Entrez l'URL :",
            "link_text_placeholder":            "texte du lien",
            "image":                            "Image",
            "prompt_insert_image_url":          "Entrez l'URL de l'image (https) :",
            "prompt_image_title":               "Titre de l'image (facultatif) :",
            "image_title_placeholder":          "ex. Ma belle image",
            "prompt_image_width":               "Largeur (facultatif) :",
            "prompt_image_height":              "Hauteur (facultatif) :",
            "video":                            "Vidéo",
            "prompt_video_type":                "Type de vidéo",
            "video_type_embed":                 "Intégrée (YouTube, Bilibili)",
            "video_type_html5":                 "Vidéo HTML5 (URL directe)",
            "prompt_video_poster_url":          "URL de l'image d'affiche",
            "prompt_insert_video_url":          "Entrez l'URL de la vidéo :",
            "prompt_video_width":               "Largeur (facultatif) :",
            "prompt_video_height":              "Hauteur (facultatif) :",
            "alert_invalid_video_url":          "URL de vidéo invalide ou non prise en charge.",
            "table":                            "Tableau",
            "prompt_columns":                   "Nombre de colonnes :",
            "prompt_rows":                      "Nombre de lignes :",
            "table_header_placeholder":         "En-tête",
            "table_cell_placeholder":           "Cellule",
            "subscript":                        "Indice",
            "subscript_placeholder":            "ind",
            "superscript":                      "Exposant",
            "superscript_placeholder":          "exp",
            "highlight":                        "Surligner",
            "highlight_placeholder":            "surligné",
            "keyboard":                         "Clavier",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "Abréviation",
            "prompt_abbreviation_meaning":      "Que signifie l'abréviation ?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "Couleur du texte",
            "colored_text_placeholder":         "texte coloré",
            "background_color":                 "Couleur de fond",
            "colored_background_placeholder":   "fond coloré",
            "details":                          "Section réductible",
            "details_summary_placeholder":      "Résumé ou Titre",
            "details_content_placeholder":      "Contenu à masquer...",
            "center":                           "Aligner au centre",
            "center_placeholder":               "texte centré",
            "notFound":                         "Code non trouvé !",
            "scriptIdNotFound":                 "Impossible d'identifier l'ID du script.",
            "downloading":                      "Téléchargement en cours...",
            "downloadError":                    "Une erreur s'est produite lors du téléchargement du script.",
            "downloadTimeout":                  "Le téléchargement du script a expiré.",
            "info_tooltip":                     "Raccourcis",
            "info_shortcuts_title":             "Raccourcis clavier",
            "info_header_shortcut":             "Raccourci",
            "info_header_action":               "Action",
            "info_shortcut_tab":                "Insère une tabulation.",
            "info_shortcut_shift_enter":        "Insère un saut de ligne <br>.",
            "info_shortcut_ctrl_d":             "Enveloppe la sélection dans une balise <div>.",
            "info_shortcut_ctrl_p":             "Enveloppe la sélection dans un paragraphe <p>.",
            "info_shortcut_ctrl_m":             "Enveloppe la sélection dans un bloc de code markdown.",
            "info_shortcut_ctrl_space":         "Insère un espace insécable  .",
            "prompt_link_text":                 "Texte du lien :",
            "prompt_abbreviation_text":         "Texte de l'abréviation :",
            "border_style":                     "Style de bordure",
            "prompt_border_size":               "Taille de la bordure (px)",
            "prompt_border_color":              "Couleur de la bordure",
            "prompt_border_text":               "Texte",
            "prompt_border_tag_type":           "Type de balise",
            "border_text_placeholder":          "Texte"
        },
        "zh-CN": {
            "langName":                         "简体中文",
            "languageSettings":                 "🌐 语言",
            "close":                            "关闭",
            "confirm":                          "确认",
            "cancel":                           "取消",
            "download":                         "下载",
            "compatible_with":                  "兼容",
            "force_update":                     "🔄️ 强制更新",
            "force_update_alert":               "缓存已清除。页面将重新加载以获取最新数据。",
            "titles":                           "标题",
            "title_placeholder":                "标题",
            "bold":                             "粗体",
            "bold_placeholder":                 "粗体文本",
            "italic":                           "斜体",
            "italic_placeholder":               "斜体文本",
            "underline":                        "下划线",
            "underline_placeholder":            "下划线文本",
            "strikethrough":                    "删除线",
            "strikethrough_placeholder":        "删除线文本",
            "unordered_list":                   "无序列表",
            "ordered_list":                     "有序列表",
            "list_item_placeholder":            "项目",
            "quote":                            "引用",
            "inline_code":                      "行内代码",
            "inline_code_placeholder":          "代码",
            "code_block":                       "代码块",
            "code_block_placeholder":           "在此处编写代码",
            "horizontal_line":                  "水平线",
            "horizontal_line_style":            "水平线样式",
            "prompt_hr_size":                   "大小 (px)",
            "prompt_hr_color":                  "颜色",
            "link":                             "链接",
            "prompt_insert_url":                "请输入网址:",
            "link_text_placeholder":            "链接文本",
            "image":                            "图片",
            "prompt_insert_image_url":          "请输入图片网址 (https):",
            "prompt_image_title":               "图片标题(可选):",
            "image_title_placeholder":          "例如:我美丽的图片",
            "prompt_image_width":               "宽度(可选):",
            "prompt_image_height":              "高度(可选):",
            "video":                            "视频",
            "prompt_video_type":                "视频类型",
            "video_type_embed":                 "嵌入 (YouTube, 哔哩哔哩)",
            "video_type_html5":                 "HTML5 视频 (直接链接)",
            "prompt_video_poster_url":          "封面图片链接 (poster)",
            "prompt_insert_video_url":          "请输入视频网址:",
            "prompt_video_width":               "宽度(可选):",
            "prompt_video_height":              "高度(可选):",
            "alert_invalid_video_url":          "无效或不支持的视频网址。",
            "table":                            "表格",
            "prompt_columns":                   "列数:",
            "prompt_rows":                      "行数:",
            "table_header_placeholder":         "标题",
            "table_cell_placeholder":           "单元格",
            "subscript":                        "下标",
            "subscript_placeholder":            "下标",
            "superscript":                      "上标",
            "superscript_placeholder":          "上标",
            "highlight":                        "标记",
            "highlight_placeholder":            "标记",
            "keyboard":                         "键盘",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "缩写",
            "prompt_abbreviation_meaning":      "缩写的含义是什么?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "文字颜色",
            "colored_text_placeholder":         "彩色文本",
            "background_color":                 "背景颜色",
            "colored_background_placeholder":   "彩色背景",
            "details":                          "可折叠部分",
            "details_summary_placeholder":      "摘要或标题",
            "details_content_placeholder":      "要隐藏的内容...",
            "center":                           "居中",
            "center_placeholder":               "居中文字",
            "notFound":                         "未找到代码!",
            "scriptIdNotFound":                 "无法识别脚本 ID。",
            "downloading":                      "下载中...",
            "downloadError":                    "下载脚本时发生错误。",
            "downloadTimeout":                  "脚本下载超时。",
            "info_tooltip":                     "快捷方式",
            "info_shortcuts_title":             "键盘快捷键",
            "info_header_shortcut":             "快捷键",
            "info_header_action":               "功能",
            "info_shortcut_tab":                "插入一个制表符。",
            "info_shortcut_shift_enter":        "插入一个换行符 <br>。",
            "info_shortcut_ctrl_d":             "将所选内容包裹在 <div> 标签中。",
            "info_shortcut_ctrl_p":             "将所选内容包裹在 <p> 段落中。",
            "info_shortcut_ctrl_m":             "将所选内容包裹在 markdown 代码块中。",
            "info_shortcut_ctrl_space":         "插入不间断空格  。",
            "prompt_link_text":                 "链接文本:",
            "prompt_abbreviation_text":         "缩写文本:",
            "border_style":                     "边框样式",
            "prompt_border_size":               "边框大小 (px)",
            "prompt_border_color":              "边框颜色",
            "prompt_border_text":               "文本",
            "prompt_border_tag_type":           "标签类型",
            "border_text_placeholder":          "文本"
        },
        "ja": {
            "langName":                         "日本語",
            "languageSettings":                 "🌐 言語",
            "close":                            "閉じる",
            "confirm":                          "確認",
            "cancel":                           "キャンセル",
            "download":                         "ダウンロード",
            "compatible_with":                  "互換性:",
            "force_update":                     "🔄️ 強制アップデート",
            "force_update_alert":               "キャッシュがクリアされました。ページがリロードされ、更新されたデータが取得されます。",
            "titles":                           "見出し",
            "title_placeholder":                "見出し",
            "bold":                             "太字",
            "bold_placeholder":                 "太字のテキスト",
            "italic":                           "斜体",
            "italic_placeholder":               "斜体のテキスト",
            "underline":                        "下線",
            "underline_placeholder":            "下線付きテキスト",
            "strikethrough":                    "取り消し線",
            "strikethrough_placeholder":        "取り消し線付きテキスト",
            "unordered_list":                   "順序なしリスト",
            "ordered_list":                     "順序付きリスト",
            "list_item_placeholder":            "アイテム",
            "quote":                            "引用",
            "inline_code":                      "インラインコード",
            "inline_code_placeholder":          "コード",
            "code_block":                       "コードブロック",
            "code_block_placeholder":           "ここにコード",
            "horizontal_line":                  "水平線",
            "horizontal_line_style":            "水平線のスタイル",
            "prompt_hr_size":                   "サイズ (px)",
            "prompt_hr_color":                  "色",
            "link":                             "リンク",
            "prompt_insert_url":                "URLを入力してください:",
            "link_text_placeholder":            "リンクテキスト",
            "image":                            "画像",
            "prompt_insert_image_url":          "画像URLを入力してください (https):",
            "prompt_image_title":               "画像のタイトル (任意):",
            "image_title_placeholder":          "例: 私の美しい画像",
            "prompt_image_width":               "幅 (任意):",
            "prompt_image_height":              "高さ (任意):",
            "video":                            "動画",
            "prompt_video_type":                "動画タイプ",
            "video_type_embed":                 "埋め込み (YouTube, Bilibili)",
            "video_type_html5":                 "HTML5 ビデオ (直接URL)",
            "prompt_video_poster_url":          "ポスター画像のURL",
            "prompt_insert_video_url":          "動画URLを入力してください:",
            "prompt_video_width":               "幅 (任意):",
            "prompt_video_height":              "高さ (任意):",
            "alert_invalid_video_url":          "無効またはサポートされていない動画URLです。",
            "table":                            "表",
            "prompt_columns":                   "列数:",
            "prompt_rows":                      "行数:",
            "table_header_placeholder":         "ヘッダー",
            "table_cell_placeholder":           "セル",
            "subscript":                        "下付き文字",
            "subscript_placeholder":            "下付き",
            "superscript":                      "上付き文字",
            "superscript_placeholder":          "上付き",
            "highlight":                        "ハイライト",
            "highlight_placeholder":            "ハイライトされたテキスト",
            "keyboard":                         "キーボード",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "略語",
            "prompt_abbreviation_meaning":      "この略語は何の略ですか?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "文字色",
            "colored_text_placeholder":         "色付きテキスト",
            "background_color":                 "背景色",
            "colored_background_placeholder":   "色付き背景",
            "details":                          "折りたたみ可能なセクション",
            "details_summary_placeholder":      "概要またはタイトル",
            "details_content_placeholder":      "非表示にするコンテンツ...",
            "center":                           "中央揃え",
            "center_placeholder":               "中央揃えのテキスト",
            "notFound":                         "コードが見つかりません!",
            "scriptIdNotFound":                 "スクリプトIDを特定できませんでした。",
            "downloading":                      "ダウンロード中...",
            "downloadError":                    "スクリプトのダウンロード中にエラーが発生しました。",
            "downloadTimeout":                  "スクリプトのダウンロードがタイムアウトしました。",
            "info_tooltip":                     "ショートカット",
            "info_shortcuts_title":             "キーボードショートカット",
            "info_header_shortcut":             "ショートカット",
            "info_header_action":               "アクション",
            "info_shortcut_tab":                "タブスペースを挿入します。",
            "info_shortcut_shift_enter":        "改行 <br> を挿入します。",
            "info_shortcut_ctrl_d":             "選択範囲を <div> タグで囲みます。",
            "info_shortcut_ctrl_p":             "選択範囲を <p> 段落で囲みます。",
            "info_shortcut_ctrl_m":             "選択範囲を Markdown コードブロックで囲みます。",
            "info_shortcut_ctrl_space":         "改行しないスペース   を挿入します。",
            "prompt_link_text":                 "リンクテキスト:",
            "prompt_abbreviation_text":         "略語テキスト:",
            "border_style":                     "枠線のスタイル",
            "prompt_border_size":               "枠線のサイズ (px)",
            "prompt_border_color":              "枠線の色",
            "prompt_border_text":               "テキスト",
            "prompt_border_tag_type":           "タグタイプ",
            "border_text_placeholder":          "テキスト"
        },
        "ko": {
            "langName":                         "한국어",
            "languageSettings":                 "🌐 언어",
            "close":                            "닫기",
            "confirm":                          "확인",
            "cancel":                           "취소",
            "download":                         "다운로드",
            "compatible_with":                  "호환 가능:",
            "force_update":                     "🔄️ 강제 업데이트",
            "force_update_alert":               "캐시가 지워졌습니다. 페이지를 새로고침하여 업데이트된 데이터를 가져옵니다.",
            "titles":                           "제목",
            "title_placeholder":                "제목",
            "bold":                             "굵게",
            "bold_placeholder":                 "굵은 텍스트",
            "italic":                           "기울임꼴",
            "italic_placeholder":               "기울임꼴 텍스트",
            "underline":                        "밑줄",
            "underline_placeholder":            "밑줄 친 텍스트",
            "strikethrough":                    "취소선",
            "strikethrough_placeholder":        "취소선 텍스트",
            "unordered_list":                   "순서 없는 목록",
            "ordered_list":                     "순서 있는 목록",
            "list_item_placeholder":            "항목",
            "quote":                            "인용",
            "inline_code":                      "인라인 코드",
            "inline_code_placeholder":          "코드",
            "code_block":                       "코드 블록",
            "code_block_placeholder":           "여기에 코드",
            "horizontal_line":                  "가로줄",
            "horizontal_line_style":            "가로줄 스타일",
            "prompt_hr_size":                   "크기 (px)",
            "prompt_hr_color":                  "색상",
            "link":                             "링크",
            "prompt_insert_url":                "URL을 입력하세요:",
            "link_text_placeholder":            "링크 텍스트",
            "image":                            "이미지",
            "prompt_insert_image_url":          "이미지 URL을 입력하세요 (https):",
            "prompt_image_title":               "이미지 제목 (선택 사항):",
            "image_title_placeholder":          "예: 내 아름다운 이미지",
            "prompt_image_width":               "너비 (선택 사항):",
            "prompt_image_height":              "높이 (선택 사항):",
            "video":                            "동영상",
            "prompt_video_type":                "비디오 유형",
            "video_type_embed":                 "임베드 (YouTube, Bilibili)",
            "video_type_html5":                 "HTML5 비디오 (직접 URL)",
            "prompt_video_poster_url":          "포스터 이미지 URL",
            "prompt_insert_video_url":          "동영상 URL을 입력하세요:",
            "prompt_video_width":               "너비 (선택 사항):",
            "prompt_video_height":              "높이 (선택 사항):",
            "alert_invalid_video_url":          "잘못되었거나 지원되지 않는 동영상 URL입니다.",
            "table":                            "표",
            "prompt_columns":                   "열 수:",
            "prompt_rows":                      "행 수:",
            "table_header_placeholder":         "헤더",
            "table_cell_placeholder":           "셀",
            "subscript":                        "아래 첨자",
            "subscript_placeholder":            "아래 첨자",
            "superscript":                      "위 첨자",
            "superscript_placeholder":          "위 첨자",
            "highlight":                        "강조",
            "highlight_placeholder":            "강조된 텍스트",
            "keyboard":                         "키보드",
            "keyboard_placeholder":             "Ctrl+C",
            "abbreviation":                     "약어",
            "prompt_abbreviation_meaning":      "이 약어는 무엇을 의미합니까?",
            "abbreviation_placeholder":         "HTML",
            "text_color":                       "텍스트 색상",
            "colored_text_placeholder":         "색깔 있는 텍스트",
            "background_color":                 "배경색",
            "colored_background_placeholder":   "색깔 있는 배경",
            "details":                          "접을 수 있는 섹션",
            "details_summary_placeholder":      "요약 또는 제목",
            "details_content_placeholder":      "숨길 내용...",
            "center":                           "가운데 정렬",
            "center_placeholder":               "가운데 정렬된 텍스트",
            "notFound":                         "코드를 찾을 수 없습니다!",
            "scriptIdNotFound":                 "스크립트 ID를 식별할 수 없습니다.",
            "downloading":                      "다운로드 중...",
            "downloadError":                    "스크립트를 다운로드하는 동안 오류가 발생했습니다.",
            "downloadTimeout":                  "스크립트 다운로드 시간이 초과되었습니다.",
            "info_tooltip":                     "단축키",
            "info_shortcuts_title":             "키보드 단축키",
            "info_header_shortcut":             "단축키",
            "info_header_action":               "동작",
            "info_shortcut_tab":                "탭 공백을 삽입합니다.",
            "info_shortcut_shift_enter":        "줄 바꿈 <br>을 삽입합니다.",
            "info_shortcut_ctrl_d":             "선택 항목을 <div> 태그로 래핑합니다.",
            "info_shortcut_ctrl_p":             "선택한 내용을 <p> 문단으로 감쌉니다.",
            "info_shortcut_ctrl_m":             "선택한 내용을 마크다운 코드 블록으로 감쌉니다.",
            "info_shortcut_ctrl_space":         "줄 바꿈되지 않는 공백   을 삽입합니다.",
            "prompt_link_text":                 "링크 텍스트:",
            "prompt_abbreviation_text":         "약어 텍스트:",
            "border_style":                     "테두리 스타일",
            "prompt_border_size":               "테두리 크기 (px)",
            "prompt_border_color":              "테두리 색상",
            "prompt_border_text":               "텍스트",
            "prompt_border_tag_type":           "태그 유형",
            "border_text_placeholder":          "텍스트"
        }
    };

    const translations = allTranslations;
    const icons = JSON.parse(GM_getResourceText("iconsJSON"));
    const myCss = GM_getResourceText("customCSS");

    GM_addStyle(myCss);

    function capitalizeCompatItem(item) {
        return item.replace(/\b\w/g, char => char.toUpperCase());
    }

    let currentLang         = 'en';
    let languageModal       = null;
    const CACHE_KEY         = 'Values';
    const LANG_STORAGE_KEY  = 'UserScriptLang';
    const LAST_TAG_TYPE_KEY = 'Tag';
    const LAST_COLOR_KEY    = 'Color';

    const SCRIPT_CONFIG = {
    notificationsUrl:   'https://gist.github.com/0H4S/d55d216b4487d64c606abb5d4f097fe0',
    scriptVersion:      '1.7',
    };
    const notifier = new ScriptNotifier(SCRIPT_CONFIG);
    notifier.run();

    function getTranslation(key) {
        return translations[currentLang] ?.[key] || translations.en[key];
    }

    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('ja')) currentLang = 'ja';
        else if (browserLang.startsWith('ko')) currentLang = 'ko';
        else currentLang = 'en';
    }

    function registerLanguageMenu() {
        GM_registerMenuCommand(getTranslation('languageSettings'), () => {
            showModal(languageModal);
        });
    }

    function registerForceUpdateMenu() {
        GM_registerMenuCommand(getTranslation('force_update'), forceUpdate);
    }

    function showModal(modal) {
        if (!modal) return;
        modal.style.display = 'flex';
        setTimeout(() => {
            const box = modal.querySelector('.lang-modal-box');
            box.style.opacity = '1';
            box.style.transform = 'scale(1)';
        }, 10);
    }

    function hideModal(modal) {
        if (!modal) return;
        const box = modal.querySelector('.lang-modal-box');
        box.style.opacity = '0';
        box.style.transform = 'scale(0.95)';
        setTimeout(() => {
            modal.style.display = 'none';
        }, 200);
    }

    function createLanguageModal() {
        const overlay = document.createElement('div');
        overlay.className = 'lang-modal-overlay';
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                hideModal(overlay);
            }
        });
        const box = document.createElement('div');
        box.className = 'lang-modal-box';
        const buttonsContainer = document.createElement('div');
        buttonsContainer.className = 'lang-modal-buttons';
        Object.keys(translations).forEach(langKey => {
            const btn = document.createElement('button');
            btn.textContent = translations[langKey].langName;
            btn.onclick = async () => {
                await GM_setValue(LANG_STORAGE_KEY, langKey);
                window.location.reload();
            };
            buttonsContainer.appendChild(btn);
        });
        box.appendChild(buttonsContainer);
        overlay.appendChild(box);
        const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');

        function applyTheme(isDark) {
            box.classList.toggle('dark-theme', isDark);
            box.classList.toggle('light-theme', !isDark);
        }
        applyTheme(mediaQuery.matches);
        mediaQuery.addEventListener('change', e => applyTheme(e.matches));
        return overlay;
    }

    async function forceUpdate() {
        alert(getTranslation('force_update_alert'));
        await GM_deleteValue(CACHE_KEY);
        window.location.reload();
    }
    // #endregion

    // ================
    // #region ESTILIZAR
    // ================

    function isScriptPage() {
        const path = window.location.pathname;
        return /^\/([a-z]{2}(-[A-Z]{2})?\/)?scripts\/\d+-[^/]+$/.test(path);
    }

    function addAdditionalInfoSeparator() {
        const additionalInfo = document.getElementById('additional-info');
        if (additionalInfo && !additionalInfo.previousElementSibling?.matches('hr.bgs-info-separator')) {
            const hr = document.createElement('hr');
            hr.className = 'bgs-info-separator';
            additionalInfo.before(hr);
        }
    }

    function highlightScriptDescription() {
        const descriptionElements = document.querySelectorAll('#script-description, .script-description.description');
        descriptionElements.forEach(element => {
            const scriptLink = element.closest('article, li')?.querySelector('a.script-link');
            const path = scriptLink ? normalizeScriptPath(new URL(scriptLink.href).pathname) : normalizeScriptPath(window.location.pathname);
            if (element && element.parentElement.tagName !== 'BLOCKQUOTE') {
                const blockquoteWrapper = document.createElement('blockquote');
                blockquoteWrapper.className = 'script-description-blockquote';
                if (path) {
                    blockquoteWrapper.dataset.bgfPath = path;
                }
                element.parentNode.insertBefore(blockquoteWrapper, element);
                blockquoteWrapper.appendChild(element);
            }
        });
    }

    function makeDiscussionClickable() {
        document.querySelectorAll('.discussion-list-container').forEach(container => {
            container.removeEventListener('click', handleDiscussionClick);
            container.addEventListener('click', handleDiscussionClick);
        });
    }

    function handleDiscussionClick(e) {
        if (e.target.tagName === 'A' ||
            e.target.closest('a') ||
            e.target.closest('.user-link') ||
            e.target.closest('.badge-author') ||
            e.target.closest('.rating-icon')) {
            return;
        }
        const discussionLink = this.querySelector('.discussion-title');
        if (discussionLink && discussionLink.href) {
            window.location.href = discussionLink.href;
        }
    }

    function applySyntaxHighlighting() {
        document.querySelectorAll('pre code').forEach(block => {
            if (block.dataset.highlighted === 'true') { return; }
            const code = block.textContent;
            block.innerHTML = highlight(code);
            block.dataset.highlighted = 'true';
        });
    }

    function escapeHtml(str) {
        return str.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
    }

    function highlight(code) {
        const keywords = new Set(['const', 'let', 'var', 'function', 'return', 'if', 'else', 'for', 'while', 'of', 'in', 'async', 'await', 'try', 'catch', 'new', 'import', 'export', 'from', 'class', 'extends', 'super', 'true', 'false', 'null', 'undefined', 'document', 'window']);
        const tokens = [];
        let cursor = 0;

        const tokenDefinitions = [
            { type: 'url',              regex: /^(https?:\/\/[^\s"'`<>]+)/ },
            { type: 'comment-special',  regex: /^(\/\/[^\r\n]*)/ },
            { type: 'comment',          regex: /^(\/\*[\s\S]*?\*\/|<!--[\s\S]*?-->)/ },
            { type: 'string',           regex: /^(`(?:\\.|[^`])*`|"(?:\\.|[^"])*"|'(?:\\.|[^'])*')/ },
            { type: 'tag-punctuation',  regex: /^(&lt;\/?|\/&gt;|&gt;)/ },
            { type: 'tag-name',         regex: /^([\w-]+)/, context: (t) => { const l=t[t.length-1]; return l&&l.type==='tag-punctuation'&&l.content.startsWith('&lt;') }},
            { type: 'attribute',        regex: /^([\w-]+)/, context: (t) => { for(let i=t.length-1;i>=0;i--){const n=t[i];if(n.type==='tag-punctuation'&&n.content.includes('&gt;'))return!1;if(n.type==='tag-name')return!0;if(n.type==='whitespace')continue}return!1 }},
            { type: 'regex',            regex: /^(\/(?!\*)(?:[^\r\n/\\]|\\.)+\/[gimyus]*)/ },
            { type: 'number',           regex: /^\b-?(\d+(\.\d+)?)\b/ },
            { type: 'keyword',          regex: new RegExp(`^\\b(${Array.from(keywords).join('|')})\\b`) },
            { type: 'function',         regex: /^([a-zA-Z_][\w_]*)(?=\s*\()/ },
            { type: 'property',         regex: /^\.([a-zA-Z_][\w_]*)/ },
            { type: 'operator',         regex: /^(==?=?|!=?=?|=>|[+\-*/%&|^<>]=?|\?|:|=)/ },
            { type: 'punctuation',      regex: /^([,;(){}[\]])/ },
            { type: 'whitespace',       regex: /^\s+/ },
            { type: 'unknown',          regex: /^./ },
        ];
        let processedCode = escapeHtml(code);
        while (cursor < processedCode.length) {
            let matched = false;
            for (const def of tokenDefinitions) {
                if (def.context && !def.context(tokens)) { continue; }
                const match = def.regex.exec(processedCode.slice(cursor));
                if (match) {
                    const content = match[0];
                    if (def.type === 'function' && keywords.has(content)) { continue; }
                    tokens.push({ type: def.type, content });
                    cursor += content.length;
                    matched = true;
                    break;
                }
            }
            if (!matched) {
                 tokens.push({ type: 'unknown', content: processedCode[cursor] });
                 cursor++;
            }
        }
        for (let i = 0; i < tokens.length; i++) {
            if (tokens[i].type === 'string') {
                let nextToken = null;
                for(let j=i+1;j<tokens.length;j++){if(tokens[j].type!=='whitespace'){nextToken=tokens[j];break}}
                if (nextToken && nextToken.content === ':') { tokens[i].type = 'json-key'; }
            }
        }
        return tokens.map(token => {
            if (['whitespace', 'unknown', 'url'].includes(token.type)) return token.content;
            if (token.type === 'property') return `<span class="sh-punctuation">.</span><span class="sh-property">${token.content.slice(1)}</span>`;
            return `<span class="sh-${token.type}">${token.content}</span>`;
        }).join('');
    }
    // #endregion

    // ================
    // #region ÍCONES
    // ================

    let iconCache;
    const processedKeys = new Set();

    async function saveCache() {
        await GM_setValue(CACHE_KEY, iconCache);
    }

    function normalizeScriptPath(pathname) {
        let withoutLocale = pathname.replace(/^\/[a-z]{2}(?:-[A-Z]{2})?\//, '/');
        const match = withoutLocale.match(/^\/scripts\/\d+-.+?(?=\/|$)/);
        return match ? match[0] : null;
    }

    function extractScriptIdFromNormalizedPath(normalized) {
        const match = normalized.match(/\/scripts\/(\d+)-/);
        return match ? match[1] : null;
    }

    function createIconElement(src, isHeader = false) {
        const img = document.createElement('img');
        img.src = src;
        img.alt = '';
        if (isHeader) {
            img.style.cssText = `
                width: 80px;
                height: 80px;
                margin-right: 10px;
                vertical-align: middle;
                border-radius: 4px;
                object-fit: contain;
                pointer-events: none;
            `;
        } else {
            img.style.cssText = `
                width: 40px;
                height: 40px;
                margin-right: 8px;
                vertical-align: middle;
                border-radius: 3px;
                object-fit: contain;
                pointer-events: none;
            `;
        }
        img.loading = 'lazy';
        return img;
    }

    function extractMetadataFromContent(content) {
        if (typeof content !== 'string') return {};
        const metadata = {};
        const lines = content.split('\n');
        const supportedTags = new Set([ '@icon', '@bgf-colorLT', '@bgf-colorDT', '@bgf-compatible', '@bgf-copyright', '@bgf-social' ]);
        for (const line of lines) {
            const trimmedLine = line.trim();
            if (trimmedLine.startsWith('// ==/UserScript==')) break;
            if (!trimmedLine.startsWith('// @')) continue;
            const match = trimmedLine.match(/\/\/\s*(@[a-zA-Z0-9-]+)\s+(.+)/);
            if (!match) continue;
            const key = match[1];
            let value = match[2].trim();
            if (supportedTags.has(key) && !metadata.hasOwnProperty(key)) {
                if (key === '@bgf-colorLT' || key === '@bgf-colorDT') {
                    const colorRegex = /(#[0-9a-fA-F]{3,8}|(?:rgba?|hsla?)\s*\([^)]+\))/;
                    const colorMatch = value.match(colorRegex);
                    if (colorMatch) {
                        value = colorMatch[0];
                    } else {
                        value = value.split(',')[0].trim();
                    }
                }
                metadata[key] = value;
            }
        }
        return metadata;
    }

    function isValidIconUrl(url) {
        return url && (url.startsWith('http') || url.startsWith(''));
    }

    async function processScript(normalizedPath, targetElement, isHeader = false) {
        if (processedKeys.has(normalizedPath) && isHeader) {
            applyBfgFeatures(iconCache[normalizedPath]);
        }
        if (processedKeys.has(normalizedPath) && !isHeader) {
            const cached = iconCache[normalizedPath];
            if (cached && isValidIconUrl(cached.iconUrl)) {
                targetElement.prepend(createIconElement(cached.iconUrl, isHeader));
            }
            return;
        }
        processedKeys.add(normalizedPath);
        const cached = iconCache[normalizedPath];
        const now = Date.now();
        const applyColorToBlockquote = (metadata) => {
            const blockquotes = document.querySelectorAll(`blockquote.script-description-blockquote[data-bgf-path="${normalizedPath}"]`);
            if (blockquotes.length === 0) return;

            const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
            const colorToApply = isDarkMode ? metadata.bgfColorDT : metadata.bgfColorLT;

            blockquotes.forEach(bq => {
                if (colorToApply) {
                    bq.style.setProperty('border-left-color', colorToApply, 'important');
                } else {
                    bq.style.removeProperty('border-left-color');
                }
            });
        };
        if (cached && now - cached.ts < 7 * 24 * 60 * 60 * 1000) {
            if (isValidIconUrl(cached.iconUrl)) {
                targetElement.prepend(createIconElement(cached.iconUrl, isHeader));
            }
            applyColorToBlockquote(cached);
            if (isHeader) {
                applyBfgFeatures(cached);
            }
            return;
        }
        const scriptId = extractScriptIdFromNormalizedPath(normalizedPath);
        if (!scriptId) {
            iconCache[normalizedPath] = { ts: now };
            await saveCache();
            return;
        }
        const scriptUrl = `https://update.greasyfork.org/scripts/${scriptId}.js`;
        GM_xmlhttpRequest({
            method: 'GET',
            url: scriptUrl,
            timeout: 6000,
            onload: async function (res) {
                if (typeof res.responseText !== 'string') {
                    iconCache[normalizedPath] = { ts: now };
                    await saveCache();
                    return;
                }
                const rawMetadata = extractMetadataFromContent(res.responseText);
                const metadata = {
                    iconUrl:        rawMetadata['@icon']            || null,
                    bgfColorLT:     rawMetadata['@bgf-colorLT']     || null,
                    bgfColorDT:     rawMetadata['@bgf-colorDT']     || null,
                    bgfCompatible:  rawMetadata['@bgf-compatible']  || null,
                    bgfCopyright:   rawMetadata['@bgf-copyright']   || null,
                    bgfSocial:      rawMetadata['@bgf-social']      || null,
                    ts:             now
                };
                iconCache[normalizedPath] = metadata;
                await saveCache();
                if (isValidIconUrl(metadata.iconUrl)) {
                    targetElement.prepend(createIconElement(metadata.iconUrl, isHeader));
                }
                applyColorToBlockquote(metadata);
                if (isHeader) {
                    applyBfgFeatures(metadata);
                }
            },
            onerror: async function () {
                iconCache[normalizedPath] = { ts: now };
                await saveCache();
            }
        });
    }

    function handleScriptLink(linkEl) {
        if (linkEl._handled) return;
        linkEl._handled = true;
        const href = linkEl.getAttribute('href');
        if (!href || !href.startsWith('/')) return;
        try {
            const url = new URL(href, window.location.origin);
            const normalized = normalizeScriptPath(url.pathname);
            if (!normalized) return;
            setTimeout(() => processScript(normalized, linkEl, false), 0);
        } catch (e) {}
    }

    function handleMainHeaderH2() {
        const headers = document.querySelectorAll('header');
        for (const header of headers) {
            const h2 = header.querySelector('h2');
            const desc = header.querySelector('p.script-description');
            if (h2 && desc && !h2._handled) {
                h2._handled = true;
                const normalized = normalizeScriptPath(window.location.pathname);
                if (!normalized) return;
                setTimeout(() => processScript(normalized, h2, true), 0);
                break;
            }
        }
    }

    function processIconElements() {
        document.querySelectorAll('a.script-link:not([data-icon-processed])')
            .forEach(el => {
                el.setAttribute('data-icon-processed', '1');
                handleScriptLink(el);
            });
        handleMainHeaderH2();
    }
    // #endregion

    // ================
    // #region METADADOS
    // ================

    function applyBfgFeatures(metadata) {
        if (!metadata) return;
        applyBfgCompatibility(metadata.bgfCompatible);
        applyBfgCopyright(metadata.bgfCopyright);
        applyBfgSocial(metadata.bgfSocial);
    }

    function applyBfgCompatibility(compatValue) {
        if (!compatValue) return;
        const compatDd = document.querySelector('dd.script-show-compatibility');
        if (!compatDd) {
            return;
        }
        let compatContainer = compatDd.querySelector('span');
        if (!compatContainer) {
            compatContainer = document.createElement('span');
            compatDd.innerHTML = '';
            compatDd.appendChild(compatContainer);
        }
        const compatItems = compatValue.split(',').map(item => item.trim().toLowerCase());
        compatItems.forEach(item => {
            if (!icons[item] || compatContainer.querySelector(`.bgf-compat-${item}`)) {
                return;
            }
            const img = document.createElement('img');
            img.className = `browser-compatible bgf-compat-${item}`;
            const displayName = capitalizeCompatItem(item);
            img.alt = `${getTranslation('compatible_with')} ${displayName}`;
            img.title = `${getTranslation('compatible_with')} ${displayName}`;
            img.style.marginLeft = '1px';
            img.src = `data:image/svg+xml;utf8,${encodeURIComponent(icons[item])}`;
            compatContainer.appendChild(img);
        });
    }

    function reapplyAllBlockquoteColors() {
        const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
        const allBlockquotes = document.querySelectorAll('blockquote.script-description-blockquote[data-bgf-path]');
        allBlockquotes.forEach(bq => {
            const path = bq.dataset.bgfPath;
            if (!path || !iconCache[path]) return;
            const metadata = iconCache[path];
            const colorToApply = isDarkMode ? metadata.bgfColorDT : metadata.bgfColorLT;
            if (colorToApply) {
                bq.style.setProperty('border-left-color', colorToApply, 'important');
            } else {
                bq.style.removeProperty('border-left-color');
            }
        });
    }

    function setupThemeChangeListener() {
        const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
        mediaQuery.addEventListener('change', reapplyAllBlockquoteColors);
    }

    function applyBfgCopyright(copyrightValue) {
        if (!copyrightValue || document.querySelector('.script-show-copyright')) return;
        const copyrightRegex = /\[(.{1,50})\]\((https:\/\/gist\.github\.com\/[^)]+)\)/;
        const match = copyrightValue.match(copyrightRegex);
        if (!match) return;
        const licenseDd = document.querySelector('dd.script-show-license');
        if (!licenseDd) return;
        const text = match[1];
        const url = match[2];
        const copyrightDt = document.createElement('dt');
        copyrightDt.className = 'script-show-copyright';
        copyrightDt.innerHTML = '<span>Copyright</span>';
        const copyrightDd = document.createElement('dd');
        copyrightDd.className = 'script-show-copyright';
        copyrightDd.style.alignSelf = 'center';
        const link = document.createElement('a');
        link.href = url;
        link.textContent = text;
        link.target = '_blank';
        link.rel = 'noopener noreferrer';
        const span = document.createElement('span');
        span.appendChild(link);
        copyrightDd.appendChild(span);
        licenseDd.after(copyrightDt, copyrightDd);
    }

    function applyBfgSocial(socialValue) {
        if (!socialValue || document.querySelector('.script-show-social')) return;
        const authorDd = document.querySelector('dd.script-show-author');
        if (!authorDd) return;
        const socialDomainMap = {
            'instagram.com':    { icon: icons.instagram,    name: 'Instagram'   },
            'facebook.com':     { icon: icons.facebook,     name: 'Facebook'    },
            'x.com':            { icon: icons.x,            name: 'X / Twitter' },
            'youtube.com':      { icon: icons.youtube,      name: 'YouTube'     },
            'bilibili.com':     { icon: icons.bilibili,     name: 'Bilibili'    },
            'tiktok.com':       { icon: icons.tiktok,       name: 'TikTok'      },
            'douyin.com':       { icon: icons.tiktok,       name: 'Douyin'      },
            'github.com':       { icon: icons.github,       name: 'GitHub'      },
            'linkedin.com':     { icon: icons.linkedin,     name: 'LinkedIn'    },
        };
        const urls = socialValue.split(',').map(url => url.trim());
        const validLinks = [];
        let tiktokFamilyProcessed = false;
        urls.forEach(url => {
            try {
                const domain = new URL(url).hostname.replace('www.', '');
                if (socialDomainMap[domain]) {
                    if (domain === 'tiktok.com' || domain === 'douyin.com') {
                        if (tiktokFamilyProcessed) return;
                        tiktokFamilyProcessed = true;
                    }
                    validLinks.push({ url, ...socialDomainMap[domain] });
                }
            } catch (e) {}
        });
        if (validLinks.length === 0) return;
        const socialDt = document.createElement('dt');
        socialDt.className = 'script-show-social';
        socialDt.innerHTML = '<span>Social</span>';
        const socialDd = document.createElement('dd');
        socialDd.className = 'script-show-social';
        socialDd.style.cssText = 'display: flex; gap: 8px; align-items: center; align-self: center; z-index: 10;';
        validLinks.forEach(linkInfo => {
            const link = document.createElement('a');
            link.href = linkInfo.url;
            link.title = linkInfo.name;
            link.target = '_blank';
            link.rel = 'noopener noreferrer';
            link.innerHTML = linkInfo.icon;
            const svg = link.querySelector('svg');
            if (svg) {
                svg.style.width = '20px';
                svg.style.height = '20px';
                svg.style.verticalAlign = 'middle';
            }
            socialDd.appendChild(link);
        });
        authorDd.after(socialDt, socialDd);
    }
    // #endregion

    // ================
    // #region EDITOR HTML
    // ================

    function insertText(textarea, prefix, suffix = '', placeholder = '') {
        const start = textarea.selectionStart;
        const end = textarea.selectionEnd;
        const selected = textarea.value.substring(start, end);
        const text = selected || placeholder;
        if (!selected && !placeholder) {
            textarea.setRangeText(prefix + suffix, start, end);
            const cursorPosition = start + prefix.length;
            textarea.setSelectionRange(cursorPosition, cursorPosition);
        } else {
            textarea.setRangeText(prefix + text + suffix, start, end, selected ? 'end' : 'select');
        }
        textarea.focus();
    }

    function createToolbarButton(def) {
        const btn = document.createElement('button');
        btn.type = 'button';
        btn.className = 'txt-editor-toolbar-button';
        btn.dataset.tooltip = def.title;
        btn.innerHTML = def.icon || def.label;
        btn.addEventListener('click', e => {
            e.preventDefault();
            def.action();
        });
        return btn;
    }

    function showCustomAlert(message) {
        const overlay = document.createElement('div');
        overlay.className = 'custom-prompt-overlay';
        const modal = document.createElement('div');
        modal.className = 'custom-prompt-box custom-alert-box';
        const editorContainer = document.querySelector('.txt-editor-container');
        modal.classList.add(editorContainer && editorContainer.classList.contains('dark-theme') ? 'dark-theme' : 'light-theme');
        const messageP = document.createElement('p');
        messageP.textContent = message;
        const closeBtn = document.createElement('button');
        closeBtn.textContent = getTranslation('close');
        closeBtn.className = 'custom-prompt-confirm';
        closeBtn.onclick = () => document.body.removeChild(overlay);
        modal.append(messageP, closeBtn);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
        closeBtn.focus();
    }

    function showCustomPrompt({ inputs, onConfirm }) {
        const overlay = document.createElement('div');
        overlay.className = 'custom-prompt-overlay';
        const modal = document.createElement('div');
        modal.className = 'custom-prompt-box';
        const editorContainer = document.querySelector('.txt-editor-container');
        modal.classList.add(editorContainer && editorContainer.classList.contains('dark-theme') ? 'dark-theme' : 'light-theme');
        const form = document.createElement('form');
        const inputsMap = new Map();
        inputs.forEach(config => {
            const label = document.createElement('label');
            label.textContent = config.label;
            let field;
            if (config.type === 'select') {
                field = document.createElement('select');
                (config.options || []).forEach(opt => {
                    const option = document.createElement('option');
                    option.value = opt.value;
                    option.textContent = opt.text;
                    if (config.value && opt.value === config.value) {
                        option.selected = true;
                    }
                    field.appendChild(option);
                });
            } else {
                field = document.createElement('input');
                field.type = config.type || 'text';
                field.placeholder = config.placeholder || '';
                field.value = config.value || '';
                field.required = config.required !== false;
                if (config.type === 'number') field.min = '1';
            }
            label.appendChild(field);
            form.appendChild(label);
            inputsMap.set(config.id, field);
        });
        const buttons = document.createElement('div');
        buttons.className = 'custom-prompt-buttons';
        const confirmBtn = document.createElement('button');
        confirmBtn.type = 'submit';
        confirmBtn.textContent = getTranslation('confirm');
        confirmBtn.className = 'custom-prompt-confirm';
        const cancelBtn = document.createElement('button');
        cancelBtn.type = 'button';
        cancelBtn.textContent = getTranslation('cancel');
        cancelBtn.className = 'custom-prompt-cancel';
        cancelBtn.onclick = () => document.body.removeChild(overlay);
        form.onsubmit = (e) => {
            e.preventDefault();
            const results = {};
            for (const [id, inputElement] of inputsMap.entries()) {
                results[id] = inputElement.value;
            }
            onConfirm(results);
            document.body.removeChild(overlay);
        };
        buttons.append(confirmBtn, cancelBtn);
        form.appendChild(buttons);
        modal.appendChild(form);
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
        inputsMap.values().next().value.focus();
    }

    function showInfoModal() {
        const overlay = document.createElement('div');
        overlay.className = 'custom-prompt-overlay info-modal-overlay';
        overlay.style.display = 'flex';
        const modal = document.createElement('div');
        modal.className = 'custom-prompt-box info-modal-box';
        const editorContainer = document.querySelector('.txt-editor-container');
        modal.classList.add(editorContainer && editorContainer.classList.contains('dark-theme') ? 'dark-theme' : 'light-theme');
        modal.innerHTML = `
            <h2>${getTranslation('info_shortcuts_title')}</h2>
            <div class="info-shortcuts">
                <table>
                    <thead>
                        <tr>
                            <th>${getTranslation('info_header_shortcut')}</th>
                            <th>${getTranslation('info_header_action')}</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td><code>Tab</code></td>
                            <td>${getTranslation('info_shortcut_tab')}</td>
                        </tr>
                        <tr>
                            <td><code>Shift + Enter</code></td>
                            <td>${getTranslation('info_shortcut_shift_enter')}</td>
                        </tr>
                        <tr>
                            <td><code>Ctrl + D</code></td>
                            <td>${getTranslation('info_shortcut_ctrl_d')}</td>
                        </tr>
                        <tr>
                            <td><code>Ctrl + P</code></td>
                            <td>${getTranslation('info_shortcut_ctrl_p')}</td>
                        </tr>
                        <tr>
                            <td><code>Ctrl + M</code></td>
                            <td>${getTranslation('info_shortcut_ctrl_m')}</td>
                        </tr>
                        <tr>
                            <td><code>Ctrl + Space</code></td>
                            <td>${getTranslation('info_shortcut_ctrl_space')}</td>
                        </tr>
                    </tbody>
                </table>
            </div>
            <div class="custom-prompt-buttons">
                <button class="custom-prompt-cancel">${getTranslation('close')}</button>
            </div>
        `;
        overlay.appendChild(modal);
        document.body.appendChild(overlay);
        overlay.querySelector('.custom-prompt-cancel').onclick = () => document.body.removeChild(overlay);
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay) {
                document.body.removeChild(overlay);
            }
        });
    }

    async function createTextStyleEditor(textarea) {
        if (textarea.dataset.editorApplied) return;
        textarea.dataset.editorApplied = 'true';
        textarea.addEventListener('keydown', function(e) {
            if (e.key === 'Tab') {
                e.preventDefault();
                const start = this.selectionStart;
                const end = this.selectionEnd;
                this.setRangeText('   ', start, end, 'end');
            }
            if (e.shiftKey && e.key === 'Enter') {
                e.preventDefault();
                const start = this.selectionStart;
                const end = this.selectionEnd;
                this.setRangeText('\n<br>\n', start, end, 'end');
            }
            if (e.ctrlKey && !e.shiftKey && e.key.toLowerCase() === 'd') {
                e.preventDefault();
                insertText(this, '<div>\n', '\n</div>', '');
            }
            if (e.ctrlKey && !e.shiftKey && e.key.toLowerCase() === 'p') {
                e.preventDefault();
                insertText(this, '<p>', '</p>', '');
            }
            if (e.ctrlKey && !e.shiftKey && e.key.toLowerCase() === 'm') {
                e.preventDefault();
                insertText(this, '```\n', '\n```', '');
            }
            if (e.ctrlKey && !e.shiftKey && e.key === ' ') {
                e.preventDefault();
                insertText(this, '&nbsp;', '', '');
            }
        });
        const container = document.createElement('div');
        container.className = 'txt-editor-container';
        const toolbar = document.createElement('div');
        toolbar.className = 'txt-editor-toolbar';
        const mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');

        function applyTheme(isDark) {
            container.classList.toggle('dark-theme', isDark);
            container.classList.toggle('light-theme', !isDark);
        }
        applyTheme(mediaQuery.matches);
        mediaQuery.addEventListener('change', e => applyTheme(e.matches));
        const tools = [
            { type: 'select', title: getTranslation('titles'), options: { 'H1': '1', 'H2': '2', 'H3': '3', 'H4': '4', 'H5': '5', 'H6': '6' }, action: (val) => insertText(textarea, `<h${val}>`, `</h${val}>`, getTranslation('title_placeholder')) },
            { type: 'divider' },
            { title: getTranslation('bold'),            icon:   icons.bold,             action: () => insertText(textarea, '<strong>', '</strong>',         getTranslation('bold_placeholder')) },
            { title: getTranslation('italic'),          icon:   icons.italic,           action: () => insertText(textarea, '<em>', '</em>',                 getTranslation('italic_placeholder')) },
            { title: getTranslation('underline'),       icon:   icons.underline,        action: () => insertText(textarea, '<u>', '</u>',                   getTranslation('underline_placeholder')) },
            { title: getTranslation('strikethrough'),   icon:   icons.strikethrough,    action: () => insertText(textarea, '<s>', '</s>',                   getTranslation('strikethrough_placeholder')) },
            { type: 'divider' },
            { title: getTranslation('unordered_list'),  icon:   icons.ul,               action: () => { const start = textarea.selectionStart; const end = textarea.selectionEnd; const selection = textarea.value.substring(start, end); const items = selection ? selection.split('\n').map(line => `  <li>${line}</li>`).join('\n') : `  <li>${getTranslation('list_item_placeholder')}</li>`; const listHtml = `<ul>\n${items}\n</ul>`; textarea.setRangeText(listHtml, start, end, 'select'); textarea.focus(); } },
            { title: getTranslation('ordered_list'),    icon:   icons.ol,               action: () => { const start = textarea.selectionStart; const end = textarea.selectionEnd; const selection = textarea.value.substring(start, end); const items = selection ? selection.split('\n').map(line => `  <li>${line}</li>`).join('\n') : `  <li>${getTranslation('list_item_placeholder')}</li>`; const listHtml = `<ol>\n${items}\n</ol>`; textarea.setRangeText(listHtml, start, end, 'select');textarea.focus(); } },
            { title: getTranslation('details'),         label:  icons.details,          action: () => insertText(textarea, '\n<details>\n  <summary>' +     getTranslation('details_summary_placeholder') + '</summary>\n\n  ' + getTranslation('details_content_placeholder') + '\n\n</details>\n') },
            { title: getTranslation('center'),          label:  icons.center,           action: () => insertText(textarea, '\n<center>\n', '\n</center>',   getTranslation('center_placeholder')) },
            { type: 'divider' },
            { title: getTranslation('quote'),           icon:   icons.quote,            action: () => { const start = textarea.selectionStart; const end = textarea.selectionEnd; const selection = textarea.value.substring(start, end); const content = selection ? selection.replace(/\n/g, '<br>\n  ') : getTranslation('quote'); const quoteHtml = `\n<blockquote>\n  ${content}\n</blockquote>\n`; textarea.setRangeText(quoteHtml, start, end, 'select'); textarea.focus(); } },
            { title: getTranslation('inline_code'),     icon:   icons.code,             action: () => insertText(textarea, '<code>', '</code>',             getTranslation('inline_code_placeholder')) },
            { title: getTranslation('code_block'),      label:  icons.code_block,       action: () => insertText(textarea, '<pre><code>', '</code></pre>',  getTranslation('code_block_placeholder')) },
            { title: getTranslation('horizontal_line'), icon:   icons.hr,               action: () => insertText(textarea, '\n<hr>\n') },
            { type: 'divider' },
            { title: getTranslation('link'),            icon:   icons.link,             action: () => { const start = textarea.selectionStart; const end = textarea.selectionEnd; const selectedText = textarea.value.substring(start, end); showCustomPrompt({ inputs: [ { id: 'url', label: getTranslation('prompt_insert_url'), placeholder: 'https://', type: 'url', required: true }, { id: 'text', label: getTranslation('prompt_link_text'), placeholder: getTranslation('link_text_placeholder'), value: selectedText, required: false } ], onConfirm: ({ url, text }) => { if (url) { const linkText = text || selectedText || url; const selectionMode = selectedText ? 'end' : 'select'; textarea.setRangeText(`<a href="${url}">${linkText}</a>`, start, end, selectionMode); textarea.focus(); } } }); } },
            { title: getTranslation('image'),           icon:   icons.image,            action: () => showCustomPrompt({ inputs: [ { id: 'src', label:      getTranslation('prompt_insert_image_url'), placeholder: 'https://', type: 'url' }, { id: 'title', label: getTranslation('prompt_image_title'), placeholder: getTranslation('image_title_placeholder'), required: false }, { id: 'width', label: getTranslation('prompt_image_width'), placeholder: '500px', type: 'number', required: false }, { id: 'height', label: getTranslation('prompt_image_height'), placeholder: '500px', type: 'number', required: false } ], onConfirm: ({ src, title, width, height }) => { if (src) { const titleAttr = title ? ` title="${title}"` : ''; const widthAttr = width ? ` width="${width}"` : ''; const heightAttr = height ? ` height="${height}"` : ''; insertText(textarea, `<img src="${src}"${titleAttr}${widthAttr}${heightAttr}>`); } } }) },
            { title: getTranslation('table'),           icon:   icons.table,            action: () => showCustomPrompt({ inputs: [ { id: 'cols', label:     getTranslation('prompt_columns'), type: 'number', value: '3' }, { id: 'rows', label: getTranslation('prompt_rows'), type: 'number', value: '2' } ], onConfirm: ({ cols, rows }) => { const numCols = parseInt(cols, 10) || 3; const numRows = parseInt(rows, 10) || 2; let table = '<table>\n  <thead>\n    <tr>\n'; table += '      ' + Array(numCols).fill(`<th>${getTranslation('table_header_placeholder')}</th>`).join('\n      ') + '\n    </tr>\n  </thead>\n  <tbody>\n'; for (let i = 0; i < numRows; i++) { table += '    <tr>\n'; table += '      ' + Array(numCols).fill(`<td>${getTranslation('table_cell_placeholder')}</td>`).join('\n      ') + '\n    </tr>\n'; } table += '  </tbody>\n</table>'; insertText(textarea, `\n${table}\n`); } }) },
            { title: getTranslation('video'),           icon:   icons.video,            action: () => showCustomPrompt({ inputs: [ { id: 'type', label:     getTranslation('prompt_video_type'), type: 'select', options: [ { value: 'embed', text: getTranslation('video_type_embed') }, { value: 'html5', text: getTranslation('video_type_html5') } ]}, { id: 'url', label: getTranslation('prompt_insert_video_url'), placeholder: 'https://', type: 'url' }, { id: 'poster', label: getTranslation('prompt_video_poster_url'), placeholder: 'https://image.jpg', type: 'url', required: false }, { id: 'width', label: getTranslation('prompt_video_width'), placeholder: '560', type: 'number', required: false }, { id: 'height', label: getTranslation('prompt_video_height'), placeholder: '315', type: 'number', required: false } ], onConfirm: ({ type, url, poster, width, height }) => { if (!url) return; const widthAttr = width ? ` width="${width}"` : ''; const heightAttr = height ? ` height="${height}"` : ''; if (type === 'html5') { const posterAttr = poster ? ` poster="${poster}"` : ''; const videoTag = `\n<video src="${url}"${posterAttr}${widthAttr}${heightAttr} controls></video>\n`; insertText(textarea, videoTag); } else { let src = ''; try { if (url.includes('youtube.com/watch?v=')) { src = `https://www.youtube.com/embed/${new URL(url).searchParams.get('v')}`; } else if (url.includes('youtu.be/')) { src = `https://www.youtube.com/embed/${new URL(url).pathname.substring(1)}`; } else if (url.includes('bilibili.com/video/')) { src = `https://player.bilibili.com/player.html?bvid=${new URL(url).pathname.split('/')[2]}`; } } catch { src = ''; } if (src) { insertText(textarea, `\n<iframe src="${src}"${widthAttr}${heightAttr} allowfullscreen></iframe>\n`); } else { showCustomAlert(getTranslation('alert_invalid_video_url')); } } } }) },
            { type: 'divider' },
            { title: getTranslation('subscript'),       label:  icons.subscript,        action: () => insertText(textarea, '<sub>', '</sub>',               getTranslation('subscript_placeholder')) },
            { title: getTranslation('superscript'),     label:  icons.superscript,      action: () => insertText(textarea, '<sup>', '</sup>',               getTranslation('superscript_placeholder')) },
            { title: getTranslation('highlight'),       label:  icons.highlight,        action: () => insertText(textarea, '<mark>', '</mark>',             getTranslation('highlight_placeholder')) },
            { title: getTranslation('keyboard'),        label:  icons.keyboard,         action: () => insertText(textarea, '<kbd>', '</kbd>',               getTranslation('keyboard_placeholder')) },
            { title: getTranslation('abbreviation'),    label:  icons.abbreviation,     action: () => { const start = textarea.selectionStart; const end = textarea.selectionEnd; const selectedText = textarea.value.substring(start, end); showCustomPrompt({ inputs: [ { id: 'title', label: getTranslation('prompt_abbreviation_meaning'), required: true }, { id: 'text', label: getTranslation('prompt_abbreviation_text'), placeholder: getTranslation('abbreviation_placeholder'), value: selectedText, required: true } ], onConfirm: ({ title, text }) => { if (title && text) { const selectionMode = selectedText ? 'end' : 'select'; textarea.setRangeText(`<abbr title="${title}">${text}</abbr>`, start, end, selectionMode); textarea.focus(); } } }); } },
            { type: 'color-picker' }
        ];
        for (const tool of tools) {
            if (tool.type === 'divider') {
                const div = document.createElement('div');
                div.className = 'txt-editor-toolbar-divider';
                toolbar.appendChild(div);
            } else if (tool.type === 'select') {
                const container = document.createElement('span');
                container.className = 'txt-editor-toolbar-button';
                container.dataset.tooltip = tool.title;
                container.style.position = 'relative';
                container.style.display = 'flex';
                container.style.alignItems = 'center';
                container.style.justifyContent = 'center';
                container.innerHTML = icons.h;
                const select = document.createElement('select');
                select.className = 'txt-editor-toolbar-select';
                select.style.cssText = ` -webkit-appearance: none; appearance: none; background: transparent; border: none; color: transparent; position: absolute; top: 0; left: 0; width: 100%; height: 100%; cursor: pointer; `;
                const placeholderOpt = document.createElement('option');
                placeholderOpt.value = '';
                placeholderOpt.textContent = '';
                placeholderOpt.disabled = true;
                placeholderOpt.selected = true;
                placeholderOpt.style.display = 'none';
                select.appendChild(placeholderOpt);
                Object.keys(tool.options).forEach(key => {
                    const opt = document.createElement('option');
                    opt.value = tool.options[key];
                    opt.textContent = key;
                    select.appendChild(opt);
                });
                select.addEventListener('change', () => {
                    if (select.value) tool.action(select.value);
                    select.selectedIndex = 0;
                });
                container.appendChild(select);
                toolbar.appendChild(container);
            } else if (tool.type === 'color-picker') {
                const colorContainer = document.createElement('div');
                colorContainer.className = 'txt-color-picker-container';
                const input = document.createElement('input');
                input.type = 'color';
                input.className = 'txt-color-picker-input';
                const lastColor = await GM_getValue(LAST_COLOR_KEY, '#58a6ff');
                input.value = lastColor;
                input.addEventListener('input', async (e) => {
                    await GM_setValue(LAST_COLOR_KEY, e.target.value);
                });
                const colorBtn = createToolbarButton({
                    title: getTranslation('text_color'),
                    label: icons.text_color,
                    action: () => insertText(textarea, `<span style="color: ${input.value};">`, '</span>', getTranslation('colored_text_placeholder'))
                });
                const bgBtn = createToolbarButton({
                    title: getTranslation('background_color'),
                    label: icons.background_color,
                    action: async () => {
                        const start = textarea.selectionStart;
                        const end = textarea.selectionEnd;
                        const selectedText = textarea.value.substring(start, end);
                        const lastTag = await GM_getValue(LAST_TAG_TYPE_KEY, 'span');
                        showCustomPrompt({
                            inputs: [
                                { id: 'text',  label: getTranslation('prompt_border_text'),     type: 'text',   value: selectedText || getTranslation('colored_background_placeholder') },
                                { id: 'color', label: getTranslation('background_color'),       type: 'text',   value: input.value },
                                { id: 'tag',   label: getTranslation('prompt_border_tag_type'), type: 'select', value: lastTag, options: [ { value: 'span', text: '<span>' }, { value: 'div', text: '<div>' } ] }
                            ],
                            onConfirm: async ({ text, color, tag }) => {
                                await GM_setValue(LAST_TAG_TYPE_KEY, tag);
                                let newElement;
                                if (tag === 'div') {
                                    newElement = `\n<div style="background-color: ${color};">\n   ${text}\n</div>\n`;
                                } else {
                                    newElement = `<span style="background-color: ${color};">${text}</span>`;
                                }
                                textarea.setRangeText(newElement, start, end, selectedText ? 'end' : 'select');
                                textarea.focus();
                            }
                        });
                    }
                });
                const hrStyleBtn = createToolbarButton({
                    title: getTranslation('horizontal_line_style'),
                    label: icons.hr_style,
                    action: () => {
                        const start = textarea.selectionStart;
                        const end = textarea.selectionEnd;
                        const selectedText = textarea.value.substring(start, end).trim();
                        let currentSize = '1';
                        let currentColor = input.value;
                        const hrRegex = /<hr\s+style="border:\s*(\d+)px\s+solid\s+([^;"]+)/i;
                        const match = selectedText.match(hrRegex);
                        if (match && selectedText.startsWith('<hr')) {
                            currentSize = match[1];
                            currentColor = match[2];
                        }
                        showCustomPrompt({
                            inputs: [
                                { id: 'size',  label: getTranslation('prompt_hr_size'),     type: 'number', value: currentSize  },
                                { id: 'color', label: getTranslation('prompt_hr_color'),    type: 'text',   value: currentColor }
                            ],
                            onConfirm: ({ size, color }) => {
                                const newHr = `<hr style="border: ${size}px solid ${color};">`;
                                textarea.setRangeText(newHr, start, end, 'select');
                                textarea.focus();
                            }
                        });
                    }
                });
                const borderStyleBtn = createToolbarButton({
                    title: getTranslation('border_style'),
                    label: icons.border,
                    action: async () => {
                        const start = textarea.selectionStart;
                        const end = textarea.selectionEnd;
                        const selectedText = textarea.value.substring(start, end);
                        const lastTag = await GM_getValue(LAST_TAG_TYPE_KEY, 'span');
                        showCustomPrompt({
                            inputs: [
                                { id: 'text',  label: getTranslation('prompt_border_text'),     type: 'text',   value: selectedText || getTranslation('border_text_placeholder') },
                                { id: 'size',  label: getTranslation('prompt_border_size'),     type: 'number', value: '1' },
                                { id: 'color', label: getTranslation('prompt_border_color'),    type: 'text',   value: input.value },
                                { id: 'tag',   label: getTranslation('prompt_border_tag_type'), type: 'select', value: lastTag, options: [ { value: 'span', text: '<span>' }, { value: 'div', text: '<div>' } ] }
                            ],
                            onConfirm: async ({ text, size, color, tag }) => {
                                await GM_setValue(LAST_TAG_TYPE_KEY, tag);
                                let newElement;
                                if (tag === 'div') {
                                    newElement = `\n<div style="border: ${size}px solid ${color};">\n   ${text}\n</div>`;
                                } else {
                                    newElement = `<span style="border: ${size}px solid ${color};">${text}</span>`;
                                }
                                textarea.setRangeText(newElement, start, end, selectedText ? 'end' : 'select');
                                textarea.focus();
                            }
                        });
                    }
                });

                colorContainer.append(input, colorBtn, bgBtn, hrStyleBtn, borderStyleBtn);
                toolbar.appendChild(colorContainer);
            } else {
                toolbar.appendChild(createToolbarButton(tool));
            }
        }
        const infoButton = createToolbarButton({
            title: getTranslation('info_tooltip'),
            icon: icons.info,
            action: showInfoModal
        });
        infoButton.style.marginLeft = 'auto';
        toolbar.appendChild(infoButton);
        textarea.parentNode.insertBefore(container, textarea);
        container.append(toolbar, textarea);
    }

    function applyToAllTextareas() {
        const textareas = document.querySelectorAll('textarea:not(#script_version_code):not([data-editor-applied])');
        textareas.forEach(createTextStyleEditor);
    }

    function enableSourceEditorCheckbox() {
        const enableCheckbox = () => {
            const checkbox = document.getElementById('enable-source-editor-code');
            if (checkbox && !checkbox.checked) {
                checkbox.checked = true;
                const event = new Event('change', {
                    bubbles: true
                });
                checkbox.dispatchEvent(event);
            }
        };
        enableCheckbox();
        const observer = new MutationObserver((mutationsList, observer) => {
            for (const mutation of mutationsList) {
                if (mutation.type === 'childList') {
                    const checkbox = document.getElementById('enable-source-editor-code');
                    if (checkbox) {
                        enableCheckbox();
                        observer.disconnect();
                        break;
                    }
                }
            }
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }

    function isMarkdownPage() {
        const path = window.location.pathname;
        const markdownSegments = [ '/new', '/edit', '/feedback', '/discussions' ];
        if (path.includes('/sets/')) {
            return false;
        }
        return markdownSegments.some(segment => path.includes(segment));
    }
    // #endregion

    // ================
    // #region DOWNLOAD
    // ================

    function isCodePage() {
        return /^\/([a-z]{2}(-[A-Z]{2})?\/)?scripts\/\d+-.+\/code/.test(window.location.pathname);
    }

    function initializeDownloadButton() {
        const waitFor = (sel) =>
            new Promise((resolve) => {
                const el = document.querySelector(sel);
                if (el) return resolve(el);
                const obs = new MutationObserver(() => {
                    const el = document.querySelector(sel);
                    if (el) {
                        obs.disconnect();
                        resolve(el);
                    }
                });
                obs.observe(document, { childList: true, subtree: true });
            });

        waitFor('label[for="wrap-lines"]').then((label) => {
            const wrapLinesCheckbox = document.getElementById('wrap-lines');
            if (wrapLinesCheckbox) {
                wrapLinesCheckbox.checked = false;
            }
            const toolbar = label.parentElement;
            const btn = document.createElement('button');
            btn.className = 'btn';
            btn.textContent = getTranslation('download');
            btn.style.marginLeft = '12px';
            btn.style.backgroundColor = '#005200';
            btn.style.color = 'white';
            btn.style.border = 'none';
            btn.style.padding = '6px 16px';
            btn.style.borderRadius = '4px';
            btn.style.cursor = 'pointer';
            btn.addEventListener('mouseenter', () => btn.style.backgroundColor = '#1e971e');
            btn.addEventListener('mouseleave', () => btn.style.backgroundColor = '#005200');

            btn.addEventListener('click', () => {
                const normalizedPath = normalizeScriptPath(window.location.pathname);
                const scriptId = extractScriptIdFromNormalizedPath(normalizedPath);

                if (!scriptId) {
                    alert(getTranslation('scriptIdNotFound'));
                    return;
                }

                const scriptUrl = `https://update.greasyfork.org/scripts/${scriptId}.js`;

                btn.disabled = true;
                btn.textContent = getTranslation('downloading');

                GM_xmlhttpRequest({
                    method: 'GET',
                    url: scriptUrl,
                    onload: function (res) {
                        const code = res.responseText;
                        if (!code) {
                            alert(getTranslation('notFound'));
                            return;
                        }
                        const nameMatch = code.match(/\/\/\s*@name\s+(.+)/i);
                        const fileName = nameMatch ? `${nameMatch[1].trim()}.user.js` : 'script.user.js';
                        const blob = new Blob([code], { type: 'application/javascript;charset=utf-8' });
                        const url = URL.createObjectURL(blob);
                        const a = document.createElement('a');
                        a.href = url;
                        a.download = fileName;
                        document.body.appendChild(a);
                        a.click();
                        document.body.removeChild(a);
                        URL.revokeObjectURL(url);
                    },
                    onerror: function (res) {
                        alert(getTranslation('downloadError'));
                    },
                    ontimeout: function () {
                        alert(getTranslation('downloadTimeout'));
                    },
                    onloadend: function () {
                        btn.disabled = false;
                        btn.textContent = getTranslation('download');
                    }
                });
            });
            toolbar.appendChild(btn);
            const spacer = document.createElement('div');
            spacer.style.height = '12px';
            toolbar.appendChild(spacer);
        });
    }
    // #endregion

    // ================
    // #region INICIALIZAR
    // ================

    async function start() {
        iconCache = await GM_getValue(CACHE_KEY, {});
        await determineLanguage();
        languageModal = createLanguageModal();
        document.body.appendChild(languageModal);
        registerLanguageMenu();
        registerForceUpdateMenu();
        setupThemeChangeListener();
        if (isMarkdownPage()) {
            applyToAllTextareas();
            enableSourceEditorCheckbox();
        }
        if (isCodePage()){
            initializeDownloadButton();
        }
        processIconElements();
        highlightScriptDescription();
        if (isScriptPage()) {
            addAdditionalInfoSeparator();
        }
        makeDiscussionClickable();
        applySyntaxHighlighting();
        const observer = new MutationObserver(() => {
            processIconElements();
            highlightScriptDescription();
            if (isScriptPage()) {
                addAdditionalInfoSeparator();
            }
            if (isMarkdownPage()) {
                applyToAllTextareas();
            }
            makeDiscussionClickable();
            applySyntaxHighlighting();
        });
        observer.observe(document.body, {
            childList: true,
            subtree: true
        });
    }
    start();
    // #endregion
})();